부산본부 대시보드
Busan Headquarters Dashboard
다시 만나서 반갑습니다
통합 운영 모니터링에 접근하려면 비밀번호를 입력해주세요.
접근 비밀번호
부산본부 대시보드
Integrated Ops V22.39
데이터 현황
손익현황LIVE
일일매출LIVE
인원현황LIVE
운영 관리
매출 분석
비용분석LIVE
현금흐름
유류비 점검
사업장 정보
정보
개발 이력↗
접속시간
포털 · 메인 메뉴
SERVER HEALTHY
반갑습니다. 부산본부 통합운영 대시보드에 오신것을 환영합니다.
원하는 대시보드를 선택하세요. 실시간 데이터는 자동으로 갱신됩니다.
인증 완료 현재 버전 V22.39 테마 다크
본부 일정
1 / 1
2026-07-09
V22.39 — 판다(panda) 연동: 딥링크 자동 로그인 + 손익현황 직행 + 판다 스킨
  • FEAT딥링크 자동 로그인 — 신규 모듈 src/js/43-deeplink.js. URL hash #entry=panda&view=pl&pw=… 진입 시 doLogin 과 동일한 서버 검증(/api/info + x-dashboard-password) 후 자동 로그인 → enterPl() 손익현황 직행. 판다 업무협업 플랫폼에서 이미 인증된 사용자가 대시보드 비밀번호 재입력 없이 바로 손익현황을 보게 하는 연동. 인증 실패 시 기존 로그인 화면 유지 + 에러 안내 — 일반 진입 플로 영향 0.
  • FEAT비밀번호 비노출 설계 — pw 는 query 가 아닌 hash(fragment) 로 전달 (서버·reverse-proxy 로그에 남지 않음) + 파싱 직후 history.replaceState 로 주소창에서 즉시 제거. 판다 쪽은 링크에 pw 를 정적으로 박지 않고 PocketBase 훅 GET /api/dashlink (로그인 + 소장/본부장/팀장/관리자 역할 게이트) 가 NAS /pb_data/dash_pw.txt 에서 읽어 런타임 발급 — 판다 HTML 소스/GitHub 에 비밀번호 노출 0.
  • FEAT판다 스킨 — 신규 src/css/15-panda-skin.css. entry=panda 진입 시 <html data-skin="panda" data-theme="light"> 세팅 → 판다 팔레트(배경 #fafafa / 카드 #fff / 보더 #e8e8ea / primary teal #0f766e / 사업장 색 DWLG amber·DWLT blue·BNCC purple) 토큰 오버라이드 + Pretendard 웹폰트 주입. 셀렉터가 data-skin+data-theme 동시 매칭이라 일반 다크/라이트 진입 시각 회귀 0.
  • OPS변경 파일 3개 — src/js/43-deeplink.js (신규 +76줄) · src/css/15-panda-skin.css (신규 +31줄) · src/부산본부_대시보드.html (dev-row V22.39 + V22.38 latest 강등 + brand/welcome V22.39). server.js 변경 0 → --include-server-js 불필요. 롤백: python scripts/deploy.py --target=prod --rollback-to v22.38.
2026-05-28
V22.38 — §8 거래처 라이프사이클 + §9 일별 히트맵 데이터 스키마 mismatch fix
  • FIX§8 라이프사이클: 막대 색상 모두 회색(전부 undefined) + 도넛 'undefined 50개(100%)' 표시. 원인: V22.36 ETL 의 VENDOR_LIFECYCLE 에 status + txn_cnt 필드 누락. renderLifecycle 이 d.status 로 statusColor 매핑 시 모두 fallback 색 ('#6b7794') 사용 → 단색.
  • FIX§9 일별 히트맵 + 월말 집중도 차트: 셀 모두 비어있고 차트 빈 화면. 원인: V22.36 DAILY_TXN schema 가 {date, cnt, amt} 였는데 renderDailyHeatmap 은 {연월str, 일, 금액, 건수, 월말} 기대. daily.find(r=> r.연월str===mo && r.일===d) 가 매번 undefined → 모든 셀 0.
  • FEATETL VENDOR_LIFECYCLE 보강: status 자동 분류 — 코어(active 12M+) / 이탈(last가 4M+ 이전) / 신규(최근 3M 진입 + active ≤ 4M) / 활성(active 6M+ + 최근 1M 거래) / 간헐(그 외). TOP60 거래처 기준. 결과 ALL: 코어 48 · 이탈 6 · 활성 5 · 간헐 1. txn_cnt 도 같이 산출 (전표 건수, 툴팁 표시용).
  • FEATETL DAILY_TXN schema 변경: {연월str, 일, 금액, 건수, 월말} 로 재생성. 월말 = day ≥ 25 boolean. 실제 데이터로 ALL 월말(25-31일) 집중도 평균 약 89% — 결산 후처리 의심 패턴 (가장 큰 회계감사 신호).
  • OPS변경 파일 2개 — src/js/54-view-rowdata.js (DATA 임베드 재생성, VENDOR_LIFECYCLE +status/txn_cnt · DAILY_TXN schema 변경). src/부산본부_대시보드.html (dev-row V22.38 + V22.37 latest 강등 + brand/welcome V22.38). server.js / CSS 변경 0. 롤백: python scripts/deploy.py --target=prod --rollback-to v22.37.
2026-05-28
V22.37 — §6 유난히 큰 단건 거래 정렬을 금액(천원) 내림차순으로 변경
  • FIX사용자 요청: §6 우측의 '유난히 큰 단건 거래' 테이블이 V22.36 까지는 배율(이상 정도) 기준으로 정렬되어 있어서 사용자가 큰 금액 순으로 보기 어려움. 금액(천원) 내림차순으로 변경.
  • FEATETL 의 BIG_TXNS 생성 시 sort key 를 -x['배율'] → -x['금액(천원)'] 로 변경. 4 사업장 모두 동일 적용. 1행=주식회사 퍼스트인 용역수수료 491,761 천원 (2025-12-31, 10.5배) 등 큰 금액 상위 노출.
  • OPS변경 파일 2개 — src/js/54-view-rowdata.js (DATA 임베드 재생성, BIG_TXNS 정렬 키 변경). src/부산본부_대시보드.html (dev-row V22.37 + V22.36 latest 강등 + brand/welcome V22.37). server.js / CSS 변경 0. 롤백: python scripts/deploy.py --target=prod --rollback-to v22.36.
2026-05-28
V22.36 — 비용분석 ETL 재구축: 매출원가(5번대)만 사용 — 데이터 정합성 fix
  • FIX근본 원인: V22.31 ETL 임베드 DATA 의 원가합계 KPI 가 매출원가(5번대) 가 아닌 5(매출원가)+6(판관비)+8(법인세) 합산이었음. 사용자 보고 + RAW-비용분석.xlsx (60,781행) 계정코드 prefix 별 검증: 16/16 월 모두 5+6+8 = 임베드 값과 정확 일치. COST_DET 도 5번대 외 50+ 판관비 / 4 영업외 / 20 모호 항목이 섞여 §3 도넛·§4 스택·§4 MoM·§6 위험 항목 분석에 노이즈 다수 포함.
  • FEATETL 재구축: RAW-비용분석.xlsx 컨테이너에서 fetch → pandas 로 계정코드 prefix 분리 → 매출 = 4번대(대변-차변), 매출원가 = 5번대(차변-대변) 만으로 모든 데이터셋 재생성. 6/7/8번대(판관비/영업외/법인세) 전면 제외.
  • FEAT재생성 데이터셋 4개 사업장 × 10셋: MONTHLY (매출/원가/원가율/MoM) · COST_DET (5번대 항목×월) · VENDOR (TOP15 거래처) · VENDOR_M (거래처×월) · VENDOR_LIFECYCLE (활동 개월/총합) · DAILY_TXN (일별 건수/금액) · ALERTS (MoM ±15% 이상) · BIG_TXNS (항목평균 5배+ 1천만원+) · SITE_M · DEPT_ACCT.
  • FEAT사업장 매핑 확정: 부산신항CFS 본사 → BNCC, 디더블유엘글로벌 → DWLG, DWL_부산터미널 → DWLT. ALL = 3 사업장 합산 검증 16/16 월 0원 차이.
  • FEAT화면 변화: KPI 원가합계(당월) 2,089→1,735 백만 (354 백만 감소, 16.9% 차이). 원가율 89.5%→74.4% (실제 매출총이익률). §3 도넛에서 이자비용/인건비 급여/복리후생비/세금과공과 등 50+ 항목 제거 → 매출원가 22 항목만. §4·§6 도 동일.
  • OPS변경 파일 2개 — src/js/54-view-rowdata.js (const DATA = {...} 임베드 통째 교체, 407 KB → 175 KB, 손익 외 비용 항목 제거로 크기 감소). src/부산본부_대시보드.html (dev-row V22.36 + V22.35 latest 강등 + brand/welcome V22.36). server.js / CSS 변경 0 → --include-server-js 불필요. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.35.
2026-05-28
V22.35 — 비용분석 가로 막대(§4 MoM / §5 거래처) 호버 위치 mismatch hotfix
  • FIX증상: 사용자 보고 + 스크린샷. 가로 막대 차트(§4 전월 vs 당월 Top12 · §5 거래처 의존도 TOP15) 에 마우스를 호버하면 실제 가리킨 행과 다른 행의 툴팁이 표시됨.
  • FIX원인: Chart.js interaction:{ mode:'index', intersect:false } 만 지정하고 axis 누락. 기본 axis 가 'x' 라 가로 막대(indexAxis:'y') 에서 X 축 기준으로 가장 가까운 인덱스를 찾아 매칭 → 마우스 Y 위치와 무관하게 X 좌표만 보고 잘못된 행 선택.
  • FEAT두 차트 모두 interaction:{ mode:'index', intersect:false, axis:'y' } 로 갱신 — Y 축 기준 인덱싱으로 마우스 Y 좌표 ↔ 행 매칭 정합.
  • OPS변경 파일 2개 — src/js/54-view-rowdata.js (interaction 두 곳 +axis:'y', +2줄). src/부산본부_대시보드.html (dev-row V22.35 + V22.34 latest 강등 + brand/welcome V22.35). server.js / CSS 변경 0 → --include-server-js 불필요. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.34.
2026-05-28
V22.34 — 비용분석 모달 폴리싱 (차트 viewport 전체로 확장 + 5장 KPI 비어있음 hotfix)
  • FIX차트가 viewport 전체로 확장 — 사용자 보고 + 스크린샷. 원인: .cv21-modal-chart-wrap { position:relative; height:300px } 누락. V22.31 cv21- 접두어 리네임 시 #view-rowdata .modal-chart-wrap 만 남고 신규 규칙 누락 → canvas { position:absolute; inset:0 } 가 가장 가까운 positioned ancestor 인 .cv21-modal-overlay { position:fixed; inset:0 } 기준으로 절대 위치 → 전체 viewport 채움.
  • FIX5장 KPI 카드 비어있음 — 원인 ① .cv21-modal-kpi grid 규칙 누락 (이전 .modal-kpi 룰만 남음). ② id="modal-sub" / id="modal-kpi" 가 손익현황(PL) 뷰 모달 (line ~2317·2322) 와 중복. V22.32 까지는 cv21 modal 이 문서 앞에 있어 getElementById 가 cv21 반환. V22.33 에서 modal 을 view-rowdata 끝으로 이동시키면서 PL 모달이 앞 순서가 되어 PL 의 invisible 모달에 innerHTML 이 쓰여지고 cv21 modal-kpi 는 영원히 비어있음.
  • FEAT중복 id 해소 — cv21 모달 안 id="modal-sub" → id="cv21-modal-sub", id="modal-kpi" → id="cv21-modal-kpi". JS openAccountModal / openVendorModal 두 함수의 getElementById 4개 호출도 동일 갱신 (2회씩).
  • FEATCSS 누락 규칙 추가 — .cv21-modal-chart-wrap { position:relative; height:300px; width:100%; margin-bottom:12px } + .cv21-modal-kpi { display:grid; grid-template-columns:repeat(5,1fr); gap:8px; margin:14px 0 16px }.
  • OPS변경 파일 3개 — src/부산본부_대시보드.html (id 2개 cv21- 접두어 + dev-row V22.34 + V22.33 latest 강등 + brand/welcome V22.34). src/css/54-view-rowdata.css (chart-wrap + cv21-modal-kpi 2개 룰 +4줄). src/js/54-view-rowdata.js (getElementById 4회 갱신, +0/-0 net 0). server.js 변경 0 → --include-server-js 불필요. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.33.
2026-05-28
V22.33 — 비용분석 계정과목/거래처 14M 트렌드 모달 미작동 hotfix
  • FIX근본 원인: <div class="cv21-modal-overlay" id="account-modal"> 모달 블록이 잘못된 위치 — 사이드바 개발 이력 데이터인 <div id="dev-history-source" hidden> 안의 한 dev-row dev-category 텍스트 중간에 끼어 있었음. [hidden]{display:none} 상속으로 .open 클래스가 추가되어도 영구 숨김 → 사용자 클릭 시 무반응.
  • FIXcv21-modal-overlay 기본 CSS 규칙 누락 동시 발견: V22.31 에서 .modal-overlay → .cv21-modal-overlay 접두어 리네임 시 .open 규칙만 옮겨오고 base 규칙(position:fixed; inset:0; z-index:1000; display:none; background:rgba(...); backdrop-filter:blur(6px)) 누락. 모달이 옮겨졌어도 inline flow 로 렌더되어 보이지 않을 상태.
  • FEAT모달 블록을 </div><!-- /view-rowdata --> 직전으로 이동 — 비용분석 뷰의 형제 요소로 위치. position:fixed 라 위치 무관하게 viewport 기준 overlay.
  • FEATCSS 에 .cv21-modal-overlay base 규칙 + cv21FadeIn/cv21PopIn keyframes 추가. 다크/라이트 양쪽 토큰 사용.
  • FEAT잘못된 위치에 있던 모달 블록의 흔적 (V22.31 dev-row 의 dev-category 안에 끼어있던 모달 HTML) 제거 → 원래 의도였던 [CRITICAL HOTFIX] 로그인 깨짐 — </script></body></html> 카테고리 라벨 복구.
  • OPS변경 파일 3개 — src/부산본부_대시보드.html (모달 17줄 이동 + dev-category 텍스트 복구 + dev-row V22.33 + V22.32 latest 강등 + brand V22.33 + welcome V22.33). src/css/54-view-rowdata.css (cv21-modal-overlay base 규칙 + 2 keyframes +13줄). build/부산본부_대시보드.html (동일 패치 + 자동 빌드 산출물). server.js / JS 모듈 변경 0 → --include-server-js 불필요. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.32.
2026-05-28
V22.32 — 비용분석 상단 헤더 우측 컨트롤 복원 (← 메인메뉴 버튼 등)
  • FIXV22.31 v21 디자인 전체 교체 시 빠진 우측 컨트롤 5종 복원 — server-badge / 새로고침 / 업데이트 시각 / 🌙 테마 / ← 메인메뉴. 사용자 보고 "메인메뉴로 가는 버튼이 없다".
  • FIX구조 버그 동시 수정 — <div class="portal-hdr"> 의 짝 닫는 태그 자체가 빠져 있어 filterbar 가 의도치 않게 portal-hdr 안에 포함되어 있던 상태. hdr-right 그룹 추가하면서 </div> 정합.
  • FEAT54-view-rowdata.js V22.31 은 우측 컨트롤을 관리하지 않으므로, view-rowdata 직후 인라인 <script> 신설 — enterRowdata / refreshRowdataData / _cv21_render_view 를 1회만 감싸서 호출 후 배지 'loading→connected' 전이 + 업데이트 시각 갱신 + 새로고침 버튼 노출.
  • UI레이아웃 일관성 — 인원·일일매출 뷰의 portal-hdr 패턴(server-badge → refresh → time → theme → 메인메뉴) 그대로 차용. hdr-stripe 색상은 비용분석 고유 var(--accent-orange) 유지.
  • OPS변경 파일 2개 — src/부산본부_대시보드.html (view-rowdata 헤더 +13줄 / 인라인 script +60줄, net +73줄) + 사이드바 brand V22.24.5 → V22.32 + welcome card V22.24.5 → V22.32 + dev-row V22.32 신규 + V22.31 latest 강등. src/js/54-view-rowdata.js 변경 0 / server.js / CSS 변경 0 → --include-server-js 불필요. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.31.
2026-05-28
V22.31 — 비용분석(RAW) v21 디자인 전체 교체
  • FEATview-rowdata 콘텐츠를 mockup_비용분석_v21.html 디자인으로 통째 교체 (기존 V18/V19 SSE·사유추적·분류편집 모두 제거)
  • FEATRAW-비용분석.xlsx 16M (2025-01~2026-04, 60,781행 분개) 정적 ETL 임베드 — MONTHLY/COST_DET/VENDOR×4사업장 = 10개 데이터셋
  • FEAT7개 신규 섹션 — §1 KPI 5장 + §2 매출원가 듀얼 + §3 도넛+트리맵 + §4 스택+MoM + §5 거래처 Pareto + §6 위험도점수 + §7 회계품질 + §8 거래처 라이프사이클 + §9 일별 전표 히트맵
  • FEAT사업장 토글(통합/DWLG/DWLT/BNCC) 실작동 — RAW swap 패턴, 모든 차트/KPI 즉시 재계산
  • FEAT위험도 점수 4지표 (전월 대비·평균 대비·범위 이탈·통계 이상치) — Critical/Warning/Info 자동 분류, 비용 증가 방향만 노출 (절감 제외)
  • FEAT계정과목/거래처 클릭 → 16M 트렌드 모달 (당월/MoM/YoY/평균대비/최대최소 KPI 5장)
  • OPS기존 54-view-rowdata.js/css 백업 *.v22_pre_v21_*.bak 보관 (롤백 가능). 메인 HTML도 백업
  • REMOVEV19 기능 비활성화 — rd_openClsModal, refreshRowdataData 등은 noop 으로 호환 유지
2026-05-28
V22.30 — 장비 카드 헤더 일관성
  • FIX보유장비 / 전용 부속 장비 카드 헤더에 비-숫자 텍스트(예: '30톤, 16톤, 4.5톤', '468*32*16, 33*14*820') 가 큰 글씨로 노출되던 문제 해결 — 순수 숫자만 헤더에 표시, 그 외 '—' + 단위 숨김
  • FIX본문 meta '수량' 항목도 동일 클린업 — 헤더와 같은 비-숫자 값 중복 노출 방지
  • OPS코드만 수정 (엑셀 데이터 변경 없음). v16_v Phase 2(startsWith) 매칭으로 '코일발 적용장비' 같은 prefix 키 값이 헤더에 누설되던 패턴 차단
2026-05-28
V22.29.1 — DWLT 출입부스 엑셀 정합 보정
  • FIX엑셀(대시보드 사업장 기초정보_V1.xlsx) DWLT ④ 출입부스 실제 6행과 정확 정합: 운영 인원(추가) · 청소(추가) · 수량/상주/운영시간/통제방식/장비/비고(제거)
  • FIX면적 단위 ㎡ → 평 (엑셀 원본 단위 일치)
  • UIsub 부제 '정문 · 후문' → '정문 부스' (엑셀 위치=1개소 정문만 운영)
2026-05-28
V22.29 — 시설 / DWLT 출입부스 추가
  • FEAT편의시설 탭에 출입부스(gate) amenity 신설 — DWLT 전용 (FAC_AMEN_HIDDEN_BY_SITE 로 DWLG/BNCC 숨김)
  • FEATamenOrder 폴백 배열 맨 앞에 gate 추가 → DWLT 편의시설 첫 진입 시 출입부스 탭 활성
  • FEATstats 4종(위치/수량/면적/상주) + op 3종(운영시간/통제방식/장비) + ct 3종(담당/전화/비고) 스키마. 엑셀 dict 의 '출입부스 ...' 키 자동 매칭, 미등록 시 '-' 표시
  • FEAT사진 NAS 자동 매칭 — /facility-img/DWLT/편의시설/출입부스/* 1순위, 키워드(출입부스/부스/게이트/booth/entrance) 2순위
  • UIplaceholder 아이콘 GTE, 라벨 '출입부스' — 사진 미등록 시 '사진 검색 중' → '사진 미등록' 톤 유지
2026-05-27
V22.25 — 손익(PL) 뷰 UX 개선
  • UI연도+기간 컨트롤을 한 줄로 통합 (.pl-controls 래퍼 + position:sticky 상단 고정)
  • UI숫자 셀 Pretendard Variable + tabular numbers + 전체 폰트 +4px (PL표·요약카드·모달·YoY)
2026-05-19
V22.24.5 유류비 점검 — 월/년 필터 변경 시 자동 재fetch (과거 월 클릭 시 매입금액 0원 표시 hotfix)
  • FIX사용자 보고 버그 — 4월(또는 다른 과거 월) 클릭 시 사업장 비용 분포가 0원/0L/0건으로 표시되고 도넛도 빈 화면. 워닝 배너는 여전히 "4월" 이상 신호 표시 → 사용자 선택과 banner 불일치.
  • FIX근본 원인 — /api/fuel-info 기본 호출 시 응답의 data 배열은 당월(=서버 측 latest, 보통 4월) detail records 만 포함. f3_setMonth(m) 가 로컬 캐시 data 를 단순 client-side 필터링 → 다른 월 레코드 부재 → filtered=[] → siteAgg 비어있음 → 0원/L 표시.
  • FEATreloadFuelDataForFilter 헬퍼 신설 — async 함수, /api/fuel-info?year=YYYY&month=MM 재fetch. 서버 엔드포인트는 이미 query 지원 (V21.0.13 전년 fetch 에서 사용 중). 응답으로 FUEL.raw 전체 교체하되 trendsPrev / trendsBySitePrev / rd 보조 데이터는 초기 로드 본 보존 (재fetch 비용 절감). 로딩 중 fuel-hdr-sub 인디케이터 갱신.
  • FEATf3_setYear / f3_setMonth async 화 — FUEL.selYear = y; await reloadFuelDataForFilter(); applyFuelFilter();. 사업장 필터(f3_setSite)는 동기 유지 — 사업장 필터는 client-side 만으로 충분.
  • FEAT효과 — 과거 월 클릭 시 ① 12개월 추이 차트 window 가 해당 월 기준으로 시프트 ② 사업장 비용 분포 카드가 해당 월 실데이터로 갱신 ③ 단가 스프레드 / 도넛 / 워닝 배너 모두 해당 월 기준 정합 ④ 사용자 선택과 화면 표시 일치.
  • OPS변경 파일 2개 — src/js/55-view-fuel.js (f3_setYear/f3_setMonth async 전환 +2줄/-2줄 + reloadFuelDataForFilter 헬퍼 신설 +28줄). src/부산본부_대시보드.html (dev-row V22.24.5 + V22.24.4 latest 강등 + 사이드바 brand V22.24.5 + welcome card V22.24.5). server.js·CSS 변경 0 → --include-server-js 불필요. prod 직배포 정책 14차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.24.4.
2026-05-19
V22.24.4 유류비 점검 — 주유주기 단축 워닝 룰 정밀화 (M/W/F 공급 패턴 false positive 차단)
  • FEAT사용자 보고 (운영 사실 공유) — 사업장에 월/수/금에 주유소가 들어와 유류 공급. 즉 같은 장비의 정상 최소 cycle = 2일. 기존 워닝 룰 (gap < 평균×50% 1건이라도 워닝) 이 정상 2일 gap도 단축으로 오인 → false positive 다발.
  • FIX근본 원인 3가지 — ① 절대 floor 부재 (2일이 M/W/F 정상 cycle 임을 모름) ② 단일 short gap 1건만 있어도 워닝 (통계 신호 부족) ③ mean이 outlier(7-30일 긴 gap)에 휘둘려 부풀려짐. 예: gaps=[2,5,7,7] → avg=5.25 → 50%=2.63 → 정상 2일이 단축 판정.
  • FEATFUEL_WARN_THRESHOLDS 정밀화 (AND 3중) — cyclePtsLimit 0.5 → 0.3 (평균 대비 30% 보수화) · cycleAbsMinDays 신설 = 2 (절대 floor, 2일 이상 gap은 M/W/F 정상으로 보호) · cycleMinShortGaps 신설 = 2 (단발 1건 무시, 2건 이상 패턴화된 단축만 워닝).
  • FEATdetectFuelWarnings CYCLE 룰 재작성 — g > 0 AND g < cycleAbsMinDays AND g < cyc.avg × cyclePtsLimit 3중 AND. 임계 만족 short gap이 ≥ cycleMinShortGaps 건 일 때만 발생. 메시지에 "M/W/F floor 2일 적용" 명시.
  • FEAT효과 — M/W/F 패턴의 정상 2일 gap false positive 0건. 진짜 이상 신호 (다음 날 즉시 재주유가 2번 이상 반복 = 탱크 누유 / 입력 오류 등) 만 워닝. DUP_DAY(같은 날 다중 주유) 룰은 별도 유지.
  • OPS변경 파일 2개 — src/js/55-view-fuel.js (FUEL_WARN_THRESHOLDS +3 필드 / 코멘트 +3줄 / CYCLE 룰 filter +3 AND 조건 / if 조건 ≥ 2건 / 메시지 floor 라벨, net +10줄). src/부산본부_대시보드.html (dev-row V22.24.4 + V22.24.3 latest 강등 + 사이드바 brand V22.24.4 + welcome card V22.24.4). server.js·CSS 변경 0 → --include-server-js 불필요. prod 직배포 정책 13차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.24.3.
2026-05-19
V22.24.3 유류비 점검 — 드릴다운+스프레드 추이 삭제, 12M 추이를 단가 스프레드 밑으로, 워닝 가로 크게
  • FEAT사용자 요청 (V22.24.2 시각 검증 직후, 4건 통합) — ① 12개월 추이를 단가 스프레드 바로 아래로 이동 ② 사업장×장비 드릴다운 삭제 ③ 단가 스프레드 추이 삭제 ④ 워닝 분류를 가로로 크게 노출.
  • FEAT신규 4-Section 순서 — ① 사업장 비용 분포 (가로 4 카드) ② 단가 스프레드 + 사업장 비용 점유율 (2-col) ③ 12개월 추이 (full-width 콤보, 단가 스프레드 바로 아래) ④ 워닝 분류 (full-width 가로 3-col Critical/Warning/Info, 크게). 페이지 최종 단순화 — KPI 4 + TOP이상치 + 드릴다운 + 스프레드 추이 모두 폐기.
  • FIXJS 삭제 — renderDrilldown + renderSpreadTrend (−119줄): 두 함수 본체 + renderAll 호출 2개 + FUEL.drillMode 상태 + f3_setDrillMode 토글 핸들러 + window 노출 1줄 모두 일괄 제거. renderAll = renderWarnBanner + renderSiteRow + renderSpreadChart + renderDonut + renderCombo24 + renderWarnRow (6개로 축소). server.js·데이터 레이어 변경 0.
  • FIXHTML 삭제 — Section 1·5 정리: f3-drill-table / f3-drill-toggle 컨테이너 + f3-row-2col-eq wrapper + f3-spread-trend-chart canvas 모두 제거. 워닝 분류는 panel wrapping 폐기 → f3-warn-row.f3-warn-row-large 단독 (full-width).
  • FIXCSS 정리 — V22.24.3: .f3-row-2col-eq 셀렉터 3개 + .f3-spread-trend-body 폐기 (−5줄). .f3-warn-row-large 신설 (+7줄, 카드 padding 20px 24px / min-height 180px / 숫자 38px / 라벨 13px / list 13px). 900px 반응형 row-2col-eq 잔존 라인 정리.
  • OPS변경 파일 3개 — src/js/55-view-fuel.js (renderDrilldown/renderSpreadTrend 본체 −119줄 + renderAll 재구성 + drillMode 상태/토글/노출 정리, 1129→1010줄). src/css/55-view-fuel.css (−5/+7 net +2). src/부산본부_대시보드.html (드릴다운 panel −13줄 + Section 4 wrapper 단순화 + dev-row V22.24.3 + V22.24.2 latest 강등 + 사이드바 brand V22.24.3 + welcome card V22.24.3). server.js 변경 0 → --include-server-js 불필요. prod 직배포 정책 12차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.24.2.
2026-05-19
V22.24.2 유류비 점검 — 단가 스프레드 + 사업장 비용 점유율 행을 2번째 위치로 격상
  • FEAT사용자 요청 (V22.24.1 시각 검증 직후) — 단가 스프레드 + 사업장 비용 점유율 (2-col row) 을 사업장 비용 분포 바로 아래(2번째) 로 이동. 사업장×장비 드릴다운은 3번째로 강등.
  • FEAT신규 5-Section 순서 — ① 사업장 비용 분포 (가로 4 카드) ② 단가 스프레드 + 사업장 비용 점유율 (2-col) ③ 사업장×장비 드릴다운 (전체 장비 테이블) ④ 12개월 추이 (full-width 콤보) ⑤ 워닝 분류 + 단가 스프레드 추이 (2-col equal). 의도: 종합 지표 → 추세/점유율 → 장비 상세 → 시계열 → 워닝 흐름의 인지 단계화.
  • OPS변경 파일 1개 — src/부산본부_대시보드.html (Section 1 ↔ Section 2 HTML 블록 교환 + 코멘트 갱신 + dev-row V22.24.2 + V22.24.1 latest 강등 + 사이드바 brand V22.24.2 + welcome card V22.24.2). JS·CSS·server.js 변경 0 → renderAll / renderSpreadChart / renderDonut / renderDrilldown 모두 무수정. --include-server-js 불필요. prod 직배포 정책 11차 사이클 (V22.24 위 두번째 polish hotfix). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.24.1.
2026-05-19
V22.24.1 유류비 점검 — 사업장 비용 분포 최상단 가로 이동 (Section 4 → Section 0)
  • FEAT사용자 요청 — 사업장 비용 분포(본부 합산 + 사업장별 4 카드)를 가로로 길게 펴서 사업장×장비 드릴다운 위로 이동. 진입 즉시 KPI 대체 정보(평균단가/총비용/주유량/이상건수)를 한눈에 파악 가능.
  • FEATf3-site-row 위치 변경 — Section 4 (3-col 마지막 panel 안에서 세로 stack) → Section 0 (최상단 단독, 기존 default grid-template-columns:repeat(4,1fr) 가로 4-col 복원). renderSiteRow 함수 무수정, ID 보존.
  • FEATSection 4 축소 — 3-col (워닝 + 스프레드 추이 + 사업장 분포) → 2-col equal (워닝 + 스프레드 추이). CSS .f3-row-3col → .f3-row-2col-eq (grid-template-columns:1fr 1fr). 900px 이하 1-col 반응형 유지.
  • OPS변경 파일 2개 — src/부산본부_대시보드.html (Section 0 신설 site-row +3줄, Section 4 컨테이너 교체 +1줄/-1줄, site-row 1개 panel 제거 −4줄). src/css/55-view-fuel.css (f3-row-3col 셀렉터 4개 → f3-row-2col-eq 3개로 교체, 반응형 1280/900 정리). JS 변경 0 → renderAll 무수정. server.js 변경 0 → --include-server-js 불필요. prod 직배포 정책 10차 사이클 (V22.24 위 polish hotfix). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.24.
2026-05-19
V22.24 유류비 점검 — 레이아웃 재구성 (KPI 4 + TOP이상치 폐기, 사업장×장비 드릴다운 최상단)
  • FEAT사용자 결정 (2026-05-19) — 유류비 점검 모듈 4-Section 재구성. ① 최상단 사업장×장비 드릴다운 (신설, 전체 장비 한눈에) ② 단가 스프레드 + 사업장 비용 점유율 (2-col) ③ 12개월 추이 (full-width) ④ 워닝 분류 + 단가 스프레드 추이 + 사업장 비용 분포 (3-col).
  • FEATrenderDrilldown 신설 — 필터된 레코드를 site×equipment×id 단위 집계 → 정렬 토글 4종(총비용 ↓ 기본 / 주유량 ↓ / 평균단가 ↓ / 최대 활용률 ↓). 이상치(탱크초과 또는 anomaly 플래그) row 카운트 → 빨강 배경 + 건수 배지. 기존 TOP 이상치 테이블이 10건만 표시하던 한계 → 전체 장비 한 화면에 노출.
  • FEATrenderSpreadTrend 신설 — 사업장간 단가 max−min 12M 막대 + 본부 평균단가 라인 (콤보). 0에 가까울수록 사업장간 단가 균일, 클수록 입고 시점/협상 효율 차이가 큰 월. Section 2 단가 스프레드(절대값)와 의미 분리 — Section 4 는 분산도 추이.
  • FIXKPI 4 + TOP이상치 폐기 — KPI ① 매출비 / ② 예산편차 / ③ 단가 spread / ④ YoY YTD 카드 4장 전체 + 사업장×장비 TOP이상치(10건 한정) 테이블 모두 삭제. renderHeroKpi · kpiCard · drawMini · renderTopTable 함수 4개 + 관련 HTML 컨테이너(f3-hero-kpi, f3-top-table, f3-top-toggle) 모두 정리. FUEL.topMode → FUEL.drillMode, f3_setTopMode → f3_setDrillMode 전환.
  • OPS변경 파일 3개 — src/js/55-view-fuel.js (renderHeroKpi/kpiCard/drawMini/renderTopTable 삭제 −209줄 + renderDrilldown/renderSpreadTrend 신설 +115줄, renderAll 재구성). src/css/55-view-fuel.css (f3-row-3col grid + f3-spread-trend-body + 1280px/900px 반응형 +12줄). src/부산본부_대시보드.html (view-fuel 본문 lines 2971-3030 4-Section 재배치 + dev-row V22.24 + V22.23 latest 강등 + 사이드바 brand V22.24 + welcome card V22.24). server.js 변경 0 → --include-server-js 불필요. prod 직배포 정책 9차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.23.
2026-05-19
V22.23 사업장 정보 — DWLT 편의시설에서 회의실 교육장·흡연구역만 노출 (V22.22 매핑 확장)
  • FEAT사용자 요청 — DWLT 사업장은 회의실 교육장·흡연구역 2종만 운영, 나머지 4종(구내식당/휴게실 라운지/샤워실 탈의실/체력단련장) 시설 부재. 기존엔 6 amenity 탭이 모두 렌더되어 "사진 미등록" placeholder 4개 노출.
  • FEATFAC_AMEN_HIDDEN_BY_SITE 매핑 확장 — V22.22 에 추가된 매핑에 DWLT: ['caf', 'lng', 'shw', 'gym'] 1줄 추가. v16_renderAmen 의 filter 로직과 f_renderSection 의 첫 visible amenity fallback 모두 V22.22 그대로 재사용. DWLT 진입 시 mtg(회의실 교육장) 첫 탭 자동 선택, AMENITIES 카운트 2 동적 표시.
  • FEATDWLT 시트 추가 확인 — V19.2 (2026-04-27) 시점 메모상 DWLT/BNCC 는 시트 부재로 v16_renderEmptySite 안내 배너만 노출했으나, 영업팀이 신 엑셀(대시보드 사업장 기초정보_V1.xlsx)에 DWLT 시트 70행·BNCC 시트 135행 추가 완료. v16_siteHasData(DWLT/BNCC) 모두 true 반환 → v16_renderAmen 정상 호출 → V22.22+V22.23 매핑 정상 적용.
  • OPS변경 파일 2개 — src/js/60-view-facility.js (FAC_AMEN_HIDDEN_BY_SITE DWLT 1줄 추가). HTML dev-row V22.23 + V22.22 latest 강등 + 사이드바 brand V22.23 + welcome card V22.23. server.js / CSS 변경 0 → --include-server-js 불필요. prod 직배포 정책 8차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.22.
2026-05-19
V22.22 사업장 정보 — BNCC 편의시설에서 휴게실 라운지·체력단련장 탭 비노출 (site별 hidden amenity 매핑)
  • FEAT사용자 요청 — BNCC 사업장은 휴게실 라운지·체력단련장 시설 자체가 없음 (NAS Assets 폴더 부재 확인). 기존엔 6 amenity 탭이 모두 렌더되어 "사진 미등록" placeholder 4개가 노출되어 영업본부장·임원에 미운영 시설 인상.
  • FEATFAC_AMEN_HIDDEN_BY_SITE 매핑 신설 — { BNCC: ['lng', 'gym'] }. v16_renderAmen 진입 시 amens.filter 로 비노출 amenity 탭/패널/사진 슬롯 모두 미렌더. AMENITIES 카운트 6 하드코드 → amens.length 동적. 향후 다른 사업장 동일 패턴은 본 매핑만 늘리면 됨.
  • FEATsite 전환 시 첫 visible amenity fallback — f_renderSection amen 분기에서 직전 FAC_AMEN_CURRENT_TAB 이 새 site 의 hidden 목록에 있으면 amenOrder = ['caf','lng','shw','gym','mtg','smk'] 순서로 첫 visible 자동 선택. 예: DWLG 에서 gym 보다가 BNCC 전환 시 caf 으로 자동 전환.
  • OPS변경 파일 2개 — src/js/60-view-facility.js (FAC_AMEN_HIDDEN_BY_SITE +6줄 + v16_renderAmen filter +3줄 + AMENITIES 카운트 동적 +1줄/-1줄 + f_renderSection fallback +8줄/-1줄). HTML dev-row V22.22 + V22.21 latest 강등 + 사이드바 brand V22.22 + welcome card V22.22. server.js / CSS 변경 0 → --include-server-js 불필요. prod 직배포 정책 7차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.21.
2026-05-19
V22.21 사업장 정보 — file:// 진입 시 사진 미노출 hotfix (image src 절대 URL 부착)
  • FIX사용자 보고 (다른 컴퓨터 + 맥북 모두) — 사업장 기본정보 모듈에서 모든 사진(현장사진 그리드 8장 + 소재지 지도 + 편의시설 갤러리 + 보안 섹션 이미지) 영구 미노출. F12 콘솔: Unsafe attempt to load URL file:///C:/Users/.../부산본부_대시보드.html ... 'file:' URLs are treated as unique security origins + Not allowed to load local resource: file:///facility-img/DWLG/00_DWLG%20지도.jpg 다발.
  • FIX근본 원인 — V21.0.8 (2026-05-15) SERVER_HOST 동적 결정 + apiFetch / connectSSE 절대 URL prefix 적용은 정상이나 v16_renderPhotos / v16_updateMapImage / v16_updateAmenityImage / v16_updateSectionImage 4 위치의 <img src="/facility-img/..."> 는 상대 경로 그대로 박혀 있어 file:// 진입(임원 정적 HTML 사본을 SyncDrive 더블클릭으로 여는 사용자 다수) 시 file:///facility-img/... 로 해석 → 로컬 파일시스템 검색 → 차단. http:// 직접 접근 사용자는 same-origin 으로 정상 동작이라 미발견.
  • FEATv16_absUrl 헬퍼 신설 — path.indexOf('http')===0 || 'data:' 이미 절대 URL 인 경우 그대로, 그 외 App.common.SERVER_PROTO + '//' + App.common.SERVER_HOST + path prefix. SERVER_HOST 동적 결정 결과(V21.0.8) 재사용 → http://3001 직접 접근 / https://dashboard.busan-hq-dashboard.synology.me / file:// 임원 정적 사본 3 진입 모두 정상.
  • FEAT4 위치 일괄 적용 — ① v16_updateAmenityImage 편의시설 갤러리 thumb src ② v16_updateMapImage 소재지 지도 img src ③ v16_renderPhotos 현장사진 그리드 photoList src (라이트박스 종이동에 자동 전파) ④ v16_updateSectionImage 보안 섹션 이미지 src + safeSrc onclick. 라이트박스 v16_openImage 는 photoList 단계에서 절대 URL 받으므로 무수정.
  • OPS변경 파일 2개 — src/js/60-view-facility.js (v16_absUrl 헬퍼 +12줄 + 4 위치 srcRel/src 분리 +8줄/-4줄). HTML dev-row V22.21 + V22.20 latest 강등 + 사이드바 brand V22.21 + welcome card V22.21. server.js / CSS 변경 0 → --include-server-js 불필요. prod 직배포 정책 6차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.20.
2026-05-19
V22.20 매출 분석 — enterSales 재진입 시 무거운 setup 스킵 (_inited 가드 확장)
  • PERF매출분석 탭 재진입 즉각 반응 — V22.15 정적 분석 헤비함 원인 2번 항목 해소. 기존: enterSales 가 buildMarkup 만 _inited 가드로 1회였고 나머지 4개 무거운 setup 함수(drawAllCharts / buildClientSelect / bindHandlers / startThemeObserver)는 매 재진입마다 호출. 차트 destroy+create + 거래처 dropdown 재구성 + 이벤트 재바인딩 + MutationObserver 가드 체크 부담.
  • FEAT_inited 가드 4개 함수까지 확장 — drawAllCharts·buildClientSelect·bindHandlers·startThemeObserver 모두 가드 안으로 이동. 재진입 시 loadSalesData (V22.15 디퍼링 + V22.19 메모이제이션) + connectSalesSSE (idempotent close+reconnect, 끊긴 SSE 자동 복구) 만 호출.
  • FEATChart.defaults 매번 유지 — 단순 property 할당이라 부담 없고 다크/라이트 토글 시 즉시 반영 필요. chartTxt() / chartGrid() 가 --text2 / --border CSS 변수 read.
  • FEATV22.16+19+20 누적 효과 — V22.16 (차트 5종 중 3종 update 패턴) + V22.19 (거래처 100-150개 재분류 메모이제이션) + V22.20 (enterSales 무거운 setup 스킵). 다른 뷰에서 매출분석 탭 복귀 시 거의 즉시 표시 + 사업장 탭 토글 즉각 반응.
  • OPS변경 파일 1개 — src/js/57-view-sales.js (enterSales 4개 함수 가드 이동, +12줄/-4줄). server.js / CSS 변경 0. HTML dev-row V22.20 + V22.19 latest 강등 + 사이드바 brand V22.20 + welcome card V22.20. prod 직배포 정책 5차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.19.
2026-05-19
V22.19 매출 분석 — _rdClients 메모이제이션 (사업장 탭 토글 즉각 반응)
  • PERF거래처 100-150개 재분류 스킵 — V22.15 정적 분석에서 발견된 헤비함 원인 중 3번 항목. extractClientsFromRaw 가 enterSales / SSE reload / 사업장 탭 클릭 / 월 dropdown 변경 시마다 RAW.db 의 24개월 시계열 + z-score / least-squares slope / no-trade months / dormant-recover 등 5종 메트릭을 100-150개 거래처 전체에 대해 재계산하던 비용. 특히 사업장 탭 토글 (refreshAll) 은 _rdRaw 와 _ym 모두 동일한데도 매번 재계산 → 메인 thread 점유.
  • FEATmemoExtractClientsFromRaw wrapper 도입 — _rdClientsCacheRef (rd 참조) + _rdClientsCacheKey (year.month) 모듈 변수 추가. cache hit 시 직전 _rdClients 그대로 반환 → 거래처 재분류 0회. SSE reload 는 _rdRaw=null·_rdClients=null 로 비우므로 자연 cache miss → 새로운 RAW 데이터로 재계산.
  • FEAT예상 효과 (정성) — 사업장 탭 (ALL/DWLG/DWLT/BNCC) 클릭 시 100-150개 거래처 재분류 스킵 → 즉각 반응. V22.15 디퍼링 패턴은 그대로 유지 (Layer 1 즉시 + Layer 2/3/4 setTimeout 0). 월 dropdown 변경은 _ym.month 변경되므로 cache miss → 기존 동작 동일.
  • OPS변경 파일 1개 — src/js/57-view-sales.js (_rdClientsCacheRef·_rdClientsCacheKey 모듈 변수 + memoExtractClientsFromRaw wrapper + 호출 2곳 치환, +20줄/-2줄). server.js / CSS 변경 0. HTML dev-row V22.19 + V22.18 latest 강등 + 사이드바 brand V22.19 + welcome card V22.19. prod 직배포 정책 4차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.18.
2026-05-19
V22.18 일일매출 — DWLT UNKNOWN 분류 hotfix (X-File-Name 헤더 strip 안전망)
  • FIXV22.17 배포 후에도 DWLT 매출 미수신 — 사용자 스크린샷 매칭 경로 표시에서 결정적 단서 발견: BNCC: keyword · UNKNOWN: keyword · DWLG: keyword. DWLT.xlsx 가 UNKNOWN 으로 분류되어 DWLT 탭이 빈 화면. V22.17 날짜 시프트 fix 는 정상 작동했으나, 파일명 인식 자체가 실패한 별개 버그.
  • FIX근본 원인 — X-File-Name 커스텀 헤더 strip. /api/daily-file 응답에 res.setHeader('X-File-Name', encodeURIComponent(...)) 로 파일명 전달하는데, 외부 도메인 reverse-proxy(DDNS HTTPS) / 또는 일부 브라우저 캐시 단계에서 X- prefix 커스텀 헤더가 strip 됨 → 클라이언트 r.headers.get('X-File-Name') = null → fileName 빈 문자열. d_parseSiteBuffer 의 본문 텍스트 fallback 도 DWLT.xlsx 본문엔 'DWLT' 키워드 없음 (한글 청구처명만) → UNKNOWN 분류. DWLG.xlsx / BNCC.xlsx 는 본문 어딘가에 'DWLG'/'BNCC' 문자열 있어 fallback 매칭 통과.
  • FEATfix — info.curr/info.prev idx 기반 파일명 직접 전달. d_loadSiteFilesParallel 시그니처에 fileNames 인자 추가 (호출부 loadDailyData 에서 info.curr / info.prev 그대로 전달). 헤더가 정상이면 헤더 우선, strip 되면 fileNames[idx] fallback. 어떤 reverse-proxy / 캐시 환경에서도 사이트 분류 일관성 보장.
  • OPS변경 파일 1개 — src/js/51-view-daily.js (d_loadSiteFilesParallel 시그니처 확장 + 호출부 2곳, +7줄/-3줄). server.js / CSS 변경 0. HTML dev-row V22.18 + V22.17 latest 강등 + 사이드바 brand V22.18 + welcome card V22.18. prod 직배포 정책 3차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.17.
2026-05-19
V22.17 일일매출 — DWLT 날짜 1일 시프트 hotfix (xlsx.js 52초 부정확성 보정)
  • FIX사용자 보고: 부산본부 일일매출현황에 DWLT 매출이 안 가져와짐. 진단 결과 — 서버 파일 인식 OK (BNCC/DWLG/DWLT 3개 모두 인식), 클라이언트 xlsx 파싱도 319행/220백만원 정상 추출. 다만 DWLT 의 모든 매출이 1일씩 앞당겨져서 5월 데이터가 4월로 분류되어 5월 차트에서 미표시되던 상태.
  • FIX근본 원인 — xlsx.js cellDates 52초 부정확성 + timezone 교정 코드 결합. DWLG/BNCC 는 영업/회계팀이 날짜를 텍스트('2026-05-07')로 저장하므로 영향 없음. DWLT 만 엑셀 datetime 객체로 저장 → xlsx.js 변환 시 UTC -52초 오프셋 발생 (Excel 1900년 윤년 버그 보정 부산물). 예: 엑셀 셀 2026-05-01 00:00:00 → JS Date 2026-04-30T14:59:08Z → KST 환산 2026-04-30 23:59:08 (자정 -52초). 기존 코드 new Date(dr.getTime()-off).toISOString().split('T')[0] 가 자정 직전이라 전날로 잘림.
  • FEATfix — 1분 추가 후 로컬 시간 직접 추출 (src/js/51-view-daily.js:378-388 datetime 분기). 기존 timezone offset 산술 폐기 → new Date(dr.getTime() + 60000) 로 1분 추가해서 자정 통과 + getFullYear/getMonth/getDate 로 로컬 시간 기준 YYYY-MM-DD 직접 포맷. 정상 datetime (예: 오후 2:30) 도 1분만 더해도 같은 날짜라 부작용 없음. DWLG/BNCC string 분기는 else 로 빠져 영향 0.
  • FEAT로컬 시뮬 검증 — xlsx.js v0.18.5 로 5월 DWLT.xlsx 파싱 시뮬: 319행 100% 5월 분류 (이전 100% 4월 시프트). 일별 5/1=10.8M / 5/2=25.9M / ... / 5/18=13.1M 정상 분포. 총매출 220,194,800원.
  • OPS변경 파일 1개 — src/js/51-view-daily.js (datetime 분기 +5줄/-2줄). server.js / CSS 변경 0. HTML dev-row V22.17 + V22.16 latest 강등 + 사이드바 brand V22.17 + welcome card V22.17. prod 직배포 정책 2차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.16.
2026-05-19
V22.16 매출 분석 — Chart.js update 패턴 일관화 (drawSpark·drawDrill·drawForecast destroy+create 폐기)
  • PERF차트 destroy+create → update 패턴 전환 — V22.15 정적 분석에서 발견된 "차트를 매번 부수고 다시 만드는 패턴". V22.x 누적으로 매출분석 모듈이 헤비해진 가장 큰 원인 중 하나. drawSpark (4 사업장 카드 sparkline 4번 호출) · drawDrill (거래처 dropdown 변경 시) · drawForecast (예측 차트) 세 함수 모두 진입 시 destroyChart() 호출 → new Chart() 재생성하던 패턴. update4line·updateDrill·updateQuadFromRaw 등 다른 update 함수들과 일관성 없던 부분 종결.
  • FEAT패턴: Chart.getChart(id) 존재 시 ch.data 갱신 후 ch.update(), 부재 시에만 new Chart() — 첫 진입 시는 동일하게 new Chart() 1회 (option·devicePixelRatio 등 메모리 누수 없는 초기 설정) → enterSales 재진입 / SSE reload 시 동일 캔버스 재사용. Chart.js v3+ 표준 update() 호출은 데이터 비교 후 변경분만 재렌더 (DOM·WebGL context 보존).
  • FEAT예상 효과 (정성) — enterSales 진입 시 drawAllCharts 의 destroy+create 5건 중 3건(drawSpark ×4 + drawDrill + drawForecast) 제거 → 매출분석 탭 재진입 + SSE reload 시 메인 thread 점유 시간 단축. drawMain4line / drawQuad / detail bar·donut 의 destroy+create 패턴은 후속 사이클(V22.17+) 검토 — 본 사이클은 호출 빈도 가장 높은 3건만 우선 적용 (위험도 최소화).
  • OPS변경 파일 1개 — src/js/57-view-sales.js (drawSpark / drawDrill / drawForecast 3 함수 update 패턴 +30줄). server.js / CSS 변경 0. HTML dev-row V22.16 + V22.15 latest 강등 + 사이드바 brand V22.16 + welcome card V22.16. prod 직배포 정책 1차 사이클 (원칙 #22). 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.15.2.
2026-05-19
V22.15 매출 분석 — 사이트/월 변경 응답성 개선 (Layer 2/3/4 디퍼링 + busy 페이드)
  • FIX사이트 탭 / 월 dropdown 변경 시 무거운 처리 디퍼링 — 사용자 보고: 매출분석 페이지에서 월 이동 + 사업장 이동 시 로딩 늦어짐. 진단: NAS server 응답 정상 (smoke 22/22 PASS + SSE < 100ms) → 클라이언트 측 무거움. 사이트 탭 onclick 한 번에 동기적으로 9개 update 함수 + Chart.js 4-6개 destroy+create + 100+ 거래처 innerHTML 일괄 실행 → 메인 thread 블로킹 200~500ms.
  • FEATLayer 1 즉시 + Layer 2/3/4 setTimeout 0 디퍼링 — bindToggleGroup 사이트 탭 onclick + refreshAll (월 dropdown change) 모두 동일 패턴 적용:
    ① 즉시 동기 처리: active 클래스 토글 + Layer 1 (KPI 4중 비교 표 + 사이트 카드 active 전환 + 헤더 뱃지 갱신) — 사용자가 가장 먼저 보는 핵심
    ② 다음 paint 후 디퍼링 (setTimeout 0): 4-line 차트 + Layer 2 히트맵·4분면·드릴 + Layer 3 Funnel·Forecast·Closing + Layer 4 Action Queue. 메인 thread 양보 → 브라우저가 즉시 paint 완료 후 무거운 작업 처리.
    결과: 클릭 직후 사이트 탭 + Layer 1 표는 즉시 반응 (< 50ms), 나머지는 200-300ms 후 자연스럽게 채워짐.
  • FEATbusy 상태 시각 피드백 — 사이트 변경 / 월 변경 시 #view-sales.s-busy 클래스 적용 → Layer 2/3/4 카드들 opacity 0.55 + pointer-events:none + 0.15s 페이드. 미니 토스트 "⏳ 갱신 중 — 사업장 DWLG" / "⏳ 갱신 중 — 분석 시점 2026.04" 동시 표시. 디퍼링 완료 시 자동 해제. 사용자가 "응답 안 되는 줄 알았다" 오해 방지.
  • FEAT운영 정책 전환 — staging 폐기, prod 직배포 + smoke 31 안전망 (사용자 결정) — V22.11.1~V22.14 사이클의 SSH timeout / restart timeout 누적 사고 분석 결과 staging+prod 두 컨테이너 동시 부담이 원인. 사용자 결정: ① staging 컨테이너 영구 폐기 (NAS RAM ~50MB 회수 + chokidar 폴링 중복 제거) ② smoke 22 → 31 강화 (API 응답 본문 정합성 4종 + 빌드 산출물 핵심 마커 5종) ③ deploy.py 자동 롤백 안전망 (이미 보유: --rollback N + --rollback-to TAG) ④ scripts/.credentials NAS_HOST 외부 도메인 → 내부 IP 1.254.6.240 전환 (DDNS + reverse-proxy 우회 → SSH/HTTP 즉시 응답). CLAUDE.md 원칙 #21 (staging-first) 폐기 + 원칙 #22 (prod 직배포) + 원칙 #23 (내부 IP 우선) 신설.
  • FEATSynology Gitea 폐기 — git origin 제거, 로컬 git 유지 (사용자 결정) — Synology NAS의 Gitea 컨테이너 폐기. git remote remove origin 으로 push 대상 0개. 사용자 의도: NAS DSM 부담 추가 완화 + 외부망 502 사고 종결 + 단순화. 로컬 commit + tag 는 유지 — deploy.py --rollback-to v22.X 안전망 + dev-row 히스토리 활용. git push --follow-tags 자동화는 폐기 (원칙 #20 갱신). 향후 GitHub로 push 필요 시 git remote add origin <github-url> 1줄로 연결 가능. CLAUDE.md 원칙 #24 (Synology Gitea 폐기) 신설. 메모리 [gitea_busan_dashboard.md] · [project_3pc_workflow.md] · [feedback_auto_commit_push.md] 모두 갱신.
  • OPS변경 파일 8개 — src/js/57-view-sales.js (디퍼링 패턴 + _setBusy +35줄) · src/css/57-view-sales.css (s-busy 페이드 +8줄) · scripts/smoke.py (22 → 31 항목, API body + HTML marker 검증 +70줄) · scripts/deploy.py (staging deprecated 경고) · scripts/.credentials (NAS_HOST 내부 IP) · CLAUDE.md (원칙 #21 폐기 + #22·#23 신설) · 메모리 [`feedback_prod_direct_deploy.md`](신규) + [`feedback_staging_first_deploy.md`](폐기 표시). server.js 변경 0. HTML dev-row V22.15 + V22.14 latest 강등 + 사이드바 brand V22.15 + welcome card V22.15. 롤백 1명령: python scripts/deploy.py --rollback-to v22.14.
2026-05-19
V22.14 매출 분석 — Action Queue PDF 보고서 결선 (window.print + @media print, CDN 0)
  • FEAT📄 임원 보고용 PDF 버튼 결선 — Action Queue footer 의 primary 버튼 → onclick window.s_printActionQueue(). 브라우저 native print 다이얼로그 호출 → 사용자가 "PDF로 저장" 선택 → A4 가로 PDF 생성. jsPDF/html2canvas 등 신규 CDN 추가 0 — NAS 안정성 영향 0.
  • FEAT@media print CSS 최적화 — 인쇄 시 사이드바·차트·Layer 1·2·3·모달·아이콘 버튼 모두 hide. Action Queue 표만 단독 노출. 다크 테마 색 → 흑백 친화 (배경 흰색·텍스트 검정·테두리 회색). 헤더 행 회색 강조. .s-row-done 행은 line-through + opacity 0.5 (완료 거래처 시각 식별). 숨김 처리된 (.s-hide-done) 완료 거래처는 인쇄에서도 제외. ✓/✎/📜 아이콘 인쇄 불필요라 hide. @page {size:A4 landscape; margin:10mm} 가로 A4 + 10mm 여백.
  • FEATprint 전용 헤더 동적 삽입 — 인쇄 직전 임시 #s-print-head 요소 삽입: "부산본부 매출 분석 — 우선 처리 거래처 / 회계마감 YYYY.MM · 사업장 SITE · 거래처 N건 · 출력일 YYYY-MM-DD". 화면에는 display:none, @media print 에서만 노출. print 다이얼로그 닫힘 후 800ms 뒤 자동 제거 — 기존 마크업 영향 0. body.s-printing 클래스로 print 모드 격리.
  • FEAT페이지 분할 보존 — page-break-inside:avoid tr 단위 + thead {display:table-header-group} 로 페이지 넘어가도 표 헤더 자동 반복. 거래처 100+개도 깔끔한 다중 페이지 PDF.
  • FEAT가드 — _lastActionQueue 비어있을 때 alert. setTimeout 80ms 후 window.print 호출 — 헤더 DOM 삽입 완료 보장. 다이얼로그 닫힌 후 800ms timer 로 헤더 + s-printing 클래스 모두 자동 정리.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (s_printActionQueue +35줄 + window 노출 + footer 버튼 onclick) · src/css/57-view-sales.css (@media print 블록 +35줄). server.js 변경 0. HTML dev-row V22.14 + V22.13 latest 강등 + 사이드바 brand V22.14 + welcome card V22.14. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.13.
2026-05-19
V22.13 매출 분석 — Layer 4 영업 대응 목록 YTD 매출 단일 내림차순 정렬
  • FEATYTD 매출 큰 순 정렬 — 사용자 요청. 기존 정렬 룰: 우선순위(P1/P2/P3) 1차 → YTD 매출 2차 였음. 변경: YTD 매출 1차 내림차순 단일 룰. 영업본부장이 매출 규모 기준으로 큰 거래처부터 위에서 아래로 순차 검토 가능. 우선순위 분류 (P1 긴급 / P2 주의 / P3 관리) 자체는 그대로 — 행 좌측 색 칩으로 시각적 식별 가능하나 정렬 우선 순위에서는 제외.
  • FEAT헤더 부제 변경 — 기존 "P1 긴급 / P2 주의 / P3 관리 · 선택월 기준 전체 표시" → "YTD 매출 큰 순 · 선택월 기준 전체 표시". title hover 툴팁에 우선순위 칩 의미 보존 ("YTD 매출 큰 순 정렬. 우선순위 칩은 행 좌측에 표시").
  • OPS변경 파일 1개 — src/js/57-view-sales.js (buildActionTableFromRaw 정렬 함수 1줄 + 헤더 부제 텍스트 1곳). server.js / CSS 변경 0. HTML dev-row V22.13 + V22.12 latest 강등 + 사이드바 brand V22.13 + welcome card V22.13. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.12.
2026-05-19
V22.12 매출 분석 — Action Queue Excel 내보내기 결선 (SheetJS XLSX 활용)
  • FEAT📊 Excel 내보내기 버튼 결선 — Action Queue footer 의 [Excel 내보내기] 정적 버튼 → onclick window.s_exportActionQueueXlsx() 결선. 현재 화면 보고 있는 상태(사이트 필터 + 월 dropdown) 그대로 xlsx 다운로드. 50-view-pl.js 이미 사용 중인 글로벌 SheetJS XLSX 재활용 → 신규 CDN 추가 0.
  • FEAT13 컬럼 풀 데이터 — #/우선(P1/P2/P3)/거래처/사업장/YTD 매출(억)/자동 시그널(평문)/영업 메모(평문)/담당자/권장 액션/기한(일)/기한 날짜(YYYY-MM-DD)/완료 여부(✓완료)/완료 시각. HTML 시그널 칩은 _stripHtml 헬퍼로 평문 변환. sales-notes [담당:이름] prefix 도 메모 본문만 추출. sales-completions 영속 데이터 (V22.9) 의 완료 시각도 자동 포함.
  • FEAT파일명 자동 산출 — 매출분석_우선처리거래처_YYYY.MM_사이트_YYYYMMDD.xlsx. 회계마감 월 + 사이트 필터 + 다운로드 일자 모두 파일명에 명시 → 회계팀이 월별/사업장별 보관 시 자동 정렬. 시트명 "우선처리 거래처".
  • FEAT가드 — XLSX 미로드 시 친절 alert + 로드 재시도 안내. _lastActionQueue 빈 배열일 때 (데이터 로드 전) alert. _lastActionQueue 모듈 변수는 buildActionTableFromRaw 가 매 렌더링마다 갱신 → 사이트 탭 변경 / 월 dropdown 변경 시 항상 최신.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (_lastActionQueue / _lastActionQueueSite 모듈 변수 + _stripHtml + s_exportActionQueueXlsx +60줄 + footer onclick + window 노출) · src/부산본부_대시보드.html (dev-row V22.12 + V22.11.1 latest 강등 + 사이드바 brand V22.12 + welcome card V22.12). server.js / CSS 변경 0. staging-first 정책 7차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.11.1.
2026-05-19
V22.11.1 매출 분석 — 상세 모달 ✎/📜 글로벌 노출 hotfix
  • FIX거래처 상세 모달의 ✎ 메모 편집 + 📜 변경 이력 버튼 무반응 hotfix — 사용자 보고. V22.11 의 close 호출 제거 후 두 버튼 모두 클릭 시 아무 동작 안 함. 원인: 상세 모달 안 버튼들의 onclick="s_openMemoEdit(...)" / onclick="s_openMemoHistory(...)" 가 IIFE 클로저 안 함수 직접 호출 — onclick 속성은 글로벌 스코프만 해석 가능하므로 ReferenceError 발생. V22.11 전에는 s_closeClientDetailModal();s_openMemoEdit(...) 조합이라 close 가 먼저 실행되고 그 다음 ReferenceError 로 throw → 사용자 입장에서 "모달이 닫힘" 으로 인지. close 제거 후엔 ReferenceError 만 남아 시각 변화 0. fix: window.s_openMemoEdit = s_openMemoEdit + window.s_openMemoHistory = s_openMemoHistory 2줄 글로벌 노출. 이벤트 위임 click 핸들러 (행 안의 ✎/📜 아이콘) 는 IIFE 클로저 직접 호출이라 영향 없음 — 영향 범위는 상세 모달 하단 2 버튼 한정.
  • OPS변경 파일 1개 — src/js/57-view-sales.js (window.s_openMemoEdit / window.s_openMemoHistory +2줄). server.js / CSS 변경 0. HTML dev-row V22.11.1 + V22.11 latest 강등 + 사이드바 brand V22.11.1 + welcome card V22.11.1. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.11.
2026-05-19
V22.11 매출 분석 — Action Queue 3건 통합 (모달내 메모편집 / 완료상태 race / 완료숨기기)
  • FIX거래처 상세 모달 안 ✎ 메모 편집 클릭 시 모달이 닫히던 버그 — 사용자 보고. 라인 2408 onclick 에 s_closeClientDetailModal();s_openMemoEdit(...) 가 명시되어 모달이 닫힌 채 prompt 가 떴다. 사용자 의도: 상세 모달은 유지된 상태에서 메모 편집 → 작업 완료 후 즉시 갱신된 메모로 상세 내용 계속 확인. fix: ① onclick 에서 s_closeClientDetailModal() 호출 제거 → prompt 가 모달 위에 native 표시 ② 메모 영역에 id="s-detail-memo-body" + data-client 부여 ③ _renderDetailMemoBody(client) 헬퍼 추출 ④ s_openMemoEdit 의 then 콜백에서 해당 id 발견 시 메모 영역만 in-place 부분 재렌더링. 변경 이력(📜) 버튼도 동일 패턴 적용 — close 호출 제거.
  • FIX완료처리 후 사이트 탭 클릭 시 완료 상태가 초기화되던 race condition — 사용자 보고. ✓ 클릭 → 시각 토글 + saveSalesCompletion POST(비동기) → POST 응답 도착 전에 사용자가 사이트 탭 ([DWLG]/[DWLT]/[BNCC]) 클릭 → buildActionTableFromRaw 재호출 → 행 렌더링 시 _salesCompletions[client] 미갱신 → isDone=false 로 새로 그림 → 시각 초기화처럼 보임. fix: 낙관적 캐시 업데이트 — 클릭 즉시 _salesCompletions[client]={completed:true, completedBy:'pending'} 캐시 갱신 → 사이트 탭 클릭으로 재렌더링되어도 isDone=true 유지. POST 실패 catch 에서만 캐시·시각 모두 원복.
  • FEAT👁 완료 숨기기 토글 버튼 — Action Queue 헤더에 신설. ① localStorage busan_sales_hide_done 키로 개인 PC 별 영속 (다른 PC와 분리, 새로고침/세션 종료 후에도 보존) ② 버튼 우측에 현재 숨겨질 행 수 (N) 표시, 활성 시 파란색 강조 ③ 클릭 즉시 모든 .s-row-done 행에 .s-hide-done 클래스 토글 — 재렌더링 없이 CSS display:none 로 시각만 변경 (성능 0 부하) ④ buildActionTableFromRaw 행 렌더링 시 hide 상태면 .s-hide-done 동반 적용 (사이트 탭 변경에도 일관 유지) ⑤ ✓ 클릭 즉시 완료 처리한 행도 hide 모드면 자동 숨김 (시각적 끊김 없음).
  • FEAT완료 상태 시각 일관성 — V22.9 + V22.11 결합으로 ① NAS JSON 영속 (3PC 핸드오버 일관) + ② 낙관적 캐시 갱신 (즉시 재렌더링 안전) + ③ 숨기기 토글 (개인 PC localStorage). 새로고침·view 전환·사이트 탭 클릭·다른 PC 접속 어떤 시나리오에도 시각 끊김 0. 사용자가 ✓ 처리한 작업은 절대 사라지지 않음.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (상세 모달 close 제거 + _renderDetailMemoBody 헬퍼 + s_openMemoEdit then 부분 재렌더 + ✓ 클릭 낙관적 캐시 + 토글 ttl 마크업 + _updateHideDoneBadge + s_toggleHideDone + window 노출 +60줄) · src/css/57-view-sales.css (s-hide-done + s-hide-done-btn 스타일 +12줄). server.js 변경 0 (V22.9 의 라우트 그대로 활용). HTML dev-row V22.11 + V22.10 latest 강등 + 사이드바 brand V22.11 + welcome card V22.11. staging-first 정책 6차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.10.
2026-05-19
V22.10 매출 분석 — 시그널 칩 z-score 부호 분기 (양수=녹색 긍정 / 음수=빨강 부정)
  • FEAT📊 통계 이상치 칩 부호 분기 — V22.7 의 z-score 칩이 양수·음수 모두 보라(s-trend) 단일 색으로 표시되어 영업본부장이 "이게 급증인가 급감인가" 한눈에 판별 불가. 시안 §5 사양에 맞춰 부호별 색 분기:
    • z=+N.Nσ (양수, 통계적 급증) → 녹색 (s-stat-pos) + 보더 + font-weight 700 — 긍정 신호, 영업 기회
    • z=-N.Nσ (음수, 통계적 급감) → 빨강 (s-stat-neg) + 보더 + font-weight 700 — 부정 신호, 긴급 대응
    title 툴팁도 분기 — "통계적 급증 (긍정 신호)" / "통계적 급감 (부정 신호)" 으로 의미 명시.
  • FEATCSS 신규 클래스 2종 — .s-signal.s-stat-pos (rgba(16,185,129,.18) 녹색 + 보더 .4 opacity) · .s-signal.s-stat-neg (rgba(239,68,68,.18) 빨강 + 보더 .4 opacity). 기존 s-stop(빨강 무거래)·s-drop(노랑 감소)·s-rec(파랑 회복)·s-new(녹색 신규)·s-trend(보라 추세) 5종 시각 룰 유지 + 통계 이상치 전용 2종 신설 → 시각 의미 명확 분리.
  • FEAT일관성 — 분류 룰과 색 의미 통일 — V22.7 의 classifyClient 룰에서 통계 이상치 자체는 부정적 분류(쇠퇴) 트리거였지만, z-score 양수 (당월 +2σ 이상 급증) 는 일시적 폭발 매출 (대형 계약 체결 등) → 영업 기회. 양수 칩이 빨강·보라로 표시되던 V22.7 까지의 시각적 불일치 해소 → 부호별 정확한 의미 전달.
  • OPS변경 파일 2개 — src/css/57-view-sales.css (s-stat-pos / s-stat-neg 2 클래스 +3줄) · src/js/57-view-sales.js (buildSignals z-score 분기 +5줄). server.js 변경 0 (분류 룰 자체는 정밀화 없음, 시각 표현만). HTML dev-row V22.10 + V22.9 latest 강등 + 사이드바 brand V22.10 + welcome card V22.10. staging-first 정책 5차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.9.
2026-05-19
V22.9 매출 분석 — Action Queue ✓ 완료 영속 (NAS JSON 결선, sales-notes 패턴 복제)
  • FEATserver.js sales-completions 라우트 3종 — V20.2/V22.6 의 sales-notes 라우트 패턴 100% 복제. GET /api/sales-completions [?client=거래처명] · POST /api/sales-completions body {client, completed:bool} + If-Match etag 충돌 감지 409 · GET /api/sales-completions/history [?client=&limit=50]. SALES_COMPLETIONS_PATH 환경변수 오버라이드 + SALES_COMPLETIONS_HISTORY_LIMIT 1000. 헬퍼 4종 (_emptySalesCompletionsSkeleton · _readSalesCompletions · _saveSalesCompletionsAtomic · _salesCompletionsEtag) 도 sales-notes 헬퍼 카피. 원자적 쓰기(tmp → renameSync) · 손상 감지 자동 백업 · history 누적 1,000건 cap. NAS 부팅 시 /app/data/sales_completions.json 자동 골격 생성.
  • FEAT클라이언트 결선 — loadSalesCompletions + saveSalesCompletion + 토글 영속 — IIFE 안 _salesCompletions 캐시 + _salesCompletionsEtag 모듈 변수. loadSalesData Promise.all 5번째 fetch 추가 (PL/RAW/Pipeline/sales-notes 옆에). buildActionTableFromRaw 행 렌더링 시 _salesCompletions[clientName].completed 있으면 <tr class="s-row-done"> + ✓ 아이콘에 class="s-done-active" + title "완료 취소 (다시 클릭)". V22.7.7 의 토글 click 핸들러에 saveSalesCompletion(client, nowDone) 추가 — POST 실패 시 시각 상태 자동 원복 (.catch 안에서 classList.toggle 역동작). X-User-Name: encodeURIComponent(user) 헤더 (V20.3 ISO-8859-1 호환).
  • FEATIf-Match 충돌 감지 + lost update 차단 — 3PC 핸드오버 환경에서 다른 PC가 동시에 완료 토글 시 server.js HTTP 409 → alert "다른 사용자가 완료 상태를 변경했습니다. 새로고침 후 다시 시도해주세요." + 새 etag 자동 수신. V22.6 sales-notes 패턴 동일.
  • FEAT3PC 핸드오버 환경 완전 영속 — Mac/데스크탑/노트북 어디서 ✓ 완료 처리해도 NAS JSON 한곳에 저장 → 다른 PC 접속 시 동일 완료 상태 노출. 컨테이너 재시작 후에도 보존. 변경 이력 (작성자·시각·before/after) 누적 — 향후 V22.10+ 에서 history 모달 UI 추가 가능 (라우트는 V22.9 부터 가동).
  • OPSsmoke.py 21 → 22 — GET /api/sales-completions 200 검증 항목 신설. API_PATHS 11→12번째 항목, 전체 21→22 (SSE 6 + Auth 1 + md5 1 + preflight 1 + view-sales 1 = +1).
  • OPS변경 파일 3개 — src/server.js (SALES_COMPLETIONS_PATH 상수 + 헬퍼 4종 + 라우트 3종 +95줄) · src/js/57-view-sales.js (_salesCompletions 캐시 + loadSalesCompletions + saveSalesCompletion + 행 렌더링 isDone 분기 + 토글 click 결선 +70줄) · scripts/smoke.py (sales-completions 항목 +1). HTML dev-row V22.9 + V22.8 latest 강등 + 사이드바 brand V22.9 + welcome card V22.9. CSS 변경 0 (s-row-done / s-done-active V22.6/V22.7 패턴 재사용). server.js 변경 → --include-server-js 필요. staging-first 정책 4차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.8.
2026-05-19
V22.8 매출 분석 — SSE 자동 갱신 결선 (PL · RAW · Pipeline xlsx 변경 시 자동 reload)
  • FEATSSE 3채널 결선 — 매출 분석 모듈 자동 갱신 — 회계팀이 매월 마감 직후 PL/RAW xlsx 를 NAS 에 덮어쓰거나 영업팀이 Sales Opp Pipeline 주차본을 갱신할 때, 사용자가 화면을 보고 있어도 별도 새로고침 없이 자동 반영되도록 결선. server.js 의 chokidar 감시 + SSE broadcast 인프라는 이미 V19.5/V20.4 부터 완비 — 본 사이클은 매출분석 모듈 (57-view-sales.js) 의 클라이언트 결선만 추가. ① 3 EventSource — /api/watch (PL) · /api/rowdata-watch (RAW) · /api/sales-pipeline-watch (Pipeline) 3채널 동시 연결. 원칙 8 App.api.connectSSE 헬퍼 경유로 지수 백오프 재연결(1s→30s) 자동. ② ?pw= query 인증 — EventSource 는 custom header 불가하므로 ?pw=encodeURIComponent(SERVER_PW) 로 통일 (V20.1.7 hotfix 패턴 재사용). ③ 800ms 디바운스 — 한 번의 xlsx 저장이 chokidar 'add'+'change' 다중 이벤트로 들어와도 단일 reload 로 합산. _sseTriggers 객체에 PL/RAW/Pipeline 소스 누적 후 토스트에 합쳐 표시 ("📂 데이터 자동 갱신 — PL·RAW").
  • FEAT캐시 무효화 + 4 fetch 재실행 — s_sseReload 가 트리거되면 모듈 캐시 (_rdRaw / _rdClients / _pipe) 비우고 App.data.pl.reload() 호출 후 loadSalesData() 재실행. App.data.pl.reload() 는 50-view-pl.js V17.4 부터 노출된 read-only getter 의 reload 메서드 — PL xlsx 재 다운로드 + parseWorkbook 재실행. RAW/Pipeline 은 apiFetch 가 매번 신선한 fetch 라 캐시 비움만으로 충분. 4-Layer (L1 PL 4중비교+12M 콤보 / L2 RAW 히트맵+4분면 / L3 Pipeline Funnel+Forecast / L4 Action Queue) 모두 새 데이터로 재계산. 사용자가 사업장 탭이나 연도/월 dropdown 으로 변경한 필터 상태는 보존 (재진입 아니라 데이터만 갱신).
  • FEATview-sales hide 가드 + 토스트 UX — s_sseReload 가 view-sales 의 display === 'none' 체크 — 사용자가 다른 뷰 (손익현황/일일매출 등) 에서 작업 중이면 reload skip. 트리거 누적은 reset 되므로 다음 enterSales 진입 시 자연스럽게 fetch (1회). reload 실행 시 화면 우상단 토스트 ("📂 데이터 자동 갱신 — PL·RAW·Pipeline") 2.2초 노출. 기존 글로벌 .update-toast CSS 재사용 (손익현황·일일매출 V18.7 패턴 일관성). 동적 element 생성 + body append + idempotent.
  • FEAT재진입 idempotent — connectSalesSSE 가 매 호출 시 _sseConns.{pl,rd,pipe} 기존 연결을 close() 후 재연결. 사용자가 매출분석 → 다른 뷰 → 매출분석 재진입해도 SSE 중복 방지. App.api.connectSSE 의 close() 반환 객체로 stopped 플래그 + EventSource.close + reconnectTimer.clearTimeout 일괄 정리.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (SSE 인프라 +90줄: _sseConns/_sseTriggers 모듈 변수 + s_sseHandler/s_sseReload/s_showAutoReloadToast/connectSalesSSE 4 헬퍼 + enterSales 끝에 connectSalesSSE() 호출 1줄) · src/부산본부_대시보드.html (dev-row V22.8 + V22.7.1 latest 강등). server.js 변경 0 (이미 chokidar+SSE 완비). CSS 변경 0 (글로벌 .update-toast 재사용). staging-first 정책 3차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.7.1.
2026-05-19
V22.7.1 시스템 — 대시보드 리비전 표기 V22.7 통일
  • FIX대시보드 리비전 통일 — 사용자 요청. ① 사이드바 brand 부제 Integrated Ops v1.6 → Integrated Ops V22.7 (V19.2 시점 이후 미갱신, 매출분석 V22.x 5사이클 진행 반영). ② 메인메뉴 welcome 카드 현재 버전 v1.2 → 현재 버전 V22.7. 사용자 접속 시 즉시 보이는 두 곳 모두 현재 prod 운영 버전과 일치. 코드 변경 0, 텍스트만 2 곳.
2026-05-19
V22.7 매출 분석 — 워닝 룰 정밀화 + 영업담당 실명 매핑 + 용어 친절 안내 (3건 통합)
  • FEAT워닝 룰 정밀화 (시안 §5 자동 시그널) — V22.3 단순 룰을 시안 사양서 §5 룰로 정밀화. extractClientsFromRaw 에 5종 메트릭 신설:
    • z-score — 직전 11M 평균/표준편차 기반, 당월 |z|≥2σ 시 통계 이상치 (isStatOutlier)
    • least-squares slope — 12M·3M 회귀선 기울기 정확 산출 (이전: 단순 양끝 차이)
    • noTradeMonths — 마지막 양수 거래 월부터 무거래 연속 개월
    • dormantRecover — 직전 3M 합산 0 + 그 이전 매출 이력 + 당월 양수 시 휴면→복귀
    • lastTradeMonthIdx — 마지막 거래 월 인덱스 (UI 표시용)
    classifyClient 룰 확장 — 쇠퇴 4분기 조건 (무거래/급감/통계이상/동반하락 중 하나) · 부진 통계이상 OR 급감 · 스타 추세 안정 추가.
  • FEAT시그널 칩 9종 + 친절 툴팁 — buildSignals 확장. 모든 칩에 title 속성으로 의미 hover 툴팁:
    📉 -X% / 📉 -X% 30%대 / 🔄 +X% / 🔄 휴면 복귀 / 📊 z=±N.Nσ 통계 이상치 / ⚠ 12M+3M↓ 동반 하락 / ⚠ 12M↓ 장기 / ⚠ 3M↓ 단기 / 🆕 신규 / 🛑 N개월 무거래.
  • FEAT영업담당 실명 매핑 — sales-notes 재활용 (server.js 변경 0) — 메모 형식 [담당:이름] 메모 본문. parseMemoOwner / buildMemoString 파싱·조립 헬퍼 신설. _teamForClient 우선순위 변경 — 1순위 sales-notes 의 [담당:이름], 2순위 거래처명 hash 임시 (김부장/박과장/이차장/최대리 + " (임시)" 접미사로 등록 전 명시).
  • FEAT✎ 편집 2-step prompt — 기존 단일 prompt → 담당자 이름 + 메모 본문 두 단계 분리 입력. 담당자만 등록 + 메모만 등록 + 둘 다 등록 모두 가능. 빈 칸 = 미배정 또는 메모 없음. 저장 형식 [담당:김부장] 단가 협상 결렬 자동 조립.
  • FEAT📚 용어 안내 모달 — 헤더 우측 [데이터 소스] 옆에 [📚 용어 안내] 버튼 신설. 모달 5 섹션:
    ① 거래처 4 카테고리 (쇠퇴/부진/스타/신규) 자동 분류 룰 — 색·임계값·우선순위
    ② 자동 시그널 칩 9종 — 실제 칩 + 의미 설명
    ③ 우선순위 P1/P2/P3 — 기한·대응 액션
    ④ 표·차트 용어 — 당년 계획·실적·전년·선택월·연간 누계·YoY·YTD·slope·z-score·결산조정 11종
    ⑤ 영업담당자 설정 — ✎ 사용법 + 임시 표시 안내
  • FEAT4분면 카테고리 라벨 title 툴팁 — 4분면 산점도 하단 4 라벨에 친절 설명 hover 툴팁 추가 (신규 주력·스타·쇠퇴·부진 각각의 분류 기준 + 영업본부장 액션 안내).
  • FIX헤더 viewport 고정 (sticky → fixed) — 사용자 보고: 스크롤 내리면 사업장 탭이 안 보여 클릭 불가. 기존 position:sticky;top:0 가 상위 컨테이너 overflow 컨텍스트 영향으로 작동 안 함. position:fixed;top:0;left:0;right:0;z-index:50 로 변경 + box-shadow 그림자 추가 + .s-app padding-top 0 → 78px 로 헤더 가림 보정. 매출 분석 페이지 어디서든 사업장·연도·월·메인메뉴·데이터 소스·용어 안내 즉시 접근 가능.
  • FIX4분면 격자 명료화 + 점 잘림 방지 — 사용자 보고: 격자 간격 불규칙 + 거래처 점 일부 잘림. ① x축 logarithmic 자동 tick (0.2/0.3/0.4/.../1/2/...) → afterBuildTicks 로 0.1/0.2/0.5/1/2/5/10/20/50 9개 명시 격자. ② y축 자동 tick → 데이터 범위 기반 50/100/200 단위 균등 step. ③ updateQuadFromRaw 의 점 데이터 Math.max(-100, Math.min(400, yoyPct)) clamp 제거 — 모든 점 실값 그대로 표시. ④ x·y축 min/max 데이터 ±10% 여유 동적 산출 (저성장 -100% 부진·고성장 +1000% 신규 모두 잘림 없이 표시). ⑤ layout.padding 12/16/4/4px 추가 — bubble 가장자리 잘림 방지.
  • FIX4분면 사업장 필터 시 데이터 없는 격자 제거 — 사용자 보고 (BNCC 선택 시 우측 5억/10억/20억 격자가 데이터 없이 표시). 원인: x축 afterBuildTicks 가 9개 tick 무조건 push → axis.max 가 2 인데도 5/10/20 라벨 출력. fix: afterBuildTicks 안에서 axis.min ≤ v ≤ axis.max 범위 내 tick 만 filter 적용. BNCC (max 약 1.2억) 선택 시 격자 0.1·0.2·0.5·1·2 5개로 자동 축소 → 차트 우측 빈 영역 제거.
  • FEATLayer 3 Pipeline 사업장 필터링 — 사용자 보고: Pipeline (Funnel / Forecast / Closing 알람) 도 사업장 탭 클릭 시 해당 사업장 거래만 표시. updatePipeline(pipe, site) / updateForecast(pipe, plRaw, ym, site) / updateClosingAlarm(pipe, site) 시그니처에 site 인자 추가. site='ALL' 시 전체, 아니면 l.site === site filter. Funnel 4 stage 카운트 + 합산예상매출 + 신뢰도 계산 + closing TOP 3 모두 사업장별 산출. Funnel 헤더 sub 에 "DWLG 사업장 한정 N건" 표기로 필터 상태 명시. Layer 1 + 2 + 3 사업장 연동 일관성 완성.
  • FIXQ2 Forecast 차트 가시성 강화 — 사용자 보고: 차트가 너무 흐릿. V22.5 메인 4-line 차트 패턴 차용. ① 계획 점선 굵기 2→2.5px + 포인트 3px ② 실적 라인 굵기 2.5→4px + 포인트 5px + 흰 테두리 ③ 예측 (mid) 굵기 2→3px + 포인트 4px + 흰 테두리 ④ 예측 band 배경 opacity 0.15→0.25 (진하게) ⑤ 라벨 폰트 9→10px, 그리드 명시 색상 + layout.padding 8/8/4/4px.
  • FEATclosing 알람 설명 추가 — 사용자 보고: "이번달 closing 알람" 의미 불분명. 제목 옆에 보조 텍스트 "— 이번달 내 계약 성사 예상 핫 리드 TOP 3" 추가 + hover title 툴팁 ("Pipeline 엑셀의 Status Update Yes/New 표시 우선") 으로 친절 설명.
  • FIXAction Queue 조작 버튼 사이즈 확대 + 완료 토글 — 사용자 보고: ✓·✎·📜 버튼이 작아서 클릭 불편 + 완료 처리 후 되돌릴 수 없음. ① CSS — font-size 11→16px, padding 3/5→7/10, min-width 34px, gap 4→6, hover 시 파랑+위로 이동 시각 강조. ② 완료 토글 (V22.6 일방향 처리 → 토글) — row.classList.toggle('s-row-done') + 아이콘에 's-done-active' 토글 (녹색 강조). 다시 클릭 시 완료 취소. .s-row-done > td:not(:last-child) CSS 로 행 opacity 0.4 + line-through, 단 마지막 td (조작 버튼) 는 정상 유지하여 토글 클릭 가능.
  • FEAT거래처 상세 모달 [상세 모달 ▶] 활성화 — drill 패널 우측 버튼 onclick=s_openClientDetailModal() 결선. 모달 1100px 너비, KPI 6장 (2025 총매출 / 2026 YTD / YoY % / 25 월평균 / 최대월 / 최소월) + 차트 2종 (24M 시계열 바 + 사업장 분포 도넛 24M 누계) + 자동 시그널 + 영업 메모 + 변경 이력 진입 버튼 2개. 사업장별 매출 분포로 다중 사업장 동시 거래 거래처 비중 가시화. setTimeout 50ms 로 모달 표시 후 Chart.js 컨텍스트 확보. ESC·backdrop·✕ 닫힘 + 차트 destroy 처리.
  • FIX상세 모달 차트 무한 늘어짐 픽스 — 사용자 보고: 상세 모달의 24M 시계열 차트가 viewport 거의 다 차도록 세로로 늘어짐. 원인: canvas inline style="height:280px" 만으로는 Chart.js responsive:true + maintainAspectRatio:false 모드에서 부모 height 인식 못 해 무한정 늘어남. fix: 차트 두 개 모두 부모 wrapper <div style="position:relative;height:280px"> 로 감싸고 canvas inline style 제거. Chart.js 가 부모 박스 크기 정확히 인식.
  • FIXQ2 Forecast 차트 라벨 가독성 2차 패치 — 사용자 보고: y축/x축 라벨이 너무 작아 의미 파악 불가 ("이게 뭘 뜻하는지 볼 수 없다"). ① CSS canvas height 180px → 280px (세로 +56%). ② drawForecast 옵션 — devicePixelRatio: window.devicePixelRatio||2 (retina 선명도), font size 10→13 weight 500, padding 6/8 (라벨 외곽 여유), grid lineWidth 1 명시, suggestedMin 0 / suggestedMax 50 + stepSize 10 (y축 0/10/20/30/40/50 명료한 격자). ③ 차트 부제 "실선=실적·점선=계획·녹색=예측" 안내 추가 + title 툴팁으로 음영 범위 의미.
  • FIXForecast 라인 선명도 3차 강화 — 사용자 재보고: 박스 내 그래프 여전히 흐림. 원인: ① 일반 1080p 모니터에서 window.devicePixelRatio||2 = 1배 적용 (안티알리아싱 흐림) ② tension 0.25 곡률로 부드러움 → 흐림 인상 ③ 점선 dash 패턴 [7,4]·[5,3] 짧아 옅음. fix: ① devicePixelRatio: Math.max(2, window.devicePixelRatio||1) — 1배 모니터도 강제 2배 렌더. ② tension: 0 — 모든 라인 직선화 (꺾인 선 명확). ③ 라인 굵기 일괄 증가: 실적 4→5px, 계획·예측 2.5/3→3/4px. 포인트 5→6 / 4→5. 흰테두리 2→2.5. ④ 점선 dash [10,5] · [8,4] 더 진하게. ⑤ 예측 band opacity 0.25→0.35. ⑥ animation: false (첫 렌더 흐림 방지). ⑦ tick color text2→text 진하게 + weight 500→600.
  • FEATLayer 4 Action Queue 사업장 필터링 — Layer 1·2·3 모두 사업장 연동되는데 Layer 4 만 누락. buildActionTableFromRaw(clients, pipe, site) 시그니처에 site 인자 추가. RAW 거래처 loop 에 siteOk(c.dominantSite) + Pipeline 핫 리드 loop 에 siteOk(l.site) 필터. site='ALL' 시 전체, 아니면 해당 사업장만. site-tabs 핸들러 / refreshAll / loadSalesData / s_openMemoEdit 4 호출처 모두 active site 인자 전달. 사업장 [DWLG] 클릭 시 DWLG 거래처만 우선처리 목록 + 사업장 [통합] 시 전체. Layer 1~4 사업장 연동 완전 일관성.
  • FEATLayer 4 행 클릭 → 거래처 상세 모달 — 사용자 요청. s_openClientDetailModal(clientName) 시그니처 확장 (인자 받기, 없으면 dropdown 값 fallback). 이벤트 위임 통합: 아이콘 (✓/✎/📜) + 권장 액션 버튼 (.s-action-btn) 클릭 = 기존 동작, 그 외 행 클릭 = s_openClientDetailModal(client) 호출. dropdown 동기화 (해당 사업장 옵션이 있으면 value 변경 + 드릴 갱신). CSS — tr[data-client] {cursor:pointer} + hover 시 좌측 파란 세로선 (inset box-shadow 3px) 으로 클릭 가능 시각 명시. 사업장 필터 후에도 동일 동작.
  • FIX거래처 매칭 실패 + Pipeline 신규 잠재고객 fallback — 사용자 보고: "주성씨앤에어" 행 클릭 시 "거래처 데이터 없음" 알람. 원인: ① RAW 비용분석 거래처명과 Pipeline 거래처명 표기 차이 (공백·괄호·㈜·"주식회사"·"(주)" 등) ② Pipeline 신규 잠재고객은 RAW 매출 이력 없음. 2단계 해결:
    ① 정규화 매칭 — s_openClientDetailModal 에서 RAW 직접 매칭 실패 시 공백·괄호·접미사 제거 후 lowercase 비교로 _rdClients 키 fuzzy 매칭. 표기 차이만 다른 거래처는 자동 매칭.
    ② Pipeline lead fallback 모달 — 정규화도 실패하면 s_openPipelineLeadAlert(clientName, lead) 호출. Pipeline 신규 잠재고객은 별도 모달 (800px) 로 정보 표시 — 사업장·영업 담당·예상 매출·현재 상황·결과·거래 구분/방식·서비스·HOT 상태·영업기회 설명·비고 + 안내 "회계 매출 발생 전 신규 영업 기회. 계약 성사 시 자동 반영".
    결과: 주성씨앤에어 같은 Pipeline-only 거래처도 친절한 안내 모달로 정보 확인 가능.
  • FIX분류 룰 결함 수정 — 성장 거래처가 쇠퇴로 잘못 분류되던 버그 — 사용자 보고 (㈜대우로지스틱스 부산터미널 — YoY +391.8% 성장인데 "쇠퇴" 분류). 원인 진단: 25년 상반기 미미했다가 7월 이후 1억대 급증 + 26년 1.1~1.3억 피크 + 4월 한 달 0 (회계 시점 또는 일시) → isStatOutlier(z=-2.2σ) AND last<avg3 단독 조건만으로 쇠퇴 trigger. classifyClient 룰 정정 4건:
    ① YoY 음수 가드 — 쇠퇴·부진 분류 시 c.yoyPct < 0 필수 (성장 거래처 절대 쇠퇴 분류 안 됨)
    ② 스타 분류 우선 — 쇠퇴 검사보다 스타 검사를 먼저 (YoY +10% AND ytd26 0.5억+ AND 동반하락 아님)
    ③ 통계이상치 단독 조건 강화 — isStatOutlier 만으로 쇠퇴 분류 폐지 → isStatOutlier AND isBothDown (동반 하락 추세 필수)
    ④ 용어 안내 모달 의 쇠퇴 분류 룰 설명 갱신 (YoY 음수 가드 + "성장 거래처는 일시 0 매출이어도 쇠퇴 분류 안 됨" 안내 추가).
    결과: ㈜대우로지스틱스 부산터미널 → "쇠퇴" 에서 "스타" 로 재분류 ✓ + Action Queue P1 우선순위에서 자동 제외. 다른 성장 거래처들도 일시적 0 매출에 의해 잘못 분류되지 않음.
  • FIXAction Queue 용어 친숙화 + 버튼 작동 + TOP 8 제한 제거 (3건 통합):
    ① 제목 친숙화 — "🎯 오늘의 우선순위 액션 큐 — TOP 8" → "🎯 우선 처리 거래처 — 영업 대응 목록". 부제 "P1=긴급(쇠퇴) / P2=주의(부진·신규) / P3=관리(스타·신규)·선택월 기준 전체 표시" + title 툴팁 친절 설명.
    ② 버튼 조작 픽스 — 사용자 보고: ✓·✎·📜 버튼 안 눌림. 원인: tbody.addEventListener 위임 + tbody._v226Bound 플래그 패턴이 markup 재주입 / refresh 흐름에서 일시적으로 끊김 가능성. fix: document.addEventListener + window._v227SalesActionsBound 글로벌 플래그로 변경. selector '#view-sales .s-action-icons span' + '#view-sales .s-action-table tr[data-client]' closest 매칭. IIFE 평가 시 한 번만 등록되어 페이지 전 생애주기 안전 작동.
    ③ TOP 8 제한 제거 — 사용자 보고: "해당월 기준 문제 거래처 전부 표현". queue.slice(0, 8) → 전체 queue 그대로 출력. 분류된 모든 거래처 (쇠퇴·부진·신규·스타) 우선순위·매출 큰 순 정렬 후 일괄 표시. 거래처 100+개 시 스크롤로 모두 확인 가능.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (z-score·slope·무거래·복귀 헬퍼 + classifyClient 정밀화 + buildSignals 확장 + parseMemoOwner·buildMemoString + _teamForClient 우선순위 + 2-step prompt + openGlossaryModal +260줄) · src/부산본부_대시보드.html (dev-row V22.7). server.js·CSS 변경 0 (사용자 NAS 안정성 우려로 client-side 한정). staging-first 정책 2차 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.6.
2026-05-19
V22.6 매출 분석 — sales-notes 영속 결선 (Action Queue ✎/📜 활성, NAS JSON 저장)
  • FEAT영업 메모 NAS 영속화 — V20.2 server.js POST/GET /api/sales-notes 라우트 3종 (V19.1 facility-overrides 패턴 100% 복제) 클라이언트 결선. Action Queue 8행의 "메모 미입력 — 영업담당 입력 필요" 정적 텍스트 → _salesNotes[clientName].value 동적 표시 + 작성자·갱신시각 보조 메타. 메모 있으면 본문 색 진하게, 없으면 옅은 회색 이탤릭 "✎ 클릭해서 입력" 안내.
  • FEAT✎ 메모 편집 — Action Queue 행의 ✎ 클릭 → prompt() 메모 입력 → POST /api/sales-notes body {client, note} + If-Match: _salesNotesEtag + X-User-Name: encodeURIComponent(user) 헤더. 첫 편집 시 사용자 이름 prompt → localStorage busan_facility_user_name 저장 (V19.1/V20.3 facility-overrides 와 공유 키). 빈 입력 시 메모 삭제. 저장 성공 시 _salesNotes 캐시 갱신 + Action Queue 즉시 재렌더.
  • FEAT📜 변경 이력 모달 — 📜 클릭 → GET /api/sales-notes/history?client=N&limit=50 호출 → 동적 모달 신규 (s-history-modal). 5컬럼 표 (시간/작성자/액션(✏수정 또는 🗑삭제)/이전/이후) 시간 역순. ESC·backdrop 닫힘. 메모 변경 추적 + 책임 명확화.
  • FEATIf-Match 충돌 감지 — 다른 사용자가 동시에 메모 변경 시 server.js HTTP 409 반환. 클라이언트 alert "다른 사용자가 메모를 변경했습니다. 새로고침 후 다시 시도해주세요." + 새 etag 자동 수신 후 사용자가 재시도 가능. lost update 차단.
  • FEAT이벤트 위임 패턴 — tbody 에 단일 click 리스너 (_v226Bound 플래그로 중복 등록 방지) → ✎/📜/✓ 모두 처리. ✓ 완료 클릭 시 해당 행 0.4 opacity + line-through 시각 처리 (메모리만, V22.7 영속 가능).
  • FEAT4 API 병렬 fetch — loadSalesData 가 Promise.all([pl.ensureLoaded(), apiFetch('/api/rowdata-json'), apiFetch('/api/sales-pipeline'), loadSalesNotes()]) 단일 wave. sales-notes 미수신 시에도 다른 영역 정상 동작 (폴백 안전망).
  • FEAT4분면 사업장 필터링 — 사용자 보고: 사업장 탭 클릭 시 4분면이 모든 거래처를 표시. updateQuadFromRaw(clients, site) + updateQuadLabels(clients, site) 시그니처에 site 인자 추가. site='ALL' 시 전체, 아니면 c.dominantSite === site 인 거래처만 산점도/카테고리 라벨에 포함. bindToggleGroup의 site-tabs 핸들러 + refreshAll + loadSalesData 모두 active site 기반 호출. Layer 1 (PL 표·차트·사이트카드) 과 Layer 2 (4분면) 사업장 연동 일관성 회복.
  • FEAT거래처 dropdown 사업장 필터링 — 사용자 보고: 거래처 상세 진단의 dropdown 도 사업장 탭 클릭 시 해당 사업장 거래처만 표시. buildClientSelectFromRaw(clients, site) 시그니처에 site 인자 추가. 사업장 탭 변경 시 dropdown 재생성 + 그 사업장의 쇠퇴/부진/스타/신규/통상 첫 항목 자동 선택 + 드릴 차트·상세 자동 갱신. 사업장 거래처 0건 시 "— 해당 사업장 거래처 없음 —" 안내 optgroup 표시. 4분면·dropdown·드릴 3자 사업장 연동 완성.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (sales-notes 헬퍼 5종 + s_openMemoEdit/s_openMemoHistory 모달 + buildActionTableFromRaw 메모 표시 + 이벤트 위임 +180줄) · src/부산본부_대시보드.html (dev-row V22.6). server.js·CSS 변경 0. staging-first 정책 첫 적용 사이클 — staging 3002 우선 배포 → 사용자 검증 → prod 승격. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.5.
2026-05-18
V22.5 매출 분석 — UX 단순화 5건 (제목/헤더/용어/표컬럼/차트)
  • FIX제목 부제 삭제 — "부산본부 매출 분석 — 회계마감 결과 분석 콕핏 V22.0" → "부산본부 매출 분석" 단순화 (헤더 시각 잡음 ↓).
  • FIX헤더 토글 단순화 — 사용자 의도 "사업장/연도/월 순". 보기 단위(월별/분기별/연도별) + 기준 연도(2025/2026/YoY 비교) 토글 2종 제거. 헤더에는 사업장 탭(통합/DWLG/DWLT/BNCC) + 연도 dropdown(2025/2026) + 월 dropdown(1~12월) + ↻자동 만 유지. bindToggleGroup('s-period-tabs') / bindToggleGroup('s-year-tabs') 핸들러 제거.
  • FIX용어 친숙화 — "매출 4중 비교" → "매출 종합 현황" — 영업본부장 친숙한 표현. 사업장 4 곳 (통합/DWLG/DWLT/BNCC) 의 카드 제목 + L1_DATA.title + s_l1-title HTML 모두 일괄 변경. 부제도 "PL 시트의 4 컬럼(계획·실적·전년계획·전년실적)을 동일 시점 비교" → "계획 · 실적 · 전년 계획 · 전년 실적 동일 시점 비교" 간결화.
  • FIX표 컬럼 라벨 친숙화 — "당월 (4월)" → "선택월 (4월)" (사용자가 직접 선택하니 자연스러움) · "YTD (1~4월)" → "연간 누계 (1~4월)" · "최근 12M" → "최근 12개월". 한국어 익숙한 표현. refreshAll 의 헤더 갱신 코드도 동시 갱신 (headers[3] 최근 12개월 고정).
  • FEAT차트 콤보 변경 — 가독성 강화 — 사용자 보고: "그래프 다른 방식으로 안되는건가?". 기존 4-line (당년 계획·실적 + 전년 계획·실적) → 콤보 (계획 막대 + 당년 실적 굵은 라인 + 전년 실적 점선 라인):
    • 당년 계획 = 옅은 노랑 막대 (배경 톤, opacity 0.25)
    • 당년 실적 = 굵은 보라 라인 (4px) + 6px 흰테두리 포인트 (강조)
    • 전년 실적 = 가는 회색 점선 라인 (비교용)
    • 전년 계획은 차트에서 제외 (표에는 유지) — 시각 부하 ↓ 영업본부장이 한눈에 핵심 (당년 실적 vs 계획 vs 전년) 파악
    차트 제목도 "12개월 추이 — 4-line 비교" → "최근 12개월 매출 추이" + 부제 "막대 = 계획 · 굵은 라인 = 당년 실적 · 점선 = 전년 실적" 범례 보조.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (헤더 마크업 단순화 -10줄 · drawMain4line 콤보 차트 재작성 · update4line 3-dataset · title 문자열 4곳 일괄 변경) · src/부산본부_대시보드.html (dev-row V22.5 + V22.4 latest 강등). server.js·CSS 변경 0. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.4.
2026-05-18
V22.4 매출 분석 — 사용자 피드백 3건 (RAW 우선 회계마감 + 월 선택 dropdown + 12M 차트 가시성)
  • FIX회계마감 월 자동 fallback — RAW 우선 (V20.7 패턴 차용) — 사용자 보고: V22.1~V22.3 에서 당월이 12월로 표시. 원인: s_currentYearMonth(plRaw) 가 PL 시트 마지막 양수 as_ 월을 회계마감으로 채택했으나, PL 시트는 영업 관리용으로 12월까지 미래 예상치가 미리 입력된 상태. RAW(/api/rowdata-json) 의 db[yr].total.Sales 가 실제 회계 마감 완료 월. 시그니처 s_currentYearMonth(plRaw, rdRaw) 변경 — 1순위 RAW Sales 마지막 양수 월 / 2순위 PL fallback / 3순위 캘린더 -1. 현재 데이터 기준 4월 자동 채택 정상화.
  • FEAT연도/월 선택 dropdown 추가 — 헤더 우측 마감 뱃지 옆에 <select id="s-year-select"> (2025/2026) + <select id="s-month-select"> (1~12월) + ↻ 자동 버튼 신설. 사용자 임의 월 선택 시 _ymUserOverride=true + refreshAll() 호출 → Layer 1·2·3·4 모두 그 시점 기준 재계산 (4중 비교 표 / 4-line 차트 / 사업장 카드 / 히트맵 / 4분면 / 거래처 dropdown / 드릴 / Forecast / Action Queue). 마감 뱃지·last-close·표 헤더 라벨도 ("📅 분석 시점 26.04" / "시점: 2026.04") 자동 갱신. ↻ 자동 클릭 시 RAW 마감 월로 복귀.
  • FEATrefreshAll() 함수 신설 — 사용자 월 변경 시 전체 UI 재렌더 단일 진입점. PL+RAW+Pipeline 캐시(_plRaw/_rdRaw/_pipe) 그대로 사용하되 _ym 기준만 변경 → 빠른 응답 (API 재요청 없음).
  • FEAT12개월 추이 차트 가시성 강화 — 사용자 보고: 가로/세로 작고 그래프 흐림. CSS: .s-result-board grid-template-columns 1fr 1.4fr → 1fr 1.7fr + canvas height 240px → 360px (가로 +21%, 세로 +50%). 4-line 색 일관성 개편 — 노랑 = 계획, 보라 = 실적, 굵음 = 당년, 옅음 = 전년:
    • 당년 계획: 노랑 점선 굵기 2.5px (기존 2) + 포인트 3px
    • 당년 실적: 보라 실선 굵기 4px (기존 2.8) + 포인트 5px + 흰 테두리
    • 전년 계획: 옅은 노랑 점선 (rgba 0.45, 기존 회색)
    • 전년 실적: 옅은 보라 실선 (rgba 0.6, 기존 회색)
  • OPS변경 파일 3개 — src/js/57-view-sales.js (s_currentYearMonth 시그니처 + onYmPickerChange + refreshAll +80줄, drawMain4line 색 패치, 헤더 마크업에 ym-picker 추가) · src/css/57-view-sales.css (ym-picker 스타일 +14줄, result-board grid + canvas height) · src/부산본부_대시보드.html (dev-row V22.4). server.js 변경 0. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.3.
2026-05-18
V22.3 매출 분석 — Phase 2-C/D 완료 (Layer 3 Pipeline + Forecast + Layer 4 워닝 룰 엔진 + Action Queue TOP 8 자동)
  • FEATSales Opp Pipeline 결선 (Layer 3) — V22.0 더미 funnel → /api/sales-pipeline (V20.4 구축, server.js:2038) 응답 활용. 4 결과 범주 매핑: 진행 중(inprogress) / 성약(won) / 지연(delayed) / 핫 리드(isHot). 각 stage 카운트 + 합산 예상매출(억) 자동. funnel 헤더 sub 에 주차본 파일명 자동 표기 (예: "23건 · Sales Opp Pipeline Mgt__4월 4주차").
  • FEATQ2 Forecast 자동 산출 — updateForecast(pipe, plRaw, ym): 직전 3M 실적 평균 × 2달 base + Pipeline 가중 (inprogress × 50% + delayed × 20%). PL 계획 값 = 다음 1~2달 ps 합계 자동. 신뢰도 = 50 + (inprogress 건수 × 2) (최대 85%).
  • FEATClosing 알람 동적 — updateClosingAlarm(pipe): isHot 리드 최우선 + inprogress 큰 순 TOP 3 자동 표시. 거래처명 + (사업장) + 🔥 HOT/진행 중 + 예상매출 억. 없으면 "진행 중/핫 리드 없음" 안내.
  • FEATAction Queue 워닝 룰 엔진 (Layer 4) — buildActionTableFromRaw(clients, pipe): RAW 거래처 카테고리 분류 + Pipeline 핫 리드 통합 우선순위 큐. 시안 사양서 §7-1 기반 룰:
    • P1 — 쇠퇴 + 무거래 (긴급 접촉, 5일) / 쇠퇴 + 12M ≥5억 (긴급 방문, 7일)
    • P2 — 쇠퇴 작은 (단가 재검토, 14일) / 부진 (방문 지정, 14일) / 신규 ≥1억 (추가 영업, 14일) / Pipeline 핫 리드 (계약 클로징, 14일)
    • P3 — 신규 작은 (환영 미팅, 30일) / 스타 ≥1억 (감사 인사, 30일)
    정렬: 우선순위 → 매출 큰 순. TOP 8 표시. 기한 ≤7일은 빨강 강조.
  • FEAT영업담당 자동 배정 — _teamForClient(name): 거래처명 hash → 김부장/박과장/이차장/최대리 풀에서 라운드 로빈. V22.4에서 sales-notes API 영속화 시 거래처별 실담당으로 교체 예정. 영업 메모는 현재 "메모 미입력 — 영업담당 입력 필요" 안내 (Phase 4 영속 별도 사이클).
  • FEAT3 API 병렬 fetch — loadSalesData 가 Promise.all([App.data.pl.ensureLoaded(), apiFetch('/api/rowdata-json'), apiFetch('/api/sales-pipeline')]) 단일 wave. Pipeline 미수신 시에도 PL+RAW 만으로 Layer 1·2·Forecast(PL 기반) 정상 동작.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (Pipeline 헬퍼 4종 + Action Queue 빌더 + 3-fetch Promise.all +220줄) · src/부산본부_대시보드.html (dev-row V22.3 + V22.2 latest 강등). server.js 변경 0. Phase 2 전체 완료 — Layer 1(PL) + Layer 2(RAW) + Layer 3(Pipeline+Forecast) + Layer 4(워닝 룰+Action Queue) 4 Layer 모두 실데이터. V22.4 는 sales-notes 영속화·SSE 자동 갱신 등 보강 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.2.
2026-05-18
V22.2 매출 분석 — Phase 2-B RAW 거래처 결선 (Layer 2 히트맵·4분면·드릴·dropdown 전체 + 신규/이탈 균형)
  • FEATRAW-비용분석 실데이터 결선 — V22.0 시안 16개 더미 → /api/rowdata-json 응답 (server.js:2371 buildRowdataJson) db[yr][branch].Details[m].Sales[accName][...].Client 4-tuple 거래처 데이터 활용. extractClientsFromRaw(rd, ym) 가 24M(2025+2026) 시계열 + per-site 분할 + YTD/YoY/avg3/slope12·3 메트릭 산출. 거래처 약 100-150개사 자동 인식.
  • FEAT거래처 4 카테고리 자동 분류 — classifyClient(c) 룰: 신규 (전년 동기 0 AND 당년 ≥0.01억) / 쇠퇴 (YoY ≤-50% AND 전년 1억+) / 부진 (YoY ≤-50% AND 전년 1억 미만) / 스타 (YoY ≥+10% AND 당년 0.5억+) / 통상 (위 4 분류 안 됨). 4분면 산점도 라벨 + dropdown optgroup 자동 그룹핑.
  • FEAT거래처 dropdown 전체 확장 — V22.0 하드코드 16개 → RAW 거래처 전체 (매출 0.1억 이상). 5 그룹 optgroup (💀쇠퇴 / 📉부진 / ⭐스타 / 🆕신규 / 📦통상), 각 그룹 내 YTD 큰 순 정렬, 각 그룹 라벨에 거래처 수 표시. 기본 선택: 쇠퇴 1순위 (가장 위험한 거래처) — 영업본부장 첫 진입 시 즉시 행동 트리거.
  • FEAT4분면 산점도 동적 갱신 — updateQuadFromRaw(clients): x=YTD 매출(log scale), y=YoY %, r=√매출×4 (큰 거래처 큰 점), 사업장 색. 4 카테고리 라벨도 동적 갱신 — 각 카테고리 상위 3개 거래처명 표시.
  • FEAT드릴다운 24M 시계열 + 자동 시그널 — 드롭다운 선택 시 updateDrillFromRaw(name): 24M(2025-2026) 시계열 차트 + 7개 메트릭 + 자동 시그널 칩 (📉당월대비, 🔄회복, ⚠12M+3M↓ slope, 🆕신규 진입, 🛑무거래) 자동 생성.
  • FEAT히트맵 36셀 동적 (PL 달성률) — updateHeatmap(plRaw, ym): DWLG/DWLT/BNCC × 12M sliding 윈도우 각 셀에 (PL 실적 ÷ PL 계획) × 100. 색상 자동 (l1 <70% / l2 70-85 / l3 85-95 / l4 95-105 / l5 >105 / l-warn <75% 강조). 데이터 0 (미마감) 시 future 줄무늬. 셀 hover 시 툴팁에 정확한 억 단위.
  • FEAT신규/이탈 균형 카드 RAW 산출 — 당년 동기 매출 0 → 진입 시 신규 +억, 전년 동기 매출 있고 당년 0 → 이탈 -억. 순 신규 매출 합계 자동 표시 (영업본부장 거래처 유출입 즉시 인지).
  • FEAT사업장 카드 거래처 수 자동 갱신 — RAW 결선 후 4 카드 footer 의 "X개 거래처" 숫자 자동 카운트 (사업장별 dominantSite 기준).
  • OPS변경 파일 2개 — src/js/57-view-sales.js (RAW 헬퍼 8종 + Promise.all 2 fetch + 5 update 함수 +280줄) · src/부산본부_대시보드.html (dev-row V22.2 + V22.1 latest 강등). server.js 변경 0. Phase 2-B 완료 — Layer 1(PL) + Layer 2(RAW) 모두 실데이터. Layer 3 Pipeline 결선 + Layer 4 워닝 룰 엔진 V22.3 별도 사이클. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.1.
2026-05-18
V22.1 매출 분석 — Phase 2-A PL 실데이터 결선 (Layer 1 4중 비교 + 4-line 차트 + 사업장 카드)
  • FEAT손익현황 PL 실데이터 결선 — V22.0 시안값 더미 → App.data.pl.RAW (50-view-pl.js IIFE 노출, V20.1 구축) 활용 동적 산출. App.data.pl.ensureLoaded() Promise 체인 후 _plRaw 캐시 → 4 사업장 (ALL/DWLG/DWLT/BNCC) × 단월·YTD·12M = 12셀 매출 4중 비교 표 자동 계산.
  • FEAT회계마감 월 자동 fallback — s_currentYearMonth(plRaw) 가 PL.RAW 의 마지막 양수 as_ 월을 회계마감 기준으로 자동 채택. 5월 초 (당월 마감 진행 중) 진입 시 4월 데이터 자동 표시. 회계마감 뱃지 (📌 회계마감 YY.MM) + last-close + 표 헤더 (당월 N월) 모두 동기 갱신.
  • FEAT4-line 12M 차트 PL 산출 — 당년 계획 (PL ps) / 당년 실적 (PL as_) / 전년 계획 / 전년 실적 4 시리즈 모두 PL 시트 직접 추출. compute12MSeries(plRaw, site, ym) 가 sliding 12M 윈도우 라벨 + 사업장별 합산 + 전년 동월 매핑.
  • FEAT사업장 카드 동적 갱신 — 4 카드 (통합/DWLG/DWLT/BNCC) YTD/계획대비%/YoY%/전년 YTD 모두 PL 실데이터. sparkline 12M 실적도 PL as_ 시계열로 재그림. updateSiteCards(plRaw, ym) 신규.
  • FEAT인사이트 자동 생성 — computeL1Insight 가 YTD 달성률·전년 달성률 차이·YoY 트렌드·당월 단월 달성률을 사업장 이름과 결합해 자연어 인사이트 생성. 시안값 하드코드 폴백 유지 (PL 로드 실패 시).
  • FEAT사업장 매핑 — V20.x 패턴 그대로 재사용 — PL_TEAM_TO_SITE = {DWLG:DWLG, 부산터미널:DWLT, CFS(운영):BNCC}. normSite (50-view-pl.js:254) 결과와 정합.
  • FEAT← 메인메뉴 버튼 추가 — 사용자 보고: V22.0 매출 분석 헤더에 메인메뉴 복귀 버튼 누락. 다른 view (손익/일일/인원/사업장/유류비/비용분석/현금흐름) 와 동일 패턴 <button class="back-btn" onclick="showView('view-menu')">← 메인메뉴</button> 헤더 우측 끝에 추가. 글로벌 .back-btn 클래스 활용 — 별도 CSS 추가 0.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (헬퍼 7종 + loadSalesData + updateSiteCards + update4line 패치 +160줄) · src/부산본부_대시보드.html (dev-row V22.1 + V22.0.1 latest 강등). server.js 변경 0. Phase 2-A 한정 — Layer 2 (히트맵·4분면·드릴 RAW) 와 Layer 3 (Pipeline) 와 Layer 4 (워닝 룰) 는 V22.0 더미 그대로 유지, V22.2/V22.3 사이클에서 결선 예정. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v22.0.1.
2026-05-18
V22.0.1 매출 분석 — hotfix: 사이드바 매출 분석 클릭 시 `enterSales is not defined` 에러 + showView 호출 누락
  • FIX글로벌 window.enterSales 노출 누락 — 사용자 콘솔 보고 (V22.0 staging 검증): Uncaught ReferenceError: enterSales is not defined at HTMLDivElement.onclick. 원인: 사이드바 <div onclick="enterSales()">매출 분석</div> 는 글로벌 함수 호출 패턴인데, V22.0 IIFE 가 window.App.view.enterSales 만 노출. 다른 view (51-daily/52-personnel/54-rowdata/55-fuel/56-cashflow/60-facility) 모두 window.enterX = enterX 패턴으로 글로벌 노출. V22.0 한 곳만 누락. fix: enterSales 함수 분리 + window.enterSales = enterSales 추가 + window.App.view.enterSales = enterSales 양쪽 동일 함수 참조.
  • FIXshowView('view-sales') 호출 누락 — V22.0 enterSales 가 view 전환 함수 호출 안 함 → 다른 view 가 보이는 채로 markup 만 주입. fix: enterSales 첫 줄에 if (typeof showView === 'function') showView('view-sales') 추가 — 51-view-daily.js line 89 동일 패턴.
  • OPS변경 파일 2개 — src/js/57-view-sales.js (진입점 함수 분리 + 글로벌 노출 +6줄) · src/부산본부_대시보드.html (dev-row V22.0.1 + V22.0 latest 강등). server.js 변경 0.
2026-05-18
V22.0 매출 분석 — 회계마감 결과 분석 콕핏 정식 포팅 (Phase 1 — UI 전면 교체, 데이터는 시안값 유지)
  • FEAT4-Layer Decision Cockpit 신설 — V20.x 5단 Tier 구조 → Layer 1 (회계마감 결과 보드) + Layer 2 (거래처 진단) + Layer 3 (Pipeline) + Layer 4 (Action Queue) 4단 구조. 시안: mockup_매출분석_v3.html. 데이터 소스 명시 (PL/RAW/PIPE 3종) 헤더 버튼 + 모달 팝업으로 압축.
  • FEATLayer 1 — PL 시트 4중 비교 표 — 당년 계획 / 당년 실적 / 전년 계획 / 전년 실적 + YoY 4행 × 당월·YTD·12M 3열 매트릭스. 사업장 탭 (통합/DWLG/DWLT/BNCC) 클릭 시 표·인사이트·12M 4-line 차트 모두 즉시 갱신. 4-line: 당년 계획·실적(굵게) + 전년 계획·실적(옅게) 4 라인 한 차트에서 비교.
  • FEATLayer 2 — 사업장×월 히트맵 + 거래처 4분면 + 거래처 선택 드릴다운 — DWLG/DWLT/BNCC × 12M 36셀 달성률 히트맵 (계절성·이상치 한눈에). 거래처 4분면 산점도 (매출 규모 log × YoY %, bubble=규모). 거래처 dropdown (16개, 4그룹: 쇠퇴/부진/스타/신규) 으로 임의 거래처 24M 시계열 + 7개 상세 항목 선택 가능.
  • FEATLayer 3·4 — Pipeline Funnel + Q2 Forecast + Action Queue TOP 8 — 신규 잠재 4단계 (계약임박 11억 HOT) + 신규/이탈 균형 + Q2 예측 75.2±8.4억 + closing 알람 3건. 액션 큐 P1/P2/P3 우선순위 표 (자동 시그널 + 영업 메모 + 담당자 + 기한 + ✓/✎/📜 조작).
  • FEAT기준 토글 단순화 + 일일매출 제외 — 회계기준(RAW)/영업기준(PL) 2종 (일일매출 별도 모듈로 제외). 보기 단위: 월별/분기별/연도별 3종 + 연도 선택: 2025/2026/YoY 비교.
  • OPS변경 파일 3개 — src/css/57-view-sales.css (215→363줄, #view-sales 스코프 한정 + 신규 토큰 --s-src-pl/raw/pipe) · src/js/57-view-sales.js (3006→약 690줄, IIFE + App.view.enterSales 진입점 + 동적 markup 주입 + Chart.js 헬퍼) · src/부산본부_대시보드.html (dev-row V22.0 + V20.8 latest 강등). server.js 변경 0. Phase 1 한정 — 데이터는 시안값 더미. Phase 2에서 PL/RAW/Pipeline 3 API 병렬 결선 예정. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v21.0.13.
2026-05-18
V20.8 매출 분석 — 무결성 배너에 회계결산조정 보정 (PL vs RAW 자연 갭 false positive 종결)
  • FIX4월 마감 진입 시 무결성 배너 5.09% false positive — 정합성 진단 (2026-05-18, PL 26.04 24.58억 vs RAW 23.33억): 차이 1.25억 = DWLT 4월 기타거래처(결산) 단일 행 -1.22억 단독 출처. PL 엑셀(영업/매출관리 기준)은 결산조정 미반영, RAW-비용분석(회계전표 기준)은 결산행 포함 → 매월 마감 시 자연 갭 발생. KPI/콤보/사업장 게이지는 이미 RAW 기준이라 정상 표시되나, s_checkIntegrity 가 두 합계를 직접 비교해 ±5% 룰 false positive.
  • FEAT결산조정 보정 로직 신설 — s_checkIntegrity 가 RAW db[yy].total.Details[m].Sales 순회 시 Client.indexOf('기타거래처') !== -1 || Client.indexOf('결산') !== -1 행의 Amount 누적 (rdAdjSum). PL 합계에 동등 반영 (plAdjusted = plSum + rdAdjSum) 후 RAW 합계와 비교 → 회계마감 기준으로 통일. 음수/양수 결산행 모두 동일 처리. 배너 메시지에 결산조정 금액 보조 표시 (반영 후 PL 값 명시).
  • TEST검증 (prod API 직접 비교, 2026-05-18) — PL 1~4월 누계 96.34억 vs RAW 95.09억 (결산조정 -1.20억 포함). 보정 후 96.34 + (-1.20) = 95.14억 ≈ RAW 95.09억, 차이 0.05% (반올림 + 미세거래). 배너 OFF 정상. 사업장별: DWLG/BNCC 0.00% (결산행 0건), DWLT 결산조정 -1.20억 보정 후 0.05억 차이. 2025년 12개월 전체는 결산조정 0원 (기존 0.06% 그대로 유지, 회귀 0).
  • OPS변경 파일 1개 — src/js/57-view-sales.js (s_checkIntegrity +25줄 / -7줄, 결산조정 누적 루프 + plAdjusted 계산 + 배너 메시지 조건부 보조 텍스트). server.js 변경 0 → --include-server-js 불필요. 데이터 산식 변경 0. KPI/콤보/게이지/Tier3·4·5/모달 5종 모두 영향 0. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v21.0.13.
2026-05-16
V21.0.13 유류비 점검 — KPI ① 매출액 대비 비중 + KPI ④ 전년 동기 대비 YTD 활성 (B-2 placeholder 종결)
  • FEATKPI ① 매출액 대비 유류비 비중 활성 — 사용자 보고 (2026-05-16): "매출액대비율 데이터를 못끌고 온다". 진단: V19.0 부터 placeholder ("B-2 활성 후 매출 라인 매핑 필요") 표시. 픽스: loadFuelData 에서 /api/rowdata-json 추가 fetch (병렬). 매출 = db[year][branch].Sales[m]. 사업장 매핑: {'':'total', DWLG:'dwlglobal', DWLT:'terminal', BNCC:'cfs'}. 마지막 매출>0 인 월(=회계 마감 월) 기준 (유류비/매출×100). sparkline = 12M 비중 추이. 사업장 탭 변경 시 자동 전환 (V21.0.12 monthlyScope 활용). server.js 변경 0.
  • FEATKPI ④ 전년 동기 대비 YTD 누적 증감 활성 — 사용자 보고 (동일): "전년동기 대비 데이터를 못끌고 온다". 진단: V19.0 부터 placeholder ("server.js trends 윈도우 12→24개월 필요"). 픽스 (server.js 변경 회피 — OOM 위험 차단): loadFuelData 에서 /api/fuel-info?year=2025&month=4 추가 fetch (전년 동월 기준 12M = 24.05~25.04). 현재 12M (25.05~26.04) 와 결합해 24개월 분 시계열 조립. YTD = 1월~당월(=마지막 가용 월) 누적. yoyDelta = 26 YTD - 25 YTD. 색상 룰: 유류비 증가 = 적신호. sparkline = 24M YTD 누적 (연도 reset). 사업장 탭도 trendsBySitePrev[selSite] 분기.
  • TEST검증 시나리오 (전체 사업장 기준) — KPI ①: 26.03 마감 시점이면 (24.47억 매출 vs 비용) 로 비중 계산. 26.04 매출 미마감이라 03 까지 가용. KPI ④: 25 YTD 1~4월 합 vs 26 YTD 1~4월 합 → 차이값 + %. sparkline 으로 추세 시각화. RAW 비용분석 워밍 중 (503) 이면 두 KPI 모두 안전망 "워밍 중" 표시.
  • OPS변경 파일 1개 — src/js/55-view-fuel.js (loadFuelData 병렬 3-fetch ~25줄 · renderHeroKpi KPI ①/④ 계산 ~80줄 · sparkline ratioSeries + YTD running). server.js 변경 0. 데이터 산식 변경 0. prod 직접 patch. 롤백: /app/_backup_pre_v21_0_13_*.html.
2026-05-15
V21.0.12 유류비 점검 — 사업장 필터 적용 시 12개월 추이/KPI/단가 차트가 해당 사업장 시계열로 자동 전환
  • FEAT헤더 사업장 탭 (전체/DWLG/DWLT/BNCC) 클릭 시 사업장별 시계열 활성 — 사용자 요청 (2026-05-15): "12개월 추이의 그래프가 사업장을 클릭시 각 사업장별로 비용그래프로 적용해줘". 진단: 기존 computeAll 의 monthly 가 항상 trends (3사업장 합산) 기반 → 사업장 탭 변경해도 12개월 추이/KPI/단가 차트가 안 바뀜. 사업장 카드 4장(본부합산+3사) 만 분리 표시. 픽스: computeAll 에 trendsBySite[selSite] 분기 추가. selSite === '' 면 합산, 사업장 선택 시 그 사업장 12개월 시계열만 사용. FUEL.computed.monthlyScope 메타로 차트 제목 동적 변경 (예: "12개월 추이 [DWLT] — 비용·단가").
  • FEAT자동 적용 범위 — monthly 의존 모든 화면 — KPI 5장 (당월·전월·MoM·12M 평균·z-score), 24개월 콤보 비용/단가 라인, 단가 spread 'sum' 모드 라인, 워닝 모달 12개월 추이 차트, 단가 z-score 검출 (이상감지 정확도 사업장별로 향상). 사업장 카드 4장 + 도넛은 항상 본부 합산 + 3사 표시 유지 (사업장 비교 가시화 목적). spread 'bySite' 모드도 3사 라인 유지 (사용자 비교 의도 보존).
  • TEST검증 시나리오 — 사용자가 헤더 [DWLT] 클릭: KPI ① 당월 24.46M / ② 전월 (DWLT 26.03 cost) / Z-score (DWLT 단가 12M 분포 기준) / 12개월 콤보 (DWLT만) / spread 'sum' (DWLT 단일 라인). [전체] 다시 클릭 시 본부 합산으로 복귀.
  • OPS변경 파일 1개 — src/js/55-view-fuel.js (computeAll 분기 +15줄 · renderCombo24 제목 +2줄 · renderSpreadChart sum 모드 라벨 +3줄). server.js 변경 0. 데이터 산식 변경 0. prod 직접 patch. 롤백: /app/_backup_pre_v21_0_12_*.html.
2026-05-15
V21.0.11 유류비 점검 — 워닝 모달 차트도 단가 표시 포맷 통일 (1,839.494 → 1,839 원/L)
  • FIX워닝 모달 12개월 추이 차트 + 레코드 모달 일자별 막대 차트 포맷 통일 — 사용자 보고 (V21.0.10 배포 직후 스크린샷): 워닝 모달의 12개월 추이 차트 tooltip 에서 1,839.494 식 소수점 표시 잔존. V21.0.10 은 spread/콤보만 fix, 모달 차트는 누락. 픽스: renderTrendWarnModal + renderRecordWarnModal 의 tooltip / Y축 ticks 에 Math.round(v).toLocaleString('ko-KR') 적용. metric 별 단위 분기 — unitPrice = "원/L", amount = "L", cost = fmtNumK(만/억 단축). 모든 단가 표시 일관성 확보 (KPI 카드 / 사업장 카드 / spread / 콤보 / 워닝 모달).
  • OPS변경 파일 1개 — src/js/55-view-fuel.js (모달 2종 tooltip + Y축 callback, ~20줄). 데이터 산식 변경 0. server.js 변경 0. prod 직접 patch. 롤백: /app/_backup_pre_v21_0_11_*.html.
2026-05-15
V21.0.10 유류비 점검 — 차트 단가 표시 포맷 hotfix (1836.785 → 1,837 원/L)
  • FIXspread 차트 + 24개월 콤보 단가 라인 tooltip / Y축 포맷 — 사용자 보고 (V21.0.9 배포 직후 스크린샷): BNCC 26.03 spread 차트 hover 시 1,836.785 표시 → 사용자가 영문권 천단위 콤마로 오인해 "100만원 넘는다"고 혼동. 실제 값은 cost(8,713,707) / amount(4,744L) = 1,836.785원/L 정상. 픽스: tooltip.callbacks.label 추가 — Math.round(v).toLocaleString('ko-KR') 적용해 정수 반올림 + 한국 locale 천단위 콤마 (예: 1,837 원/L). Y축 ticks 도 동일 callback. spread 차트 + 24개월 콤보 양쪽 동시 적용. 콤보 차트의 비용 라인은 기존 fmtNumK 유지, 단가 라인만 신규 포맷.
  • OPS변경 파일 1개 — src/js/55-view-fuel.js (tooltip callbacks + Y축 ticks callback, ~15줄). 데이터 산식 변경 0. server.js 변경 0. prod 직접 patch (V21.0.9 hotfix 와 동일 경로). 롤백: prod 백업 /app/_backup_pre_v21_0_10_*.html 복원.
2026-05-15
V21.0.9 유류비 점검 — 사업장별 12개월 시계열 + 데이터 가용월 fallback + VAT 라벨 명시
  • FIXDWLT 4월 파일 미작성 → 사업장 카드/도넛/장비 분포에서 통째로 누락 — 사용자 보고 (2026-05-15): 유류비 점검 숫자가 실제 데이터와 너무 다름. 진단: DWLT 의 2026/4월 xlsx 파일이 NAS 에 미작성 (1·2·3월만 존재) → findLatestFuelFile(site, curDate) 가 fallback 없이 null 반환 → data 배열에 DWLT 0건 → 사업장 4 카드 / 도넛 / 산점도 / TOP 이상치 모든 화면에서 DWLT 사라짐. trends 의 26.04 도 DWLG+BNCC 합산만 (L=15,637 vs 다른 달 평균 22,500, -31% cliff). 픽스 (V20.7 매출 분석 패턴 차용): /api/fuel-info 핸들러에 사업장별 backward 6개월 fallback 추가. data 배열은 사업장별 가장 최신 가용월로 자동 채움 (DWLT 3월, DWLG/BNCC 4월). 신규 메타 필드 dataMonthBySite 응답 — 어느 월 데이터인지, fallback 여부 표시. 사용자 개입 0, DWLT 4월 파일 업로드 후 SSE/새로고침 자동 4월 전환.
  • FEATtrendsBySite 응답 신설 — SSOT 09 이슈 #1 해소 — 기존 trends 는 3사업장 합산 12개월만 → 단가 spread 'bySite' 토글이 사업장별 시계열을 받지 못해 합산 라인 + 당월 단일 점만 표시 (B-2 한계). V21.0.9: trendsBySite: {DWLG:[{month,amount,cost,hasFile}], DWLT:[…], BNCC:[…]} 응답 추가. 클라이언트 renderSpreadChart 의 'bySite' 모드가 사업장별 12개월 단가 라인 3개(DWLG 청록·DWLT 분홍·BNCC 황색) 정상 동작. 파일 부재 월(hasFile=false)은 라인 끊김으로 명시.
  • FEATVAT 포함 라벨 명시 — 단가 1,984~1,998원/L 산식 검증: 합계금액(부가세 포함) / 주유량 → VAT 포함 단가. 시장 경유가(부가세 별도) 비교 시 사용자 혼동 우려. 24개월 콤보 차트 Y축 / 사업장 카드 평균단가 / spread 차트 Y축 모두 "원/L (VAT포함)" 명시. 응답 priceIncludesVat: true 메타 추가.
  • FEAT사업장 카드 월 배지 — 사업장 카드 헤더에 월 배지 추가. 정상 가용월은 녹색(ok), fallback 월(요청 월 미작성)은 노란색(fb) + 툴팁. 사용자가 어느 사업장이 어느 월 데이터인지 즉시 인지.
  • TESTxlsx 직접 검증 — DWLG/DWLT/BNCC 2026.04 (or 3월) xlsx 3건 다운로드 후 5열 블록 구조·단가 산식·집계 컬럼 차단 검증 완료. 단가×주유량=합계금액 (예: 242L×1,984원=480,128원). parseFuelSheet 자체는 정확, 결함은 fallback 부재만.
  • OPS변경 파일 3개 (prod hotfix — deploy.py 우회, V21.0.6 base 위에 유류비만 patch) — src/server.js (/api/fuel-info 핸들러 재작성 +60줄) · src/js/55-view-fuel.js (헤더 메타 + spread bySite 사업장별 12M + 카드 월 배지 + VAT 라벨) · src/css/55-view-fuel.css (월 배지 스타일 +4줄). 롤백 1명령: prod NAS 백업 /app/_backup_auto_prod_20260515_060748.html 로 복원 + server.js 동일.
2026-05-15
V21.0.8 인프라 — HTTPS 단일 진입 mixed-content 종결 + preflight 배너 제거
  • FIXdoLogin "Load failed" 픽스 — fetch 프로토콜 동적 분기 — 사용자 보고 (2026-05-15): HTTPS 도메인 진입 후 비밀번호 입력 시 "Load failed". 진단: 41-auth.js:20 의 fetch('http://' + SERVER_HOST + '/api/fuel-info') 가 HTTPS 페이지에서 실행되어 mixed-content 차단 (Safari "Load failed"). 동일 패턴 20-api.js apiFetch + connectSSE 2곳에도 잔존. 픽스: App.common.SERVER_PROTO 신설 — window.location.protocol === 'http:' 면 'http:', 그 외(https://, file://) 면 'https:'. 3개 fetch/EventSource 사이트 모두 SERVER_PROTO + '//' + SERVER_HOST 동적 빌드. 83e39e6 커밋 본문 "미해결 / 다음 사이클 V21.0.7+" 노트 종결.
  • FIXSERVER_HOST file:// 폴백 HTTPS 도메인으로 갱신 — 00-app-init.js: '1.254.6.240:3001' → 'dashboard.busan-hq-dashboard.synology.me'. DSM reverse-proxy HTTPS 443 정책에 정합. CloudStorage 동기화된 임원용 정적 HTML(file://) 도 동일 SSOT 로 동작.
  • FEATpreflight 배너 제거 — IIFE no-op 스텁화 — 사용자 결정 (2026-05-15): "주소가 바뀌었기 때문에 NAS 대시보드 열기 과정 생략". 기존 V7 IIFE 의 오렌지 배너 + NAS_URL = 'http://1.254.6.240:3001' 옛 IP 폴백 모두 제거. 새 preflight.js: 11줄 IIFE 가 file:// 진입 시 __IS_LOCAL_PREVIEW__ 플래그만 설정 + console.info 안내. DOM 변경 0. 정식 진입 경로는 https://dashboard.busan-hq-dashboard.synology.me 북마크.
  • FIXsmoke.py check #15 — banner string 요구 제거 — V21.0.8 배너 제거 정책 반영. ("local-preview-banner" in html AND "__IS_LOCAL_PREVIEW__" in html) → ("__IS_LOCAL_PREVIEW__" in html) 단독. docstring §20 도 동기화.
  • TEST검증 — node --check 4 모듈(preflight + 00-app-init + 20-api + 41-auth) OK. 빌드 산출물 hardcoded 'http://' 0건 (이전: 3건). 1.254.6.240 잔존 0건 (이전: 3건, src/ 영역). preflight.js 3,451 B → 957 B (-72%).
  • OPS변경 파일 5개 — src/js/00-app-init.js (SERVER_PROTO 신설 +1줄 · SERVER_HOST 폴백 도메인 갱신 · 주석 동기화) · src/js/20-api.js (apiFetch/connectSSE URL 빌더 2곳) · src/js/41-auth.js (doLogin fetch 1곳) · src/preflight.js (53줄 → 11줄 재작성) · scripts/smoke.py (check #15 + docstring). server.js 변경 0 → --include-server-js 불필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v19.7.2.
2026-05-16
V19.8.0 사업장 정보 — 5개 override 섹션 폐기 (주차 · IT 인프라 · 에너지 · 타임카드 · 회의실), 보안 1개만 유지
  • FEAT사이드바 6 → 5 항목 (override 섹션 6 → 1) — 사용자 결정 (2026-05-16): "사업장현황의 섹션중 다음은 삭제 정리하자 — 주차, it인프라, 에너지, 타임카드, 회의실". 진단: V19.1 의 6 override 섹션은 NAS JSON 기반 사용자 직접 입력 영역. 4개월 운영 결과 NAS overrides JSON 의 5개 섹션 모두 빈 데이터 (fields=0, customRows=0) 로 실 사용 0건. 정합성 영향 없이 제거 가능.
  • FEAT클라이언트 매핑 5종 일괄 축소 — FAC_OVERRIDE_SCHEMA 6 → 1 key · FAC_OVERRIDE_SECTIONS 6 → 1 entry · FAC_SECTION_SUBFOLDER 6 → 1 · FAC_SECTION_IMG_KEYWORDS 6 → 1 · FAC_EXCEL_SECTION_MAP 6 → 1 · V16_SECTION_LABEL 6 → 1. 매핑 일관성 + 코드 라인 -50줄.
  • FEATserver.js OVERRIDES_SECTIONS 축소 — ['security','parking','it','energy','timecard','meeting'] → ['security']. POST/revert 라우트의 section 검증이 자동으로 5개 차단 (return 400). _emptyOverrideSkeleton 도 신규 사이트 추가 시 security 1개만 자동 생성.
  • FEATHTML 사이드바 5 sitem 제거 — line 1982-1986 의 parking/it/energy/timecard/meeting 5 v16-sitem 일괄 제거. 보안 1개만 유지 (사용자가 V19.2.2 부터 엑셀 ⑤보안 7항목 자동 표시 + NAS JSON 자유 row 영역 사용 가능).
  • FEATNAS JSON 데이터 처리 — 기존 /app/data/facility_overrides.json 의 5개 section 키는 그대로 보존 (deploy.py 자동 백업 1회). 클라이언트가 더 이상 접근 안 함 + server section 검증이 차단 → 사실상 dead data, 향후 사용자 결정으로 수동 정리 가능. 운영 영향 0.
  • TEST검증 — node --check src/js/60-view-facility.js src/server.js 모두 OK. 사이드바 9 → 4 항목 (기본정보/장비/소방/편의시설 + 보안 = 5 항목). 빌드 산출물: 60-view-facility.js ~1,815줄 (V19.7.4 1872 → -50줄 = 5 섹션 매핑 축소). server.js ~2,562줄 (V19.7.4 2561 → +1줄 = 주석). HTML 사이드바 -5줄 + dev-row V19.8.0 신규 + V19.7.4 latest 강등.
  • OPS변경 파일 3개 — src/js/60-view-facility.js (5 매핑 축소 -50줄) · src/server.js (OVERRIDES_SECTIONS +1줄 주석) · src/부산본부_대시보드.html (사이드바 -5줄 + dev-row V19.8.0). server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v19.7.4.
2026-05-16
V19.7.4 사업장 정보 — 깊이 검토 후속 Critical 6건 일괄 hotfix (SSE dirty 보호 · amen 탭 보존 · ESC 누수 · 빈 row UI · placeholder 정확화 · 헤더 검증)
  • FIX편의시설 탭 SSE 재렌더 시 caf 강제 reset 차단 — 진단: f_renderSection 의 amen 분기가 v16_setAmenTab('caf') 무조건 호출 → 다른 사용자 편집으로 SSE overrides-changed 도착 시 사용자가 보고 있던 gym/mtg 탭이 caf 으로 점프 (시각 노이즈). 픽스: 모듈 변수 FAC_AMEN_CURRENT_TAB 신규, v16_setAmenTab 호출 시 갱신, 재렌더 시 v16_setAmenTab(FAC_AMEN_CURRENT_TAB || 'caf') 로 직전 탭 보존.
  • FIX편집 모드 중 SSE 도착 시 dirty 입력 silent 손실 차단 — 진단: 다른 사용자가 저장 → 즉시 v16_loadOverrides + f_renderSection → 입력 input DOM 교체 → dirty 큐는 남지만 화면에서 사라짐 → 저장 시 If-Match 409 → 강제 리로드 → 사용자 입력 소실. 픽스: v16_connectSSE overrides-changed 분기에 FAC_EDIT_MODE && _facHasDirty() 가드 추가 → 자동 재렌더 차단, 토스트만 노출 ("⚠ 다른 사용자가 편집했습니다. 저장 시 충돌 가능 — 새로고침 권장."). 저장 시 etag 충돌이면 기존 alert 흐름이 안전 처리.
  • FIX변경이력 모달 ESC 리스너 누수 — 진단: v16_openHistoryModal 의 onEsc 가 ESC 키 자체로만 자기 자신을 remove. ✕ 버튼 / backdrop 클릭으로 닫으면 리스너 잔존 → 모달 열 때마다 누적. 픽스: 모듈 변수 _v16HistEscHandler 에 핸들러 저장, v16_closeHistoryModal 에서 닫힘 경로(ESC/✕/backdrop) 어느 쪽이든 일괄 removeEventListener.
  • FIX빈 자유 row prune 후 UI 동기화 + 무제한 누적 차단 — V19.6 prune 은 dirty 큐만 정리하고 화면은 그대로 → 사용자가 "저장 안 됐다" 인식. 또 + 항목 직접 추가 클릭 시 직전 빈 row 가 있어도 매번 새 빈 row 추가 → dirty 카운터 무제한 증가. 픽스: (a) prune 후 f_renderSection 호출로 빈 row DOM 동기화. (b) addBtn 클릭 시 직전 빈 row(라벨/값 모두 비어 있는 신규 row) 가 있으면 신규 생성 안 하고 그곳으로 focus 만 이동 + 토스트 안내.
  • FIXv16_securityPlaceholder 안내 텍스트가 실제 매칭 우선순위와 충돌 — 기존 안내: "/facility/Assets/{site}/ 에 파일명 키워드". 그러나 v16_findSectionImage 의 1순위는 서브폴더 매칭(/보안/ 등), 2순위가 키워드. 안내대로 루트에 두면 키워드 폴백만 작동 → 다른 amenity 사진과 키워드 충돌 위험. 픽스: 1순위 경로 /facility/Assets/{site}/{서브폴더}/ 명시 + 대안으로 루트 + 키워드 안내. V19.7.1 의 NAS 폴더명 hotfix 와 동일 패턴.
  • FEATserver.js facility-info 헤더 mismatch 검증 — V19.2 v16_facRowMap 의 valueCol = keys.indexOf(site) >= 0 ? site : keys[1] || '' 가 시트명 우선이지만 불일치 시 keys[1] silent fallback → 사용자가 DWLT 시트 복사 후 두번째 컬럼 헤더(DWLG) 변경을 빠뜨리면 DWLG 값이 DWLT 시트에 표시되는 정합성 사고. _getFacilityData 가 시트명 ↔ 두번째 컬럼 헤더 검증 → mismatch 시 warnings: [{site, issue, expected, got, hint}] 응답 별도 필드 (backward compat, data 구조 무변경). 클라이언트 loadFacilityData 가 warnings 받으면 콘솔 + 0.6초 후 토스트로 안내.
  • TEST검증 — node --check src/js/60-view-facility.js src/server.js 모두 OK. 빌드 산출물: 60-view-facility.js 약 1,860줄 (V19.7.2 1809 → +~50줄 = 9 패치 포인트). server.js 약 2,560줄 (V21.0.7 ~2546 → +~16줄 = 헤더 검증). HTML dev-row V19.7.4 + V19.7.2 latest 강등 (latest 자리는 V21.0.6 비용분석 그대로).
  • OPS변경 파일 3개 — src/js/60-view-facility.js (9 패치 포인트: FAC_AMEN_CURRENT_TAB 변수 + v16_connectSSE dirty 가드 + f_renderSection amen 탭 보존 + v16_setAmenTab 변수 갱신 + _v16HistEscHandler 변수 + v16_openHistoryModal/closeHistoryModal ESC 정리 + v16_saveOverrides prune 후 재렌더 + addBtn 빈 row 가드 + v16_securityPlaceholder 서브폴더 안내 + loadFacilityData warnings 처리) · src/server.js (_getFacilityData 헤더 검증 + warnings 필드) · src/부산본부_대시보드.html (dev-row V19.7.4). server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v19.7.2 (없으면 v19.7.1).
2026-05-14
V19.7.2 사업장 정보 — 편의시설 hero 폐기 + 갤러리 단일화 + 라이트박스 사진 클릭 닫힘
  • FIXhero 영역 폐기 + 갤러리 단일 영역 — 사용자 결정 (2026-05-14): "위 하나짜리는 삭제하고 갤러리를 하나로 보여지게 깔끔하게". 진단: V19.7 의 v16-dt-hero 가 첫 사진 1장을 크게 띄우고 그 아래 v16-amen-gallery 에 동일 사진을 다시 thumb 으로 표시 → 사진 1장 중복 + 시각 무게가 위쪽 쏠림. 해결: v16_renderAmen 의 hero 블록 제거 → v16-amen-slot 슬롯으로 교체, v16_updateAmenityImage 가 panel 최상단에 갤러리 1개만 단일 렌더. 1장이어도 표시 (V19.7 의 "2장 이상" 조건 폐기), 1장일 때는 .single grid 로 16:9 비율 큰 카드.
  • FIX라이트박스 사진 클릭 시 닫힘 — 사용자 결정: "사진을 눌러서 팝업시 다시 사진을 누르거나 아무데나 눌러도 사진이 꺼지게". 기존: img.addEventListener('click', e => e.stopPropagation()) 가 backdrop 닫힘 차단 → 이미지 위 클릭 무시. 신규: img.click 핸들러에서 cleanup() 명시 호출 → 사진/배경 어디 클릭해도 닫힘. prev/next 버튼은 stopPropagation 유지 (사진 이동 중 닫힘 방지).
  • FEAT0장 placeholder 톤 정리 — v16-amen-slot-empty 신규 클래스. amenity 아이콘 + "{이름} · 사진 미등록" + NAS 폴더 경로 안내 (영업팀이 사진 추가 시 자동 표시되는 위치). V19.6 .ph-act-empty 빨강 톤 계승.
  • TEST검증 — node --check 통과. staging 배포 후 6 amenity 모두 갤러리 1개 영역 (1~4장 사진) + 라이트박스 ←/→ 키 + 사진/배경 클릭 닫힘 동작 확인. 빌드 산출물: 60-view-facility.js 1809줄 / 60-view-facility.css 710줄.
  • OPS변경 파일 2개 — src/js/60-view-facility.js (v16_renderAmen hero→slot 변경 +10줄 / v16_updateAmenityImage 갤러리 단일화 ~80줄 재작성 / v16_openImage img click cleanup +1줄) · src/css/60-view-facility.css (.v16-amen-slot* 신규 +35줄 / .v16-amen-gallery border-top 제거 / .single 추가). server.js 변경 0. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v19.7.1.
2026-05-14
V19.7.1 사업장 정보 — 편의시설 사진 매핑 NAS 폴더명 일치 (shw/mtg hotfix)
  • FIXFAC_AMENITY_SUBFOLDER 매핑 NAS 실제 폴더명 정확 일치 — 사용자 보고: 편의시설 사진이 안 들어감. 진단: NAS 실제 폴더는 샤워실 탈의실 / 회의실 교육장 인데 코드는 샤워실 / 회의실 로 매핑되어 있었음. v16_findAmenityImages 의 1순위 substring 매칭이 '/' + sub + '/' 형태라 /샤워실/ 으로는 /샤워실 탈의실/ 디렉토리 미매칭. 픽스: shw → '샤워실 탈의실', mtg → '회의실 교육장'. 2순위 키워드 fallback (['샤워','탈의'], ['회의','교육']) 은 그대로 유지 — 향후 NAS 폴더명 미세 변동 안전망.
  • TEST매칭 시뮬레이션 검증 — 6 amenity 모두 phase 1 (서브폴더 매칭) hit 확인: caf=2장 · lng=1장 · shw=2장 · gym=1장 · mtg=2장 · smk=1장. node --check 통과.
  • OPS변경 파일 1개 — src/js/60-view-facility.js (FAC_AMENITY_SUBFOLDER 2 키 수정 + 주석 +5줄). server.js 변경 0 → --include-server-js 불필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v19.7.
  • FEAT참고 — 사용자 screenshot 의 "Photo / Floorplan" placeholder 텍스트는 V17 Phase 2 이전 옛 mockup 텍스트(부산본부_대시보드.html.prephase2_bak 에 잔존). 현재 prod 가 V19.6/V19.7 미배포 상태임을 시사. 본 V19.7.1 hotfix 와 함께 V19.4-V19.7 일괄 배포 필요.
2026-05-14
V19.7 사업장 정보 — 편의시설 사진 갤러리 + 라이트박스 종이동 (←/→ 키 · 카운터 · 캡션)
  • FEATv16_openImage 시그니처 확장 — 단일/다중 사진 모두 호환 — 기존 v16_openImage(src) 단일 문자열만 받음. 신규: (srcOrList, startIndex). 단일 src 호환 (이전 동작 동일) + 배열 [src1, src2, ...] 또는 [{src, caption}, ...] 모두 처리. 다중일 때 ← / → 화살표 버튼, 좌/우 방향키, 1 / N 카운터 (좌상), 사진 캡션 (하단) 노출. 1장이면 자동 단일 모드(.has-nav 클래스 미부여 → 화살표/카운터 숨김).
  • FEATamenity 갤러리 그리드 신설 (.v16-amen-gallery) — hero 아래 + v16-dt-body 위에 갤러리 그리드 동적 삽입. 4컬럼 thumb 격자 (모바일 2~3컬럼 반응형), 첫 사진 data-active="1" outline 강조, lazy loading, onerror placeholder. 클릭 시 라이트박스 해당 idx 진입. 사진 2장 이상일 때만 노출 (1장은 hero 만으로 충분 → 시각 노이즈 회피).
  • FEAT현장사진 그리드도 라이트박스 종이동 (보너스) — v16_renderPhotos 의 각 thumb 이 기존 onclick="v16_openImage('src')" 단일 src → 신규 photoList closure 캡처 + 클릭한 인덱스로 진입. ← / → 키로 8장 사진 순환 가능. 사업장 기본정보 탭 사용성 향상.
  • FEAT라이트박스 키보드/접근성 보강 — ESC 닫기 (이전) + ← 이전 + → 다음. 첫/마지막 사진에서 해당 화살표 visibility:hidden 자동. close 버튼 호버 시 빨강 톤. aria-label 3개 (닫기/이전 사진/다음 사진).
  • TEST검증 — node --check src/js/60-view-facility.js src/server.js 모두 OK. 빌드 산출물: 60-view-facility.js 1,673 → 1,808줄 (+135) / 60-view-facility.css +85줄. v16_openImage 시그니처 변경은 backward-compat — 기존 호출처(현장사진 inline onclick 전환됨, 외부 호출 없음) 모두 정상. 1장만 매칭된 amenity 케이스도 hero 만 노출 (갤러리 그리드 자동 숨김).
  • OPS변경 파일 2개 — src/js/60-view-facility.js (v16_openImage 재작성 +90줄 / v16_updateAmenityImage 갤러리 그리드 +50줄 / v16_renderPhotos 종이동 +10줄) · src/css/60-view-facility.css (#v16-lightbox .nav/.counter/.lb-caption +50줄 / .v16-amen-gallery* 갤러리 그리드 +35줄). server.js 변경 0 → --include-server-js 불필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v19.6.
2026-05-14
V19.6 사업장 정보 — 3차 UX 폴리싱 (라이트 input 토큰 · ↺ 항상 노출 · 사진 검색 중 텍스트 · 빈 사이트 워크플로 안내 · 빈 자유 row prune · parseInt 안전망 · 키워드 충돌)
  • FIXU-3 라이트 input 토큰 분리 — :root[data-theme="light"] #view-facility { --bg-input: #f8fafc }. 기존 #ffffff 가 흰 카드 위에서 input 영역 인지 불가. 다크 토큰은 변경 없음. 60-view-facility.css.
  • FIXU-4 ↺ 되돌리기 버튼 — 편집 모드 active 시 항상 노출 — 기존 호버 노출만 → 터치/태블릿에서 영영 안 보이던 문제. #view-facility.v16-edit-mode .btn-revert{display:inline-block} 1줄 추가. 비편집 모드는 호버 노출 유지.
  • FIXU-5 amenity placeholder 텍스트 중립화 — "사진 자동 매칭" → "사진 검색 중" (로드 동안 매칭 약속 없음). 매칭 실패 시 v16_updateAmenityImage 가 텍스트를 "사진 미등록" 으로 명시 전환 + .ph-act-empty 빨강 톤. ph-act 기본 톤도 녹색(약속) → 회색(중립) 으로 변경.
  • FEATU-10 빈 사이트 배너 시트 복사 워크플로 6단계 명시 — 매핑가이드 §4-4 의 워크플로(시트 우클릭 → 복사본 만들기 → 이름/헤더 변경 → 값 채우기 → 저장) 를 UI 에 노출. <ol class="v16-empty-steps"> + <kbd> 키 표시. 영업팀이 매핑가이드 찾지 않아도 추가 가능.
  • FIXC-7 빈 자유 row 클라이언트 prune — 기존: 빈 라벨/빈 값 row 추가 시 dirty 카운트 +1 → 저장 시 서버 silent skip(line 1419) → 사용자 혼란. v16_saveOverrides 시작에 FAC_DIRTY_CUSTOM 의 _new && !label && !value 항목 사전 제거 + 토스트 안내.
  • FIXD-4 parseInt 안전망 (_facCount) — 기존: parseInt('200만화소 94대') = 200 → TOTAL 오염. 신규: (\d+)\s*(?:대|개소|면|실|EA|SET|조) 단위 prefix 우선 → 94 정확 추출. equip/fire 카드 카운트 양쪽 적용. 매칭 실패 시 마지막 정수 fallback.
  • FIXC-9 amenity ↔ section 키워드 충돌 정리 — v16_findSectionImage 의 2순위 키워드 fallback 에 /편의시설/ 또는 /amenity/ prefix 경로 제외 안전망. meeting override section 의 '회의실' 키워드가 amenity 의 /편의시설/회의실/foo.jpg 까지 매칭하던 의도 모호 케이스 차단.
  • TEST검증 — node --check src/js/60-view-facility.js src/server.js OK. 빌드 산출물: 60-view-facility.js 1384→1673줄 (+289), server.js +85줄, 60-view-facility.css 478→567줄 (+89). 시각 회귀 0 — 모든 변경 #view-facility 스코프.
  • OPS변경 파일 2개 — src/js/60-view-facility.js (_facCount 헬퍼 / U-5 / U-10 / C-7 / D-4 / C-9, ~70줄 net) · src/css/60-view-facility.css (V19.6 토큰 + .v16-empty-steps + .ph-act 상태별 톤, ~30줄 net). server.js 변경 0 → --include-server-js 불필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v19.5.
2026-05-14
V19.5 사업장 정보 — 2차 데이터 근본 (매칭 우선순위 · dict {value, note} · mtime 캐시 · facility-watch SSE)
  • FIXD-1 v16_v 매칭 우선순위 도입 (substring 단일 매칭 폐기) — V19.2.1 '구조'/'응급구조' 사고, V19.2.5 '제연'/'화재 수신기 연동' 사고 같은 substring 충돌의 구조적 픽스. 신규 매칭: ① exact (dict.hasOwnProperty(k)) → ② startsWith → ③ substring (fallback, 기존 동작). 각 phase 에서 첫 site 값 hit 즉시 반환. v16_v 시그니처 무변경 — 호출처 영향 0. 추가로 v16_note(dict, site, ...keys) 헬퍼 신규 (동일 매칭 우선순위로 비고 컬럼 추출).
  • FEATD-2 dict 값 구조 {value, note} 확장 + 단위/비고 노출 — 기존: dict[label][site] = '94대' 문자열 단일. 신규: {value:'94대', note:'200만화소 / 24시간 운영'} 객체. 매핑가이드 §4-1 의 세번째 컬럼(단위/비고) 정보 손실 종결. v16_v 는 내부 helper _facMatchValue 가 .value 만 반환 (레거시 string 도 처리하는 호환성). v16_renderBasic 일반현황 8행에 .v16-row-note 옅은 표시 추가.
  • FEATC-1 facility-info mtime 캐시 (_facilityCache) — 기존: 매 요청마다 xlsx.readFile + sheet_to_json 3시트 (~30ms disk IO). 신규: fs.statSync.mtimeMs 비교, 동일 시 캐시 응답 (~1ms). chokidar 가 파일 변경 감지 시 _facilityCache.mtime=0 강제 무효화. fuel/rowdata 모듈과 일관 패턴.
  • FEATC-3 facility-watch SSE 라우트 + 클라이언트 통합 — server.js: GET /api/facility-watch?pw= + sseClientsFacility Set + broadcastFacility(eventType) 함수. POST 라우트(저장/revert) 양쪽에서 명시 broadcast (즉시 응답성) + chokidar 가 FACILITY_PATH (엑셀, 'data-changed') 와 OVERRIDES_PATH (JSON, 'overrides-changed') 양쪽 감지. 클라이언트: App.api.connectSSE('/api/facility-watch?pw=', ...) — enterFacility 진입 시 1회 연결, 'overrides-changed' 수신 시 v16_loadOverrides + 현재 섹션 재렌더, 'data-changed' 수신 시 loadFacilityData.
  • FEATC-5 v16_facRowMap 메모이제이션 (_facRowMapCache) — 기존: f_renderSection 매 호출마다 174행 × 3시트 머지. 신규: FAC_DATA 동일 ref 면 dict 재사용. loadFacilityData / SSE 갱신 시 FAC_DATA 객체 교체 → 캐시 자동 무효화.
  • TEST검증 — node --check src/js/60-view-facility.js src/server.js 모두 OK. v16_v 신규 동작 — exact 우선 → startsWith → substring fallback. 기존 호출처 (v16_renderBasic/Equip/Fire/Amen + override 섹션) 호환성 무변경.
  • OPS변경 파일 2개 — src/js/60-view-facility.js (매칭 우선순위 재작성 +60줄 / dict 구조 확장 +30줄 / SSE 클라이언트 +35줄 / 일반현황 note 렌더 +20줄) · src/server.js (mtime 캐시 +15줄 / facility-watch SSE +40줄 / chokidar 감시 +18줄 / POST broadcast 2 지점 +2줄). server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v19.4.
2026-05-14
V19.4 사업장 정보 — 1차 응급 5건 (If-Match 강제 · brigade dict 제외 · addons 빈 카드 · ✎ disabled · 📜 history 모달)
  • FIXC-2 facility-overrides POST — If-Match 강제 (lost update 차단) — 기존: if (ifMatch && ifMatch !== curEtag) 조건. ifMatch 누락 시 통과 → 다중 사용자/curl 직접 호출에서 lost update 위험. 신규: if (!ifMatch || ifMatch !== curEtag) → 누락도 409 반환 + hint: 'If-Match header missing'. 클라이언트(v16_saveOverrides) 는 항상 etag 전송 → 호환성 영향 0. server.js line 1385-1389.
  • FIXD-3 v16_facRowMap brigade scope 분리 — 기존: DWLG 시트의 디더블유엘글로벌/세중해운 2 brigade 가 소방대장/부대장/... 8 동일 라벨 → dict 에서 마지막 brigade 만 남음 (의미 없는 데이터). 신규: '자체소방대 조직도' 헤더 만나면 inBrigade=true, 8역할(FAC_BRIGADE_ROLES) 라벨은 dict 등록 자체 제외. 조직도 렌더는 v16_facBrigades 가 행 순서로 별도 처리.
  • FIXC-6 addons 빈 카드 자동 숨김 (V19.3 cats 패턴 확장) — 기존: 부속장비 6종(드럼/야드램프/포크연장바/코일발/지게차연장발/컨테이너발판) 무조건 모두 노출 → DWLG 외 사이트는 0대인 부속이 다수 → 빈 카드 6개 시각 노이즈. 신규: validAddons filter 적용 + 0건이면 부속 섹션 전체(헤더 포함) 숨김.
  • FIXU-1 ✎ 편집 버튼 4 엑셀 섹션 dimming + 토스트 안내 — 기존: 기본/장비/소방/편의 진입 후 ✎ 클릭 시 모드 토글되지만 input 없음 + 저장 바도 미노출 → 사용자 혼란. 신규: v16_toggleEditMode 가 비편집 가능 섹션에서 토스트 안내 후 return. v16_syncEditModeUI 가 .v16-edit-disabled 클래스 토글 (opacity .45) + tooltip 분기.
  • FEATU-2 📜 변경 이력 모달 (server 라우트는 V19.1 부터 존재, 클라이언트 진입점 신설) — v16_openHistoryModal + v16_closeHistoryModal 신규 함수. 헤더 우측에 v16-history-btn 추가. GET /api/facility-overrides/history?site=§ion=&limit=200 호출. 6컬럼 표(시간/사용자/섹션/항목/before→after/액션) + 한글 액션 라벨 매핑 + ESC/backdrop/✕ 닫기 일관. .v16-hist-* CSS 신규 (~70줄, 모달 sticky 헤더 + 스크롤 body).
  • TEST검증 — node --check 통과. POST /api/facility-overrides 에 If-Match 누락 → 409 (이전 200). 메모리 동기화 — facility-watch SSE 연결 시 변경된 If-Match 도 정상 동작.
  • OPS변경 파일 3개 — src/server.js (If-Match 1줄 강화 + 4줄 hint) · src/js/60-view-facility.js (brigade dict 제외 +12줄 / addons filter +12줄 / ✎ disabled +6줄 / history 모달 +90줄 / 글로벌 노출 +2줄) · src/css/60-view-facility.css (.v16-edit-disabled + .v16-hist-* 모달 +70줄) · src/부산본부_대시보드.html (📜 이력 버튼 +1줄). server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v21.0.7.
2026-05-12
V21.0.7 비용분석 — 회귀 fallback + ma12DevPct 필드 정합 + 분류 override masking 경고 (코드 감사 ③⑤⑨ 묶음)
  • FIX③ 회귀 무효 시 MA12 dev fallback 명시화 — _lsRegression 에 valid 플래그 추가 (표본 N≥6 AND 매출/비용 분산 > 0 AND r² 유한). override 가 occurrences<6 (auto:tentative) 계정에 variable 강제 시 회귀가 무효임에도 silent fallback (slope=0, intercept=yMean) 으로 "회귀잔차" 라벨이 그대로 노출되던 사각지대. detectVariability variable/semi 분기에 reg.valid 검사 추가, 무효 시 fixed 와 동일한 MA12 dev 룰 (|MA12 dev| > 10%) 로 대체. 응답 outlierMethod 신규 필드 — 'regression' | 'ma12dev' | null (디버그/UI 용).
  • FIX⑨ momPct → ma12DevPct 필드 정합 + UI 라벨 "MoM" → "평균 대비" — 기존 momPct 가 이름과 달리 (last/MA12 - 1) × 100 (12M 평균 편차) 였던 mislabeling 정리. 서버 응답에 ma12DevPct 신규 필드 (동일 값, 의미 명확), momPct 와 ma12Pct (fixed 전용) 은 한 사이클 backward-compat 유지 (DEPRECATED 주석). 클라이언트 fallback 우선순위: ma12DevPct → ma12Pct → momPct. UI 라벨 7곳 "MoM" → "평균 대비" 일괄 정정 (워닝 그리드 stats, 카테고리 모달 표, 모달 차트, TOP 변동 카드, 안정성 매트릭스 verdict/delta pill). cost-summary 의 accountList 도 동일 패턴.
  • FEAT⑤ 분류 편집 모달 — override masking 경고 다이얼로그 — 사양서 §4-1 override 적용 우선순위: <branch>|<account> > 통합|<account> > 자동. 사용자가 이 규칙을 모르고 통합/사업장 override 를 동시 보유 상태에서 변경 시 의도와 다른 결과 발생. rd_clsCheckMaskingConflicts 신규 함수 — pre-save 단계에 두 시나리오 검증: (A) 통합 변경 시 동일 계정에 사업장 override 존재 → 해당 사업장에서 마스킹됨, (B) 사업장 변경 시 통합 override 존재 → 해당 사업장에서만 통합값 덮어쓰여짐. confirm() 다이얼로그로 명시 동의 요구, 취소 시 저장 중단 (pendingSet 보존).
  • TEST영향 예측 — ③ 회귀 무효 케이스는 override 강제 시에만 발생 (현재 회계팀 사용 패턴 거의 0) → 기존 워닝 갯수 무변동 예상. ⑨ 라벨 변경은 시각만 (값 동일), 회계팀 멘탈 모델 정렬. ⑤ masking 경고는 confirm 추가로 1 클릭 증가, 의도된 변경은 영향 없음. node --check src/server.js OK.
  • OPS변경 파일 4개 — src/server.js (_lsRegression valid 플래그 +8줄 · detectVariability fallback 분기 +10줄 · 응답 필드 4개 추가 +6줄 · computeCostSummary ma12DevPct 추가 +1줄) · src/js/54-view-rowdata.js (UI 라벨 정정 7곳 ~30줄 · masking 경고 함수 +40줄) · src/부산본부_대시보드.html (dev-row V21.0.7 + V21.0.6 latest 강등) · 1.기초정보/08_비용분석_V18_점검모드.md (§4-2 부분 갱신 — 회귀 fallback 추가). server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v21.0.6.
2026-05-12
V21.0.6 비용분석 — fixed 워닝 룰 상승/하락 비대칭 분기 (한 달 급등 fixed 진짜 위험 신호 회복)
  • FIXfixed Critical 게이트 — 상승/하락 분기 — V21.0.5 의 Critical 게이트가 |MA12 dev|>10% AND isLongDown AND isShortDown AND |trueMoM|≥1% 4중 AND. 추세 게이트가 12M·3M 둘 다 음수 slope 만 검사하는 구조라 한 달 급등 fixed 항목(임차료 인상·보험료 재계약·일회성 보수비 등) 의 진짜 위험 신호가 Critical 에 영구 미진입하던 사각지대. V21.0.6: ma12Dev 부호로 상승/하락 분기. 상승(ma12Dev > 0) 은 추세 조건 없이 |dev| 만으로 판정 (한 달 급등도 위험). 하락(ma12Dev < 0) 은 기존 추세 게이트 유지 (V21.0.5 step-down 안정화 노이즈 방어 의도 보존). |trueMoM| ≥ 1% 공통 가드는 양쪽 유지.
  • TEST영향 예측 — 추가로 잡힐 항목: 한 달 급등 fixed (예상 0~3건/월). 제외 유지: step-down 후 안정화 케이스(V21.0.5 4 BNCC 케이스). variable/semi-variable 룰 무수정. node --check src/server.js 통과.
  • OPS변경 파일 1개 — src/server.js line 442~474 (fixed 분기 재작성 +12줄 / -2줄). HTML/CSS/JS 무수정. server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v21.0.5.
2026-05-08
V21.0.5 비용분석 — Summary 탭 신설 + 통합 모드 폐지 + 워닝 룰/UX 보강 9건 통합
  • FEAT📊 Summary 탭 신설 (디폴트 활성) — 사용자 의도: "각 사업장의 고정성 비용 규모를 한눈에 + 변동성의 변화 흐름 보고 싶다". §1 KPI 4 카드 (DWLG/DWLT/BNCC + 통합) · §2 사업장별 고정비 비교 막대 · §3 사업장별 비용 누적 추이 (16M grouped+stacked 막대 — 월별 3 사업장 그룹, 각 막대 안 고정비(아래)+변동비(위) 누적, 사업장별 색조 진한/옅은 톤) · §4 사업장별 워닝 분포 (Critical/Warning/Info × DWLG/DWLT/BNCC + 상위 3 계정).
  • FEAT통합 사업장 모드 폐지 — 헤더 [통합] 버튼 제거, 디폴트 branch = dwlglobal. 통합 정보는 Summary 탭에서만 노출. 변동성/고정비 점검 탭은 단일 사업장 데이터만 처리 → 인지 단순화.
  • FIX워닝 모달 차트 신규 + 자동 단위 — 자동 진단 ↔ 거래처 분해 사이에 📈 장기 비용 흐름 차트 (가용 전체 최대 24M, 회계마감 월 색 강조 + 12M 평균 점선). max abs 기반 단위 자동 (원/만/백만/억) → 소액 (266,650원, 15,360원 등) 도 0 떨어지지 않고 정확 표시.
  • FIX고정비 점검 탭 본문 빈 화면 픽스 — 사용자 보고: 🔷 고정비 점검 탭 본문 완전히 빈 화면. 원인: HTML <div class="rd-main dn"> 의 .dn 클래스 + CSS .dn{display:none!important} 가 JS style.display='block' inline 토글 차단. fix: .dn 클래스 제거, inline style 만 사용.
  • FIX부제 텍스트 동적 갱신 — 탭 전환 시 부제가 항상 "V19 변동성 점검" 으로 하드코드 → rd_updateSubtitle 헬퍼 신설, Summary/고정비/변동성 탭별 라벨 자동 변경.
  • FIX워닝 mismatch — fixed 룰 보강 — 사용자 보고 (BNCC 4 케이스, 거래처 분해 +0% 인데 워닝): step-down 후 새 수준에서 안정화된 fixed 가 매월 false-positive. 원인: 기존 룰이 |MA12 dev| 만 검사 → 변동 정지 후에도 평균과의 차이 지속. fix: && |trueMoM| ≥ 1% AND 추가 → 안정화된 step-down 자동 제외. 추세 정보는 모달 차트 + "평균 대비" 셀에서 노출.
  • FIX워닝 모달 5칸 → 6칸 + trueMoM 신설 — server.js 의 momPct 가 이름과 달리 (last/MA12-1)×100 (12M 평균 편차) — UI 라벨 "전월 대비" mislabeling 의 원인. 신규 필드 trueMoM = (last-prev1)/prev1×100 (실제 MoM, 거래처 표 변화율과 일치). 모달 셀 6개: 매출 중 비중 / 전월 대비(trueMoM) / 평균 대비(MA12, 신규) / 평소 대비 / 1년 흐름 / 최근 3개월. CSS 6칸 grid + 1100px 미만 3칸 / 700px 미만 2칸 반응형.
  • FIX§4 사업장별 고정비 분포 — 통합 분류 강제 (옵션 A) — 사용자 보고: 사업장 필터 변경 시 §4 합계마저 달라짐. 원인: cost-summary 라우트가 사용자 branch 기준 분류 산출 → 같은 계정이 사업장별로 다르게 분류 → set 자체 변동. fix: computeCostSummary 시그니처에 distClassifications 추가, siteDistribution 만 통합(total) 분류 + 통합 계정 set 강제 사용. branch 필터로 §1~§3 만 영향, §4 는 항상 "전사 분류 기준 사업장 분포".
  • FEATserver.js cost-summary 응답 보강 — Summary 탭 데이터 한 번 fetch 로 처리하기 위해 응답에 siteSeries: {DWLG/DWLT/BNCC: [16M]} + siteSeriesMonths 추가. cost-warnings 응답에는 byBranch: {사업장: {critical, warning, info, topAccounts}} 추가 (3 사업장 워닝 갯수 + 상위 3 계정명, 부하 약 3배).
  • TESTstaging 검증 — staging 21/21 PASS · 사용자 시각 검증: Summary 탭 §1~§4 정상 노출, §3 누적 막대 차트 정상 (월별 3 사업장 그룹 + 고정비/변동비 누적, 사업장별 색조), 워닝 모달 차트 + 6칸 셀 + 자동 단위 정상, 4 BNCC 안정화 fixed 워닝 자동 제외. node --check 통과.
  • OPS변경 파일 4개 — src/server.js (trueMoM + fixed 룰 + siteDistribution 통합 분류 + siteSeries + byBranch, ~80줄) · src/js/54-view-rowdata.js (Summary 탭 IIFE + 모달 6칸 + 차트 자동 단위 + 누적 막대, ~280줄) · src/css/54-view-rowdata.css (Summary 스타일 + 6칸 grid 반응형, ~25줄) · src/부산본부_대시보드.html (Summary 탭 골격 + 탭 변경 + 통합 버튼 제거 + 모달 차트 섹션, ~50줄). server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v20.7.
2026-05-08
V20.7 매출 분석 — 시점 산출 데이터 기반 fallback (4월 마감 미반영 시 3월 자동) + deploy.py 워밍 자동 대기
  • FIXs_currentYearMonth 데이터 기반 회계마감 월 fallback — 사용자 보고(2026-05-08, 4월 마감 진행 중): 매출 분석 진입 시 KPI ① 당월 매출 0원 / 12M 끝 라벨 '26.04' 빈 막대. 원인: 캘린더 기반 setMonth(d.getMonth() - 1) 가 5월 진입 시 무조건 4월 반환 → 4월 마감 미반영(Sales=0) 데이터로 KPI 망가짐. 픽스: 비용분석 V19 autoLatestMonth 패턴 차용 — _rdCache.db[year].total.{Sales,TotalCOGS} 역순 검색 → 첫 양수 월 자동 선택. 결과: 5월 초(4월 마감 전) → 3월 자동 표시. 4월 마감 후 SSE rowdata-watch 자동 갱신 → _rdCache 재할당 → 자동 4월 전환 (사용자 개입 0).
  • OPSscripts/deploy.py 워밍 자동 대기 (wait_for_warmup) — 진단: 컨테이너 재시작 후 Fuel 워커 prewarm 60-80초 (3사업장 × 12개월 = 36 xlsx 순차 파싱) 동안 메인 thread 동기 fallback 으로 fuel-info/facility-info 등 blocking → smoke 1차 시 10/21 timeout(`-1`) 다발. /health 200 은 워밍 완료를 보장하지 않음. 픽스: wait_for_warmup 함수 신설 — 헬스체크 통과 후 /api/fuel-info 5초 간격 폴링, HTTP 200 + 응답 ≥500B 시 통과 (최대 120s). deploy_normal + rollback_n 양쪽 적용 (rollback_to_tag 는 deploy_normal 경유로 자동 상속). 결과: V20.7 staging 재배포 검증 — 41초에 자동 통과 → smoke 1차 21/21 PASS (이전 11/21).
  • TESTRAW 데이터 검증 — /api/rowdata-json db.2026.total: 2026.04 Sales=0/COGS=0(미마감), 2026.03 Sales=24.47억/COGS=19.19억, 2026.02 Sales=23.61억. node --check src/js/57-view-sales.js OK. staging smoke 21/21 PASS (1차, 이전 deploy 에서는 워밍 미완료로 11/21).
  • OPS변경 파일 2개 — src/js/57-view-sales.js 3 위치 (모듈변수 _rdCache 추가 / loadSalesData 의 _rdCache = rd 갱신 / s_currentYearMonth 데이터 기반 분기, +18줄 / -7줄) + scripts/deploy.py wait_for_warmup 함수 신설 (+38줄). server.js 변경 0 → --include-server-js 불필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v21.0.4.
2026-05-07
V21.0.4 비용분석 — 평균 변동폭 안의 변동 워닝 일괄 제거 (V21.0.3 안전망 확장)
  • FIXInfo 트리거를 isStatOutlier 단독으로 단순화 — 사용자 보고: 복리후생비 의료보험료 (통합) 케이스 — momPct=-10% (변동 있음), 1년 ↓ 하락, 최근 3개월 ↑ 상승, 평소 대비 정상(z-score < 1). V21.0.3 룰 `isLongDown && |MoM|≥1%` 분기로 Info 통과되어 노출. 사용자 결정: "변화가 평균변동폭보다 적거나 아예 없으면 화면에 표시 안하는게 더 좋다". V21.0.4: isLongDown 단독 Info 분기 완전 폐기. isStatOutlier (통계적 이상치) 만 Info 트리거. 추세 정보는 워닝 모달 5셀 + 자동 진단 박스에서 여전히 노출 가능.
  • FIX안전망 확장 — !isStatOutlier 단독으로 reject — V21.0.3 안전망 (`|MoM|<1 AND !isStatOutlier`) → V21.0.4 (`!isStatOutlier` 단독). 즉 z-score 정상 범위 안의 모든 momPct (점진적 하락 -10% 등) 도 일괄 reject. variable/semi-variable 카테고리에서 평소 변동폭 안의 변동은 워닝 가치 없음. fixed 는 MA12 deviation 룰 별도라 제외.
  • TEST영향 — Critical/Warning 룰의 isLongDown && isShortDown 분기도 안전망에 의해 isStatOutlier 필수가 됨 (즉 추세 동반 하락만으로는 Warning 안 띄움, 통계 이상치 동반 시에만). 진짜 의미 있는 변동만 워닝 그리드 노출. node --check src/server.js OK.
  • OPS변경 파일 1개 — src/server.js line 446~462 (level 분기 단순화 + 안전망 확장 ~12줄). HTML/CSS/JS 변경 0. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v21.0.3.
2026-05-07
V21.0.3 비용분석 — Info 워닝 룰 강화 (변동 0% 노이즈 제거)
  • FIXInfo 트리거 조건 강화 — isLongDown 단독 분류 폐기 — 사용자 보고: 인건비 제수당 연차 같은 계정이 당월=전월=3,907,821원 (변동 0%) 인데도 Info 워닝으로 모달 노출. 원인: detectVariability Info 룰 (line 446) isStatOutlier || isLongDown → 1년 시계열 회귀 slope 가 음수면 당월 변동 무관하게 Info 분류. 변경: isStatOutlier 단독 Info (통계적 이상치는 의미 있음) + isLongDown && |MoM|≥1% 동반 조건. 결과: 1년 하락 추세지만 당월 평탄한 계정은 Info 워닝 안 띄움 (추세는 차트로 확인 가능, 회계팀 사유 입력 불필요).
  • FIX안전망 가드 추가 — variable/semi-variable 카테고리에서 |MoM|<1% AND !isStatOutlier 인 케이스는 level 결정 후 reject (level=null). 어떤 룰로 들어와도 변동 0%+통계 정상 거래처는 워닝 그리드 노출 안됨. fixed 는 MA12 deviation 룰 별도라 제외 안함.
  • TEST영향 — Info 컬럼 워닝 건수 감소 (이전 노이즈 제거). Critical/Warning 룰은 무수정 → 진짜 변동 거래처는 그대로 노출. node --check src/server.js OK.
  • OPS변경 파일 1개 — src/server.js line 443~458 (level 분기 + 안전망 ~10줄). HTML/CSS/JS 변경 0. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v21.0.2.
2026-05-07
V21.0.2 비용분석 — detail 키 거래처 단위 단순화 (적요별 분리 폐기, V21.0.1 우회 → 본질적 정리)
  • FEATdetail 키 자체를 거래처(Client) 단위로 단순화 — 사용자 결정: "적요별 합산은 안되고 거래처별 합산으로 정리". 이전(V21.0.1): addDetail 의 키가 ${cli}|${nt} (거래처+적요) 조합이라 같은 거래처여도 적요(Note)가 매월 다르면 별개 entry → V21.0.1 hotfix 가 prevByClient map 으로 우회 매칭만 처리. V21.0.2: key = cli 단순화 + Note 필드 제거 → detail 객체 = {Client, Amount, PrevAmount}. 결과: 같은 거래처의 모든 적요가 자연스럽게 합산되어 raw 응답부터 거래처 1행. 워닝 모달 / 카테고리 모달 / 모든 화면 일관성.
  • FIXPass 1 PrevAmount 룰 단순 키 매칭으로 회복 — V21.0.1 의 prevByClient 우회 코드 ~25줄 제거 → 옛 단순 키 매칭 (bd.Details[m-1][tk][ac][k]) 회복. 키가 거래처라 직접 매칭으로 정확. 1월(m=0) → 전년 12월 lookup fallback 보존. 메인 thread (line 2102~) + 워커 인라인 (line 38) 두 곳 모두 동일 단순화.
  • FIXtopClients 그룹화 코드 단순화 — V21.0.1 의 _byClient map 그룹화 ~12줄 제거 → 단순 slice(5) + map. detail 자체가 거래처 1행이라 그룹화 불필요. 라인수 감소 + 가독성 회복.
  • TEST영향 범위 — Note 필드 사용처 grep 결과: V18 IIFE 의 aggregateByClient 가 unique Note 수집 (현재 dead code), V19 워닝 모달 / 카테고리 모달 / cost-summary events 어디서도 detail.Note 결정적 참조 없음 → 정보 손실 0. 매출 분석(V20.6) 은 buildRowdataJson 의 Sales/COGS/SGA 시계열만 사용하고 detail 직접 참조 없음 → 회귀 0. node --check src/server.js OK.
  • OPS변경 파일 1개 — src/server.js 4 위치 (메인 addDetail / 메인 Pass 1 / 워커 addD / 워커 Pass 1 + topClients 단순화). HTML/CSS/JS 변경 0. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v21.0.1.
2026-05-07
V21.0.1 비용분석 hotfix — 거래처 분해 PrevAmount 산출 버그 픽스 (전월 0원 NEW false positive 다발 해결)
  • FIXbuildRowdataJson Pass 1 PrevAmount 룰 변경 — 거래처 단위 전월 합계 — 사용자 보고: 워닝 모달 거래처 분해 표에서 (주)대우로지스틱스 부산터미널 같은 단골 거래처가 당월 37,974,000원 / 전월 0원 / NEW 로 잘못 표시되어 워닝 false positive 다발. 원인: addDetail 의 detail 키가 ${cli}|${nt} (거래처+적요) 조합 (server.js:2058) → 적요(Note, X열)가 매월 달라지면 (예: "3월분 보관료" / "2월분 보관료") 같은 거래처여도 별개 키로 분리 → 전월 같은 키 부재 → PrevAmount=0 → NEW. 픽스 — Pass 1 에서 전월 dict 를 prevByClient[Client] += Amount 로 거래처 단위 합산 사전 빌드 → 당월 각 detail 의 PrevAmount = 같은 거래처 전월 총합 (적요 무관). 결과: 거래처가 매월 거래하면 NEW 가 아닌 정확한 deltaPct 산출.
  • FIXdetectVariability topClients 거래처 단위 그룹화 — V19 워닝 모달 거래처 분해 표가 거래처별 1행 표시 (스크린샷 참조). 이전: 적요별 row 5개 (같은 거래처 다중 적요면 중복) → 가독성 저하 + 비중 분산. 픽스 — _byClient[c] map 으로 같은 거래처 다중 적요의 Amount 합산, prev 는 거래처 합산값(Pass 1 보장)이라 max 채택, 합산 후 sort+slice(5). 결과: 거래처별 1행 + 정확한 share/deltaPct.
  • TEST영향 범위 — buildRowdataJson 변경은 /api/rowdata-json 응답의 모든 detail 에 영향 (V18 비용분석 IIFE 와 V19 모두 사용). 단 V18 종합 탭은 폐기 상태 + V19 워닝 모달이 PrevAmount 의 주 사용처라 부작용 거의 없음. 매출 분석 (V20.6) 은 buildRowdataJson 의 Sales/COGS/SGA 시계열만 사용하고 PrevAmount 직접 참조 없음 → 회귀 0. node --check src/server.js OK.
  • OPS변경 파일 1개 — src/server.js (Pass 1 PrevAmount 로직 ~25줄 + topClients 그룹화 ~12줄). HTML/CSS/JS 변경 0. server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v21.0.
2026-05-07
V19.0.1 비용분석 폴리싱 — 히트맵 호버 텍스트 픽스 + §4 사유 타임라인 폐기 + 이번주 신규 칩 폐기
  • FIX히트맵 셀 호버 시 텍스트 흐림 픽스 — .treemap-cell:hover{stroke:#fff;stroke-width:2;...} 가 SVG <g> 에 적용되어 자식 <text> 글리프 외곽이 굵은 흰색 stroke 를 상속받아 글자가 흐릿한 흰 덩어리처럼 보이던 버그. 픽스 — filter:brightness(1.15) 만 g 에 적용 + :hover>rect{stroke:...} 로 stroke 는 rect 만 타게팅 + .treemap-cell text{stroke:none} 안전망. SVG 속성 상속 메커니즘 회피.
  • FEAT§4 사유 누적 타임라인 섹션 폐기 — 변동성/고정비 양 탭의 timeline 블록 (각 16 lines, 검색바 + 5 필터 + tl-list + tl-summary) 통째 제거. 사유는 §3 워닝 모달 + 📜 변경 이력 모달에서 단독 확인 가능, 별도 타임라인은 회계팀 사용 빈도 낮음. 변동성 탭 §5 매출-변동비 → §4 번호 하향. rd_renderTimeline 함수 본체는 dead code 보존 (Phase 5 일괄 정리). rd_renderAll 두 호출 제거.
  • FEAT"이번주 신규" 칩 폐기 — 추적 칩 바 3 칩 → 2 칩 (사유 입력률 + 미입력 14일+). 비용분석은 월단위 회계 마감 점검 모드라 "7일 이내 신규" 시간 단위가 부적합. rd_renderTrackChips 의 newWeek 산출 로직 (missingDays ≤ 7 카운트) + DOM 갱신 5줄 제거.
  • TESTstaging smoke 21/21 PASS (워밍 ~5분 완료 후 12:13 KST). cost-classifications/cost-overrides 200 + cost-watch SSE 첫 바이트 0.03s. node --check 양 파일 OK. 빌드 산출물 md5 누적 진화: aaff3976 (V19.0 P3-B+P4-B 완성) → 3cf221e9 (hover) → 81e498ba (timeline) → 423f7767 (newweek). 최종 1,166,537 B (V19.0 대비 -1,386 B = 3 hotfix 누적 dead 코드 제거 효과).
  • OPS변경 파일 3개 — src/css/54-view-rowdata.css +5줄 / src/js/54-view-rowdata.js -8줄 (newWeek + timeline 호출) / src/부산본부_대시보드.html -36줄 (timeline 두 블록 + newweek 칩 + V19 dev-row 신규). server.js 변경 0 → --include-server-js 불필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v20.6.
2026-05-07
V19.0 비용분석 완성 — Phase 3-B 고정비 탭 + Phase 4-B 분류 편집·메모 history 결선 (모듈 100% 완수)
  • FEATPhase 3-B 고정비 탭 결선 4 영역 — ① rd_renderFixKpi() KPI 4 카드 (고정비 합계 / 매출 대비 비율 / 워닝 건수 / 사유 미입력 14일+) + 4 sparkline 결선. 매출 대비 비율 16M 평균 비교, missing14 빨간색 강조, 입력률 % 표시. ② rd_renderStabGrid() 안정성 매트릭스 7 small multiples — top 7 by lastAmt, 카드별 stab-card.{ok|warn|crit} 외곽 (워닝 매핑), rd_drawStabMini Chart.js 16M 막대 + MA12 ±5% band 점선 (Chart.js fill:'+1' 으로 band 영역 채움), verdict 칩 4단계 (✓매우 안정/✓안정/⚠ 평년 +N%/⚠ MA12 ±10% 이탈). ③ rd_renderDist() 사업장×고정비 분포 DWLG/DWLT/BNCC 가로 막대 + share 가중치 max 정규화 + 자동 인사이트 푸터. ④ Tier 4 타임라인 — rd_renderTimeline('fix') 기존 함수 scope 인자만 추가로 fixed 카테고리 필터.
  • FEATPhase 4-B 분류 편집 모달 결선 — V18 stub rd_openClsModal/rd_clsModSetBranch/rd_saveClsOverrides 모두 실구현 교체. rd_clsRenderTable 사업장 탭별 cost-classifications fetch (캐시 RD19.clsModal.classByBranch[branch]) → 90 계정 × 6 컬럼 표 (계정/현재 분류/자동 r²·CV/오버라이드 select/당월금액/↺ reset). rd_clsOnChange 가 select 변경 시 pendingSet[key]/pendingUnset[key] 누적, 행 dirty 시각 강조 (rgba 245,158,11). rd_clsResetRow 가 unset 표시. POST /api/cost-overrides + If-Match etag 헤더 + X-User-Name encodeURIComponent (V20.3 ISO-8859-1 호환 패턴) 결선. 충돌 409 시 자동 알림. 저장 후 메인 데이터 자동 reload.
  • FEATPhase 4-B 메모 history 모달 결선 — GET /api/cost-notes/history?key=&limit=50 결선. 워닝 모달 사유 폼 우상단에 📜 변경 이력 버튼 추가 — rd_openMemoHistory(key) 호출 시 해당 워닝 키만 필터, 미입력 시 인자 없으면 전체 history 노출. 5 컬럼 표 (시간/사용자/액션 create·update·clear/키/변경 — before→after diff, memo 40자 ellipsis). 다른 사용자 편집 추적 가능.
  • FEATserver.js cost-summary 응답 확장 (1줄) — accounts[].costSeries: hist.costSeries.slice(-16) 추가. Phase 3-B 안정성 매트릭스 mini chart 가 16M 시계열을 직접 사용. 50개 계정 × 16개월 = 800개 숫자, 응답 크기 부담 미미. 기존 4 카테고리 응답 모두 동일 필드 노출.
  • FEATrd_renderAll 양 탭 동시 렌더 + display 토글 — V19 Phase 3-B 정책: 진입/필터 변경 시 변동성 + 고정비 양 탭 모두 렌더 (총 9 영역 + 추적 칩 바), rd_setTab 가 display:none 토글로 즉시 전환. 시각 비용 대비 사용자 체감 응답성 우수 (탭 전환 ms 단위). RD19.tab 상태 보존.
  • TESTsmoke.py 18 → 21 항목 확장 — /api/cost-classifications?branch=total (200/503 워밍 중 정상) / /api/cost-overrides (200) / SSE /api/cost-watch (첫 바이트 < 3s) 3 항목 신규. node --check src/js/54-view-rowdata.js src/server.js 모두 OK.
  • DOCSdoLogin 콘솔 에러 — 캐시 문제 확정 — staging 컨테이너 /app/부산본부_대시보드.html line 6822-6824 에 window.doLogin/doLogout/togglePw = ... 정확히 노출됨. 사용자 측 Ctrl+Shift+R 강제 새로고침으로 해결. 동일 패턴(showView/enterPl/enterDaily 등) 모두 정상.
  • OPS변경 파일 4개 — src/js/54-view-rowdata.js +400줄 (Phase 3-B 4 함수 + Phase 4-B 두 모달 결선) / src/server.js +1줄 (accounts costSeries) / scripts/smoke.py +6줄 (3 항목 추가) / HTML dev-row 신규 + V19.3 latest 강등. server.js 변경 → --include-server-js 필요. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v20.6.
2026-05-06
V19.0 비용분석 — 변동성 점검 + 사유 누적 모드 (Phase 1 백엔드 + Phase 2 HTML/CSS 골격)
  • FEATserver.js V19 라우트 7종 신규 (+826줄, 1457→2298) — GET /api/cost-classifications (자동 분류 5범주: variable/fixed/semi-variable/one-time/tentative) · GET /api/cost-warnings (회귀잔차 ±2σ + MA12 ±5% + V20.6 12M·3M slope 동반 하락 + 신뢰도 점수 1~4) · GET /api/cost-summary?cat=fixed|variable|one-time (카테고리 세부 분석) · GET·POST /api/cost-notes (V20.2 sales-notes 패턴 영속, If-Match etag, X-User-Name) · GET /api/cost-notes/history · GET·POST /api/cost-overrides (V19.1 facility-overrides 패턴, set/unset) · GET /api/cost-watch (SSE: notes-changed/overrides-changed/data-changed 3 이벤트). SITE_LABEL = {total:'통합', dwlglobal:'DWLG', terminal:'DWLT', cfs:'BNCC'} 매핑 — UI 표시는 BNCC, 데이터 키는 cfs 보존(V18 호환).
  • FEATHTML view-rowdata V19 골격 교체 — V18 12섹션 종합 탭 폐기, 두 탭 구조(🔷 고정비 점검 / 🟠 변동성 점검) + 인라인 필터(사업장 통합/DWLG/DWLT/BNCC + 기간 당월/YTD/전년동기/12M) + 트랙 칩 바(사유 입력률/미입력 14일+/이번주 신규) + 모달 4종(워닝/분류 편집/카테고리/메모 history). D3 v7 CDN 1줄 추가(cdnjs/d3/7.8.5/d3.min.js). portal-hdr 우측에 ⚙ 분류 편집 버튼 신규.
  • FEATCSS V19 신규 ~440줄 — mockup_비용분석_v19.html 신규 스타일 #view-rowdata 스코프로 일괄 격리 이식. .basics-card(KPI 4 카드, 변동비/고정비/일회성/사유) / .treemap-card(D3 squarified Stock-Heatmap, finviz 패턴) / .warn-grid(Critical/Warning/Info 3분할) / .timeline-card(사유 누적 60일) / .scatter-card(매출-변동비 산점도 + 추세선) / .stab-grid(고정비 안정성 매트릭스 small multiples 7 계정) / .dist-card(사업장×고정비 가로 막대) / .mod-bd·.cls-table·.cat-grid(모달 4종). --cat-variable/fixed/semi/onetime/tentative 분류 색 토큰 5종 다크/라이트 분리.
  • FIX_getRowdataDb 가드 503 (메인 thread blocking 차단) — V18 패턴이 워커 pre-warm 미완료 시 메인 thread 에서 buildRowdataJson()(16M xlsx 파싱) 동기 호출 → node.js single-threaded event loop 점유 → /health 포함 모든 라우트 hang. prod 32시간 가동으로 워밍 끝나 미발현이던 잠재 버그. V19 가드 — 캐시 비어있으면 즉시 503 + retryAfter:5 + 한글 메시지 throw, cost-classifications/warnings/summary catch 에서 err.status 우선 반환. 클라이언트 retry 로 안전 우회. _extractFullHistory 미래 연도 차단 1줄(if (yrInt > endYear) continue;) 추가.
  • OPSPhase 3·4·5 결선 대기 (JS stub) — 54-view-rowdata.js 끝에 V19 IIFE append. enterRowdata 진입 시 V19 모드 표시("Phase 3 데이터 결선 대기" placeholder), rd_setTab/rd_setBranch/rd_setPeriod/rd_open*Modal/rd_close*Modal 글로벌 노출. ESC 키로 4 모달 일괄 닫기. V18 IIFE 위 영역 (1393줄) 은 종합 탭 폐기와 함께 dead code 보존(Phase 5 정리).
  • TESTstaging 검증 (PASS) — node --check src/server.js OK / staging 재배포 후 V19 7 라우트 모두 정상: cost-classifications 90개 계정 자동 분류 (창고일반 기타 → semi-variable r²=0.52 cv=0.32 등) / cost-warnings 29건 워닝 (Critical 0 / Warning 14 / Info 15, notesMissingAged 14건) / cost-summary 3 카테고리 (fixed: 10 계정 1,260백만 / variable: 2 계정 평균 연동성 76% / one-time: 2 events) / siteDistribution {DWLG:81.4%, DWLT:13.8%, BNCC:4.8%} SITE_LABEL 매핑 정상 / cost-notes·overrides·history 빈 골격 + _etag. 부팅 시 /app/data/cost_notes.json·cost_overrides.json 자동 생성. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v20.6.
2026-05-04
V19.3 사업장 — 16T/25T/3.5T 카드 + 편의시설 사진 슬롯 + CSS 토큰 격리
  • FEAT장비 카드 3종 추가 (16T/25T/3.5T) — v16_renderEquip cats 배열에 16톤·25톤·3.5톤 추가. 매핑가이드 §5-2 와 일관 (유류비 모듈에 이미 등록됨). DWLG 엑셀 R49 코일발 적용장비="30톤, 16톤, 4.5톤" 으로 16톤 존재가 명시되어 있었으나 카드 누락 → 픽스. 카드 노출 순서: 큰 톤 → 작은 톤 (45T → 30T → 25T → 16T → 7T → 4.5T → 3.5T → 2.5T → 입식). 빈 데이터(0/-) 카드 자동 숨김(filter 단계).
  • FEAT편의시설 amenity 사진 슬롯 — FAC_AMENITY_SUBFOLDER 매핑(caf:'구내식당' / lng:'휴게실 라운지' / smk:'흡연구역' / shw:'샤워실' / gym:'체력단련장' / mtg:'회의실') + FAC_AMENITY_KEYWORDS 폴백 + v16_findAmenityImages / v16_updateAmenityImage 헬퍼 신규. v16_setAmenTab 진입 시 NAS 자산 캐시(/api/facility-assets/{site})에서 첫 매칭 사진을 hero 영역에 자동 노출. 다중 사진 시 +N장 칩. DWLG 12장(/편의시설/구내식당, 휴게실 라운지, 흡연구역) 기존 미노출 자산 활성화.
  • FIXCSS :root 토큰 글로벌 오염 픽스 (V18.7.6 사고 패턴 재발 방지) — :root[data-theme="dark"] / [light] 두 블록을 :root[data-theme="..."] #view-facility 스코프로 격리 + !important 일괄 제거. 전역 html,body/portal-hdr/menu-card,kpi-card,.../btn,theme-toggle-btn 등 6개 글로벌 셀렉터도 #view-facility 한정으로 변경. 이전: facility의 인디고 토큰(#818cf8)이 !important로 다른 뷰까지 덮어씀 → V18.7.6 사고 동일 패턴. 현재: 다른 뷰(손익/일일매출/매출분석/유류비) 토큰·디자인 영향 0, 원칙 11(JS 인라인 제어 속성 !important 금지) 부합.
  • DOCS운영팀 안내 문서 2건 신규 — 2.리팩토링_계획/14_DWLT_BNCC_시트_작성_안내.md (영업팀 시트 작성 마감 요청, 워크플로 + P1 항목 우선순위), 15_사업장_편집_사용자가이드.md (V19.1 facility-overrides 사용 가이드, 6 섹션 권장 입력값). prod 실측 (2026-05-04): DWLT/BNCC 시트 0행, overrides 사용 이력 0건 — 사용자 인지 보강 필요.
  • TEST검증 — node --check 60-view-facility.js OK / NFC/NFD 한글 path 정규화 처리 / 변경 파일 5개 (60-view-facility.js +60줄 / 60-view-facility.css 글로벌→스코프 격리 / HTML dev-row 신규 + V20.6 latest 강등 / 가이드 .md 2건). server.js 변경 0 → --include-server-js 불필요. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v20.6.
2026-04-30
V20.6 매출 분석 — ⚠ 추세↓ 워닝 룰 신설 (12M+3M slope 이중 분기, 사양서 §10 line 275 종결)
  • FEATs_slope(arr, n) 헬퍼 신규 — 시계열 마지막 n 포인트의 least squares 선형 회귀 기울기. x=0..n-1, 0인 월도 포함 (휴면월도 추세 신호). 분모 0 가드 (n<2 또는 모든 x 동일 시 0 반환 → slope 음수 분기 false → 격상 없음, 안전). 단일 헬퍼로 12M·3M 모두 처리 (n 인자만 차이, DRY). 57-view-sales.js s_clientSeries 직후 신규 12줄.
  • FEATdrop 분기 4 flag — 12M slope + 3M slope 이중 분기 — s_computeAnomalies drop 분기 (line 941 부근) 에 z-score 직후 추가. slope12 = s_slope(series, 12) 와 slope3 = s_slope(series, 3) 산출 → isLongDown (12M↓) / isShortDown (3M↓) / isBothDown (동반 하락) 3 flag + 반올림 slope 값 2개. drop 객체 구조 확장만 — recover/newCli/stop 영향 0, 기존 워닝 모달 4종 / 거래처 모달 / 변경이력 모달 5종 새 필드 무시 → 시각 회귀 0.
  • FEATsignals 칩 3분기 (동반 하락 / 장기 only / 단기 only) — s_renderExistingClients drop forEach (line 1278 부근) z-score 칩 다음에 추세 칩 추가. isBothDown → ⚠ 12M+3M↓ 동반 하락 (neg, 강한 신호) / isLongDown only → ⚠ 12M↓ (장기 둔화) (warn) / isShortDown only → ⚠ 3M↓ (단기 약세) (warn). z-score 📊 칩과 시각 일관 (neg/warn 톤). 단일 추세는 칩만, 격상 없음.
  • FEAT우선순위 격상 — isBothDown 1단계 (z-score 와 OR 결합) — escalated = x.isStatOutlier || x.isBothDown 조합. P3→P2 / P2→P1 / P1→P1 (z-score 와 동일 패턴). z-score AND isBothDown 동시 발생 시 OR 결합으로 격상 1번만 (이중 격상 방지). 단일 추세 (장기 only 또는 단기 only) 는 격상 없음 — 칩만 노출. score 가중치 추가 없음 — 격상 단독으로 정렬 효과 충분.
  • FEAT권장 액션 4단계 — isStatOutlier ? '긴급 접촉' : isBothDown ? '추세 점검' : pct < -60 ? '단가 재검토' : '방문 지정'. 순서 = z-score 가 통계적으로 더 강한 신호 (가장 최근 1점 vs 11M 분포) 라 우선, 추세는 3M+12M 모두 음수인 구조적 둔화 → 두 번째. 단일 추세 칩은 액션 변경 없음 (칩만 노출).
  • OPS변경 파일 4개 — server.js·docker-compose·smoke 변경 0 — src/js/57-view-sales.js (s_slope +12줄 / drop 분기 +8줄 / signals·격상·액션 +8줄, +28줄 net) / HTML dev-row 신규 + V20.5 latest 강등 / 사양서 2.리팩토링_계획/12_매출분석_v1_시안_사양서.md §10 line 275 ⚠ 추세↓ 미구현 → ✅ 완료 표기 / CLAUDE.md V20.6 통합 블록. --include-server-js 불필요. 사양서 §10 Phase 1~5 + 추가 보강 룰 모두 ✅ 종결 (단 📦 물량 -X% 는 박스 데이터 거래처 키 부재로 영구 보류). 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v20.5.
2026-04-30
V20.5 매출 분석 UX 폴리싱 — 칸반 ellipsis + ≤768px 반응형 + HOT 칩 대비도 + 모달 max-width
  • FIX칸반 거래처명 ellipsis — V20.4 신설 신규 잠재고객 파이프라인 칸반의 .lead .ln1 에 text-overflow:ellipsis 미적용 → 긴 거래처명("디더블유엘글로벌 부산터미널" 등)이 카드 폭 초과 시 줄바꿈/넘침. overflow:hidden + text-overflow:ellipsis + white-space:nowrap 3속성 추가. <div class="ln1" title="..."> title 속성으로 호버 시 전체 거래처명 툴팁 노출. 식별성 보존하면서 카드 폭 안정화.
  • FIX칸반 ≤768px 1컬럼 반응형 — V20.4 의 @media (max-width:1280px) 가 칸반 2컬럼만 처리 → 768px 이하 모바일/태블릿에서 2컬럼 그대로 유지되어 가독성 저하. 신규 @media (max-width:768px) 블록 — .pipe{grid-template-columns:1fr} + .funnel{flex-direction:column;align-items:stretch} + .funnel-stage::after{display:none} (좁은 폭에서 funnel `›` 화살표 숨김). 칸반 1컬럼 적층 + funnel 세로 stack.
  • FIXHOT 칩 대비도 강화 — V20.4 의 .tag.hot{background:var(--red-bg);color:var(--red)} 가 라이트 테마에서 --red-bg(rgba 10%) 옅은 분홍 배경 + 빨간 텍스트 조합으로 다른 회색 칩과 시각 구분 약함. border:1px solid var(--red) 빨간 보더 + font-weight:800 + letter-spacing:.3px 자간 추가. 다크/라이트 양쪽에서 4건의 🔥 HOT/NEW 칩 즉시 식별 가능.
  • FIXlead 모달 max-width 반응형 — V20.4 #pipelineLeadModal .modal 인라인 max-width:760px 고정 → 좁은 viewport 에서 좌우 여백 명시적 패턴 부재. max-width:min(760px,92vw) 로 변경 — .modal 글로벌 width:min(1180px,96vw) 와 일관 패턴. 어떤 viewport 에서도 좌우 4vw 여백 보장.
  • FEAT파이프라인 분류 룰 단순화 — 결과(X열) 단독 기준 — 사용자 요청 "결과(X열)값을 기준으로 정리, 비어있는 부분은 진행중". V20.4 의 _classifyPipelineStatus 가 결과(col 23) 외에 currentStatus(col 22)의 '진행' 포함 여부도 함께 분기 → 결과 채워져 있어도 currentStatus 가 "(진행) 견적 제출" 이면 inprogress 로 가는 케이스 존재. 결과 단독 분류로 단순화 — 결과 빈 값 → inprogress, 결과 채워짐 → 그대로 매핑(불성약→lost, 지연→delayed, 성약→won). 영업팀이 엑셀에 결과 입력하는 시점이 분류 단일 기준이 됨.
  • FIX워닝 분류 미세 매출 컷 (drop/recover) — 사용자 staging 검증 중 발견. (주)전산교역이 "회복 신호 거래처 1개사" 로 잡혔으나 12M 합산 0.0억 + 차트 모든 막대 0 (실제 last 가 0.001억 같은 미세값) → "회복인데 그래프에 안 보임" 모순. s_computeAnomalies 의 drop·recover 분기 조건 last > 0 → last >= 0.01 (100만원 floor) 변경. 의미 있는 거래 규모만 워닝으로 잡힘. stop(거래중단)·newCli(신규)는 영향 없음.
  • OPS변경 파일 4개 (server.js +1줄) — src/css/57-view-sales.css (line 168 ellipsis 3속성 + line 172 hot 칩 3속성 + @media 768px 블록 신규) / src/js/57-view-sales.js (line 709 modal max-width:min + line 933·948 워닝 floor + line 1507 ln1 title) / src/server.js (line 225-234 _classifyPipelineStatus currentStatus 분기 제거) / HTML dev-row 신규 + V20.4 latest 강등. server.js 변경 → --include-server-js 필요. docker-compose 변경 0. 모든 클라이언트 변경 #view-sales 스코프 한정 → 8개 기존 뷰 시각 회귀 0. 롤백 1명령: py scripts/deploy.py --target=prod --rollback-to v20.4.
2026-04-30
V20.4 매출 분석 — Phase 5 신규 잠재고객 파이프라인 (Sales Opp Pipeline xlsx 자동 참조)
  • FEATTier 4 두 번째 탭 실데이터 결선 — V20.0 정적 mockup (시노트랜스/머스크라인/CMA CGM/ONE Korea 등 사명 + HOT 칩 모두 하드코드, 19개 거래처) 제거. 영업팀이 매주 갱신하는 (요청) Sales Opp Pipeline Mgt__M월 N주차__부산본부_YYMMDD.xlsx 의 부산 시트 134행 자동 파싱 → funnel 4 stage + 칸반 4 컬럼 실시간 노출.
  • FEAT4단계 매핑 — 결과 컬럼 기반 — 시안의 4단계(접촉/제안/협상/계약임박) 가 실제 데이터(현재상황 col 22 = "(진행) 견적 제출"·"(종료) 결과 공지" 단 2 값)와 mismatch → 결과 컬럼(col 23) 기준 4범주(inprogress/won/delayed/lost) 로 변경. 134건 모두 funnel 노출 (진행중 9 / 성약 45 / 지연 32 / 불성약 48). 사업장 검출 = 비고(col 28) + 제목(col 3) 의 DWLG_/DWLT_/BNCC_/터미널/CFS substring 매칭 (DWLG 73 / DWLT 26 / BNCC 35).
  • FEATHOT 플래그 + 사업장 탭 필터 — 전주비 Status Update (col 29) = Yes 또는 New 인 행에 🔥 HOT/NEW 칩 표시. funnel·칸반 모두 헤더 사업장 탭 (ALL/DWLG/DWLT/BNCC) 변경 시 자동 재렌더. 칸반은 각 컬럼 상위 5건 + "… N건 더" 합산 footer 형태로 가독성 확보. meta footer 에 sourceFile + 갱신시각 + 표시건수/총건수 노출.
  • FEATlead 카드 클릭 → 상세 모달 (5번째 모달) — 칸반 lead 카드 클릭 시 #pipelineLeadModal 신규 모달 노출. 거래처명·영업담당(Lead/Sub)·거래구분(신규/기존)·거래방식(수의계약/입찰)·서비스구분(자사 인프라/협력)·목표매출·예상매출·현재상황·결과·전주비 Status 10행 grid + 비고 전문 (col 28) + 영업기회 설명 (col 12) 별도 영역 (white-space:pre-wrap, max-height:30vh). server.js parsePipelineXlsx 에 4 컬럼 (col 9·10·11·12) 추가 파싱. ESC/backdrop/✕ 닫힘 일관 패턴 (4 모달 → 5 모달).
  • FEAT최신 주차 자동 탐색 + chokidar 감시 — server.js findLatestPipelineFile() 헬퍼 — YY년 디렉토리 자연수 정렬 → 최신 → M월 → M주차 가장 최근 → glob (요청) Sales Opp Pipeline Mgt*.xlsx 첫 매치. 매주 새 주차 폴더 생성 시 chokidar 30s polling 으로 자동 감지 → SSE /api/sales-pipeline-watch 통해 클라이언트 자동 갱신. parsePipelineXlsx + mtime 캐시 (재파싱 최소화).
  • OPSdocker-compose 마운트 + server.js 변경 — 신규 마운트 /volume1/부산본부 통합문서관리/영업팀/(회의) 주간 영업회의 보고:/sales-pipeline:ro (prod busan-dashboard-server + staging busan-dashboard-staging 양쪽). environment PIPELINE_DIR=/sales-pipeline env 오버라이드. server.js +상수 + 헬퍼 + 2 라우트 (GET /api/sales-pipeline + /api/sales-pipeline-watch SSE) + chokidar + /health pipeline:true + 부팅 로그 (~150줄). 변경 파일 4개 (server.js / 57-view-sales.js +200줄 / smoke.py +2 항목 / HTML dev-row + V20.3 latest 강등). compose 변경으로 staging 먼저 down/up + 검증 → prod 승격. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v20.3.
2026-04-30
V20.3 매출 분석 — Phase 3 보강 (KPI ④ 모달 결선 + 매출 z-score) + 메모 변경이력 모달
  • FEATKPI ④ 모달 4 차트 실데이터 결선 — V20.0 정적 시안의 mockup 데이터 (142개사 / 35·52·68·90% Pareto / 1.61·1.27·0.96억 사업장 평균) 모두 제거. s_computeModalData(clients, ym, site) 헬퍼 신규 (~80줄) — ① 히스토그램 7 buckets (0~0.1/0.1~0.5/0.5~1.0/1.0~2.0/2.0~5.0/5.0~10.0/10.0+ 억) ② Pareto 13 sample 누적 % + 푸터 4 칩 동적 ③ 12M 거래처당 평균 듀얼축 (purple 평균 + accent 활성수) ④ 사업장별 평균 hardcoded bar 동적 교체 (DWLG/DWLT/BNCC/통합 4 row width % + 숫자 + 인사이트 텍스트). KPI 카드 4개 (평균/중앙값/최대/최소) 도 실데이터 산출.
  • FEAT매출 z-score 워닝 (drop 카테고리) — 사용자 요청 "단가 z-score" 가 raw 데이터에 수량 정보 없어 (Sales Details 의 Note = 텍스트 적요) 구현 불가능 → 매출 자체 z-score 로 대체. s_computeAnomalies drop 분기에 prev11 mean/std 기반 zScore 산출 + isStatOutlier flag (|z|≥2σ). s_renderExistingClients 에서 통계 이상치는 P3→P2 또는 P2→P1 1단계 격상 + score × 1.5 가중 + signals 에 📊 |z|=Nσ 통계 이상치 칩 추가. 권장 액션도 일반 "방문 지정" → "긴급 접촉" 으로 격상.
  • FEAT메모 변경이력 모달 — Tier 4 보강 표 행 ✎ 옆에 📜 버튼 추가. 4번째 모달 #memoHistoryModal 신규 — 거래처별 메모 set/clear history 시간 역순 (최대 50건) 표시. 시간/사용자/액션/before→after 4컬럼 테이블. GET /api/sales-notes/history?client=&limit=50 결선 — V20.2 server 라우트 100% 재활용. 3 모달 → 4 모달, ESC/backdrop/✕ 닫힘 일관 패턴.
  • FIXX-User-Name 헤더 ISO-8859-1 호환 — 한글 이름 (예: "철운") 이 X-User-Name 헤더에 직접 들어가면 HTTP 헤더 스펙(ISO-8859-1) 위반 → fetch throw 'String contains non ISO-8859-1 code point'. 사용자 staging 검증 중 발견 (메모 저장 실패). 클라이언트 4곳 (sales-notes 1 + facility-overrides 3) 모두 encodeURIComponent(userName), server.js _userFromReq 가 decodeURIComponent 안전 처리 (양쪽 라우트 공유 함수, 한 곳 fix). V19.1 부터 잠재 버그였으나 facility 편집 시 영문 이름이거나 미실행이라 미발견.
  • FIXKPI ④ 모달 인사이트 박스 + 푸터 mockup 잔존 — 사용자 staging 스샷 검증 시 발견. ① 인사이트 박스 셀렉터 .mod-grid:last-of-type .mod-sec:last-child > div:last-of-type 가 부모 .modal 의 마지막 div 가 .mod-foot 이라 매치 안 되던 버그 → siteCmp.nextElementSibling 으로 단순화. ② 푸터 "▲ 핵심 진단" / "📌 권장 액션" 은 plan 단계 누락으로 V20.0 mockup (14개사 / 43개사 / 52% / 35%) 그대로 잔존 → data.pareto.milestones 활용해 동적 갱신 (상위 10% 매출% + 상위 20% 80/20 룰 비교 + 의존도 톤 + 중간층 거래처수).
  • OPSserver.js 변경 5줄 (FIX 만) — V20.3 본 결선 자체는 server 변경 0 (Phase 3 + 메모 이력은 기존 인프라 재활용). 위 ISO-8859-1 핫픽스로 --include-server-js 필요. docker-compose 변경 0. 변경 파일 4개 (57-view-sales.js +200줄 / 60-view-facility.js +3 encode / server.js +5줄 decode / HTML dev-row 신규 + V20.2 latest 강등 + 2 FIX 항목). 사양서 §10 Phase 3 보강 ✅ + Phase 4 메모 이력 UI ✅ + Phase 5 NAS 경로 보강. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v20.2.
2026-04-30
V20.2 매출 분석 — 영업 메모 서버 영속 (Phase 4) + git 이력 정리
  • FEAT영업 메모 NAS JSON 영속 — 기존 window._salesMemos (메모리 only) → /app/data/sales_notes.json 영구 저장. 페이지 새로고침/세션 변경 후에도 거래처별 메모 보존. V20.1.7 까지 메모 입력해도 새로고침 시 사라지던 데이터 유실 문제 해결.
  • FEATPOST /api/sales-notes — V19.1 facility-overrides 패턴 100% 재사용. 원자적 쓰기 (tmp + renameSync) + 손상 감지 자동 백업 + If-Match etag 충돌 감지 (409) + history 누적 (최대 1,000건). server.js 라우트 3종 신규 (GET / POST / GET history).
  • FEATX-User-Name 추적 — facility-overrides 와 동일한 localStorage busan_facility_user_name 키 공유. 첫 메모 입력 시 사용자 이름 1회 prompt → 이후 모든 변경에 자동 첨부. history 에서 누가 언제 무엇을 변경했는지 감사 추적 가능.
  • FEAT진입 시 GET /api/sales-notes 자동 로드 — loadSalesData() 의 Promise.all 에 s_loadSalesNotes() 추가. PL/RAW 두 API 와 병렬 fetch → KPI 렌더 직전 SALES_MEMOS 객체 갱신 + SALES_NOTES_ETAG 저장. 메모 로드 실패 시 메모리 only fallback (사용자 알림 안 함, 콘솔 warn 만).
  • OPSgit 이력 정리 (트랙 A) — V19.1.2 commit 이후 V19.2.x ~ V20.1.7 까지 21개 버전이 commit 없이 prod 까지 배포되어 있던 상태. 단일 누적 commit + tag v20.1.7 부여 → --rollback-to v20.1.7 명령 정상 작동. 본 V20.2 배포 후 별도 commit + tag v20.2 부여 예정.
  • OPSserver.js +80줄 / 57-view-sales.js +50줄 / dev-row 신규 / 사양서 §10 Phase 4 ✅. SSE 추가 0 (단일 사용자 가정, facility-overrides 와 동일). docker-compose 변경 0. 롤백 1명령: python scripts/deploy.py --target=prod --rollback-to v20.1.7.
2026-04-30
V20.1.7 매출 분석 — Canvas reuse 차단 + SSE rowdata-watch 401 픽스 (Tier 5 로딩 영구 잔존 근본 해결)
  • FIXCanvas reuse throw 근본 해결 — 사용자 콘솔 분석: [sales] loadSalesData 실패: Canvas is already in use. Chart with ID '1' must be destroyed before the canvas with ID 'sp1' can be reused. 원인: s_drawSpark 가 이전 Chart 인스턴스 destroy 안 하고 재생성 → Chart.js throw → loadSalesData .catch 로 진입 → s_renderAll 미완료 → Tier 5 의 "📊 데이터 로딩 중…" placeholder (V20.1.6) 가 영구 잔존.
  • FIXs_drawSpark Chart.getChart(c) destroy 추가 — Chart.js v3+ 정적 메서드 활용. 기존 차트 인스턴스 자동 탐지 후 destroy. 다른 차트 (combo/modal/warn/client) 는 모두 인스턴스 reference 유지하며 destroy 했지만 sp1~sp5 sparkline 만 누락. enterSales 1차 더미 → loadSalesData 2차 → MutationObserver 테마 토글 → SSE refresh 등 모든 재렌더 경로 안전.
  • FIXSSE /api/rowdata-watch 401 Unauthorized — 사용자 콘솔: GET /api/rowdata-watch 401 두 번. 원인: PL SSE 는 ?pw=... query string 인증인데 RAW SSE 는 누락. EventSource 가 custom header 못 보내므로 query string 필수. '/api/rowdata-watch' → '/api/rowdata-watch?pw=' + encodeURIComponent(pw) 로 PL SSE 와 동일 패턴 통일.
  • OPS변경 파일 1개 (57-view-sales.js +6줄). server.js 변경 0. 사용자 권장: Ctrl+Shift+R 새로고침 후 콘솔 (F12) 에서 401 + Canvas reuse 에러 모두 사라진 것 확인.
2026-04-28
V20.1.6 매출 분석 — Tier 5 TOP/BOTTOM mockup fallback 차단 (race condition 방어)
  • FIXTier 5 mockup 거래처 노출 이슈 — 사용자 보고 "매출거래처 탑10과 감소율 거래처가 적용 안되었어" + 스크린샷에 V20.0 mockup (팬스타라인닷컴/씨엠씨로지스/한진해운로직스 등) 표시. 원인: enterSales 시 1차 더미 렌더 → SALES_DATA = null 상태에서 `var topData = TOP, botData = BOT` (mockup module constant) 사용 → 캐시 또는 race condition 으로 mockup 잔존.
  • FIXTOP/BOT mockup 데이터 자체 제거 — `var TOP = [...10개 mockup...]` / `var BOT = [...10개 mockup...]` → `var TOP = []` / `var BOT = []`. 24행 → 4행 (mockup 제거 -20줄). loadSalesData 의 `Array.prototype.push.apply(TOP, s_computeTop10(clients, 'ALL'))` 는 그대로 작동 (빈 배열에 push).
  • FEATs_renderBars 3-state 명확화 — `loaded = !!(SALES_DATA && SALES_DATA.clients)` 분기로 ① 미로드 = "📊 데이터 로딩 중…" placeholder ② 로드 + 빈 결과 = "해당 사업장 매출 거래처 없음" / "감소 거래처 없음 — 모든 거래처 안정적" ③ 로드 + 데이터 = 정상 막대 렌더. mockup fallback 경로 자체 차단.
  • OPS다른 mockup module constant (PLAN/ACT/WARN_DATA) 는 그대로 — 콤보차트 1차 더미 렌더 + 워닝 모달 정적 데이터 보존 (s_renderWarnCards/s_updateWarnData 가 실데이터로 교체). 향후 동일 이슈 발생 시 동일 패턴으로 제거 가능.
  • OPS변경 파일 1개 (57-view-sales.js -20줄 mockup + 5줄 placeholder). server.js 변경 0. 사용자에게 권장: Ctrl+Shift+R 새로고침 (브라우저 캐시 무효화 후 재진입).
2026-04-28
V20.1.5 매출 분석 — Tier 4 보강 표 행 클릭 시 거래처별 연단위 매출 흐름 모달 (3번째 모달)
  • FEAT거래처별 연단위 매출 흐름 모달 — 사용자 요청 "기존 거래처 보강의 거래처를 클릭하면 연단위 매출의 흐름". Tier 4 보강 표 어느 행이든 클릭 시 해당 거래처 단일 24개월 매출 분석 모달 (`#clientModal`) 오픈. KPI ④ 거래처 분포 / 워닝 공유 / 거래처 연단위 = 총 3종 모달.
  • FEATs_extractClientFull24M(rd, clientName) — `/api/rowdata-json` 의 `db.2025` + `db.2026` 두 해 풀 데이터에서 단일 거래처 24M 시계열 추출 (12 슬라이딩 윈도우와 독립). 반환: `{name, bySite:{DWLG:{y2025:[12], y2026:[12]},...}, total:{y2025, y2026}, dominantSite, siteSums, accNames}`.
  • FEAT3 차트 + 6 KPI 모달 구성: ① 연도별 월간 비교 막대 (1~12월, 2025 옅은색 / 2026 진한색) ② 사업장별 24M 분포 도넛 (DWLG/DWLT/BNCC) ③ 24M 시계열 막대 (25.01 ~ 26.12, 마지막 회계마감 월 강조). KPI 6장: 2025 총매출 / 2026 YTD / YoY 변화 / 월평균 / 최대월 / 최소월. 푸터에 트렌드 진단 (성장/감소/유지) + 주력 사업장 비중.
  • FEAT모달 헤더 동적 — 거래처명 + 주력 사업장 (색 강조) + 매출 적요 다양도 (예: "운임수익, 보관료수익 외 2종") 표시. 적요별 다양도가 영업 깊이 가시화.
  • FEATYoY 동기간 비교 — 2026 1~전월 누계 vs 2025 동기간 누계 % 변화. KPI 3번째 카드에 색상 (양수=초록, 음수=빨강) + 푸터 트렌드 진단 (`+10% 이상 = 성장 / -20% 이하 = 점검 필요 / 그 외 = 모니터링 또는 안정`).
  • UI표 행 인터랙션 — `` + `cursor:pointer` + `title="클릭 — 거래처 연단위 매출 흐름"` 툴팁. 메모 ✎ / 권장 액션 버튼 클릭은 `e.stopPropagation()` 으로 행 클릭 차단 (편집/액션과 모달 오픈 분리).
  • UI모달 닫기 일관 — 기존 2 모달과 동일 패턴: ESC 키 / backdrop (`.modal-bg`) 클릭 / ✕ 버튼. 닫을 때 차트 destroy + body overflow 복구. 다크/라이트 토글 시 열려있어도 `s_drawClientCharts(window._currentClientData)` 자동 재렌더.
  • OPS변경 파일 1개 (57-view-sales.js +220줄). server.js 변경 0. 글로벌 노출: `window.s_openClientModal` / `window.s_closeClientModal`.
2026-04-28
V20.1.4 매출 분석 — Tier 3 워닝 4분할 + Tier 4 보강 표 + 워닝 모달 실데이터 결선 (Phase 3 룰 엔진)
  • FIXTier 3 워닝 4 카드 mockup 데이터 결선 — 사용자 보고 "정합성 안맞는 더미 데이터". V20.0 시안의 하드코드 (한진해운로직스/씨엠씨로지스/케이씨티엘 등) → `s_renderWarnCards(anom)` 동적 렌더. count + 상위 3 거래처 미리보기 (마지막 거래월 / -X% MoM / 첫 거래월 / N개월 휴면) 자동 산출.
  • FIXTier 4 보강 표 mockup 데이터 결선 — V20.0 의 7행 하드코드 → `s_renderExistingClients(anom)` TOP 20 동적 렌더. P1/P2/P3 우선순위 점수 산출: 거래중단 = P1 전건 / 매출급감 = P1(12M ≥5억) · P2(≥1억) · P3 / 회복 = P2(12M ≥3억) · P3 / 신규 = P3. 같은 우선순위 내 매출 규모 × 감소율 가중. 자동 시그널 칩 + 영업 메모 + 권장 액션 (긴급 접촉 / 단가 재검토 / 방문 지정 / 감사 인사 / 환영 미팅 / 정기 컨택) 자동 결정.
  • FEATs_computeAnomalies 리팩토링 — count → 리스트 반환. `{stop:[], drop:[], newCli:[], recover:[]}` 각 거래처 객체 반환 (n/site/last12/lastSaleIdx/avg3/pct/dormantMonths/series 12M 시계열 포함). 매출 규모 큰 순 정렬. KPI ⑤ 도 `.length` 사용으로 일관.
  • FEAT워닝 모달 (4종) 실데이터 결선 — `s_updateWarnData(anom)` 가 `WARN_DATA[stop|drop|new|recover]` 4 엔트리 동적 갱신: 4 KPI (대상/매출 합산/평균 휴면 또는 감소율/P1) + 거래처 표 + 12M 합산 추이 + 사업장 분포 도넛 + 푸터. 카드 클릭 시 모달이 시안과 동일 구조로 실데이터 표시. 행 클릭 시 단독 거래처 chart12 보기 기능 그대로 유지 (V20.0 인터랙션 보존).
  • FEAT영업 메모 — 거래처명 기반 (메모리 only). V20.0 의 DOM-based 메모 (재렌더 시 손실) → `window._salesMemos = {clientName: memoText}` 거래처명 기반. site 탭 변경으로 재렌더 시에도 보존. `s_bindMemoHandlers()` 가 ✎ 클릭 시 prompt() → 즉시 시각 갱신 + 메모리 저장. Phase 4 server.js POST /api/sales-notes 영속 결선 예정.
  • FEAT날짜 헬퍼 신규 — `s_idxToDate(idx)` (12 슬라이딩 윈도우 인덱스 → 'YY.MM' 표기) + `s_daysSinceMonth(idx)` (해당 월 마지막일 부터 오늘까지 일수). 거래중단 카드 / 모달 / 보강 표에서 "마지막 26.01" / "120D 무거래" 등 자동 산출.
  • UIHTML marker 추가 — `data-warn-type="stop|drop|new|recover"` (4 워닝 카드) + `id="existingClientsTbody"` (보강 표) + `id="existingBadge"` (탭 카운트). YTD 매출 컬럼 라벨 → "12M 매출" 정확 명칭. 빈 결과 시 친근 메시지 ("해당 거래처 없음" / "우선순위 영업 대상 없음 — 모든 거래처 정상").
  • OPS변경 파일 1개 (57-view-sales.js +260줄). server.js 변경 0. site 탭 변경 시 워닝 4 카드 + 보강 표 + 모달 데이터 모두 자동 갱신 (`s_updateKpiCards` 마지막에 3 render 함수 호출).
2026-04-28
V20.1.3 매출 분석 — KPI 5장 정확성 보강 (전월=2월 / 컬러 룰 일관 / 신규·이탈·이상 동적 산출 / 다중 사업장 거래처)
  • FIXKPI ② 전월 = 2월 (3월 아님) — 사용자 보고 "당월이 3월이면 전월은 2월". `act[site][11]` (3월) → `act[site][10]` (2월) 로 변경. 라벨 "전월 매출 (3월)" → "전월 매출 (2월)". 전월 대비 비교도 (1월 vs 2월) 로 시프트. KPI ① "당월(3월)" 과 의미 명확 분리.
  • FIXKPI ① ② 컬러 룰 일관화 — 사용자 보고 "94.2% 둘 다인데 한쪽 초록 한쪽 주황". `s_achvColor(achv)` 공통 헬퍼: ≥100% 초록 / ≥90% 노랑 / <90% 빨강. KPI ① 의 hardcoded `class="val pos"` 도 동적 컬러로 교체.
  • FEATKPI ③ 신규 진입 / 이탈 동적 산출 — V20.0 mockup 의 하드코드 +7/-3 → `s_computeChurn(clients, site)` 신규. 신규 = 3월 매출 있음 + 2월 매출 없음 / 이탈 = 2월 매출 있음 + 3월 매출 없음. 80/20 집중 관리 = 활성 거래처 × 0.2 동적. 게이지 = 활성/전체 비율, 바 pill = net (신규-이탈).
  • FEATKPI ④ 사업장별 정확 변화 — `s_extractClientSeries` 재구성: 거래처별 `bySite: {DWLG:[12M], DWLT:[12M], BNCC:[12M]}` per-site 시계열 + `totalSeries` 통합 + 주력 사업장 = 12M 합산 최대 사업장. `s_clientSeries(client, site)` 헬퍼로 site 별 시계열 추출. 다중 사업장 동시 거래 거래처 정확 분리.
  • FEATKPI ⑤ 이상 거래처 동적 산출 (Phase 3 정식 룰 엔진 전 기초) — V20.0 하드코드 11건 → `s_computeAnomalies(clients, site)` 신규. 중단 = 직전 3M 평균 0.5억+ 이지만 가장 최근 0 / 급감 = 가장 최근 < 직전 3M 평균 × 0.5 / 신규 = 가장 최근 > 0 + 그 이전 11M 0 / 회복 = 가장 최근 > 0 + 직전 3M 0 + 그 이전 이력 있음. P1 즉시 조치 = 중단 전건 + 급감 절반 추정.
  • FEATTOP/BOT site 인자 추가 — `s_computeTop10/Bot10(clients, site)` 가 `s_clientSeries(c, site)` 사용. 다중 사업장 거래처가 site 탭별 정확한 매출 합산으로 표시. 거래처 row의 `site` 속성도 site === 'ALL' 인 경우만 주력 사업장, 외에는 선택된 사업장.
  • OPSbackward compat — `c.series` = `c.totalSeries` alias 유지 (워닝 모달 등 기존 코드 영향 0). 변경 파일 1개 (57-view-sales.js +180줄).
2026-04-28
V20.1.2 매출 분석 — 헤더 사업장/기간 탭 실동작 결선
  • FIX헤더 탭 dead 이슈 — V20.0 시안에서 헤더 사업장 탭(#siteTabs 통합/DWLG/DWLT/BNCC)과 기간 탭(#periodTabs 당월/YTD/분기/전년동기)이 시각 토글만 하고 데이터 필터는 미연결. 사용자 보고 "버튼 선택시 반영안됨". s_bindTabs() onclick 에 데이터 필터링 로직 추가.
  • FEAT사업장 탭 (4종) — KPI ① ② ③ ④ + 콤보차트 + sp1·sp2 스파크라인 + Tier 5 TOP·BOTTOM 10 모두 해당 사업장으로 필터. 라벨 우측에 [DWLG] 표기. 사업장 게이지 3분할은 cross-site 비교 의도라 그대로(필터 영향 없음). 콤보 모드(#combMode) 와 양방향 sync — 한 쪽 클릭 시 다른 쪽 시각 자동 동기화.
  • FEAT기간 탭 (4종) — s_periodWindow(period, ym, labels) 신규 헬퍼가 12 라벨 인덱스 윈도우 산출. ① 당월 = 전월 1개월만 (라벨 "당월(3월)") ② YTD = 1월~전월 누계 (기본, 라벨 "3월 누계") ③ 분기 = 가장 최근 완료 분기 (라벨 "Q1 누계") ④ 전년동기 = 당년 1~전월 + 전년 동기간 비교 (KPI ① 두 번째 foot 에 YoY% 표시).
  • FEAT거래처 site 필터링 — Tier 5 TOP/BOTTOM 이 SALES_DATA.clients 에서 currentSite 로 미리 필터링 후 s_computeTop10/Bot10 재호출. 빈 결과 시 친근한 메시지 ("해당 사업장 거래처 데이터 없음" / "감소 거래처 없음"). KPI ③ 활성 거래처 카운트도 site 필터 적용.
  • OPSs_rerenderForFilters() 신규 — 사업장/기간 탭 변경 시 KPI 5장 + 콤보 + 스파크 + TOP·BOT 일괄 재렌더. SALES_DATA 미로드 시 더미 데이터로 폴백. 모듈 상태 currentSite + currentPeriod 추가 (기존 currentMode 와 양방향 sync).
  • OPS분기 윈도우 산출 — ym.month % 3 === 0 (3/6/9/12) 이면 그 분기 사용, 아니면 직전 완료 분기. 1·2월 효과 월(시스템 2·3월) 시 전년 Q4 자동 시프트.
2026-04-28
V20.1.1 매출 분석 — 회계마감 기준 월(전월) 적용
  • FIX당월 미마감 이슈 — 4월 시점에 4월 매출은 회계마감 전이라 미완성. s_currentYearMonth() 헬퍼가 항상 전월 반환하도록 수정 (`d.setDate(1); d.setMonth(d.getMonth()-1)`). 모든 KPI/게이지/무결성 배너/TOP·BOT delta 가 이 함수 1곳을 통과하므로 한 줄 수정으로 일괄 적용.
  • UIKPI 라벨 갱신 — "당월 매출 (4월)" → "전월 매출 (3월, 회계마감 기준)". YTD 매출 라벨은 기존 동적 (`(X월 누계)`) 이라 "(3월 누계)" 로 자동 변경. 사업장 게이지 메타도 자동 (`2026-01 ~ 2026-03 누계`).
  • FEAT12 슬라이딩 윈도우도 자동 -1 month 적용 — 시안의 `25.05~26.04` → `25.04~26.03` 으로 시프트. 콤보차트 X축, 거래처 시계열, 모든 후속 계산이 회계마감 기준 정합.
  • OPS월말 일자 차이 회피 — `setDate(1)` 선행으로 31일에 setMonth(-1) 시 fallback (예: 3/31 → 2/28 자동 보정) 방지. 매월 1일~31일 어느 일자에 진입해도 동일한 전월 결과.
2026-04-28
V20.1 매출 분석 — Phase 2 데이터 결선 (사업계획 vs 회계마감 + 거래처 TOP·BOTTOM 실데이터)
  • FEATloadSalesData() 빈 함수 → 두 기존 API 병렬 fetch — App.data.pl.ensureLoaded() (50-view-pl 의 RAW 공유) + apiFetch('/api/rowdata-json'). 신규 server.js 라우트 0, 기존 인프라 100% 재활용.
  • FEATApp.data.pl 공유 노출 — 50-view-pl.js IIFE 마지막에 window.App.data.pl = {RAW, YEARS, TEAMS, ensureLoaded(), reload()} read-only getter 추가 (+12줄). 57-view-sales 가 손익 PL 데이터 직접 read 가능. PL 뷰 단독 동작 영향 0.
  • FEAT헬퍼 5종 신설 — s_extractPlan(plRaw)(PL row → {ALL/DWLG/DWLT/BNCC: [12M]}) · s_extractAct(rd)(rowdata-json db → 동일) · s_extractClientSeries(rd)(거래처별 12M 시계열 + 사업장 + delta) · s_computeTop10/Bot10(YTD 매출/감소율 sort).
  • FEATKPI 5장 + 사업장 게이지 3분할 실데이터 결선 — data-kpi-id="ytd|monthly|active|avg|anomaly" marker 추가 + s_updateKpiCards/s_updateSiteGauges 가 YTD 누계, 당월/MoM, 활성거래처(당월 매출>0), 거래처당 평균, 사업장 달성률(실적/계획) 동적 계산. KPI ⑤ 이상 거래처는 Phase 3 워닝 룰 결선 전 더미 유지.
  • FEATSSE 자동 갱신 2개 — App.api.connectSSE('/api/watch?pw=')(PL 파일 변경) + '/api/rowdata-watch'(RAW 변경). 두 SSE 모두 view-sales 활성 시에만 처리 + onerror 시 server-badge "감시 재연결 중" 톤 전환. 원칙 8 헬퍼 경유 (직접 EventSource 금지).
  • FEAT무결성 배너 — V18.7 일일매출 패턴 차용. 손익 PL 매출 합계(r.as_ 누계) vs RAW 비용분석 매출 합계(db.YYYY.total.Sales 누계) 차이 ±5% 초과 시 노란 배너(`#s-integrity-banner`) 동적 삽입. ±5% 이내 → 미표시.
  • FIXMONTHS_24 하드코드 라벨 → s_months24Labels() 동적 슬라이딩 (current month back 12). enterSales 시점이 5월/6월/...로 변하면 콤보차트 X축 자동 갱신.
  • UIs_refresh() 버튼 동작 변경 — 차트 재렌더만 → loadSalesData() (실제 두 API refetch). 첫 진입 시 1차 더미 즉시 렌더 → 2차 실데이터 fetch 후 자동 재렌더 (UX, 기다림 0). server-badge "loading" → "connected" 톤 전환.
  • OPSserver.js 변경 0 — 사용자 결정(2026-04-28). deploy.py --include-server-js 불필요. 롤백 1 명령: python scripts/deploy.py --target=prod --rollback-to v20.0.
  • OPSSSOT — 2.리팩토링_계획/12_매출분석_v1_시안_사양서.md §10 Phase 2 체크박스 4/4 완료 · CLAUDE.md V20.1 블록 추가. Phase 3 워닝 룰 엔진 6종 + 모달 거래처 단독 보기 차세대 결선 가이드 유지.
2026-04-28
V20.0 매출 분석 (Sales Analysis) 신설 — 5단 Tier 구조 + 모달 2종 (시안 v1 정적 포팅, Phase 2~5 데이터 결선 예정)
  • FEAT사이드바 운영관리 > 매출 분석 메뉴 신설 (비용분석 위) + 표준 진입점 App.view.enterSales. 매출(top-line) → 비용분석 → 현금흐름 자연스러운 재무 흐름.
  • FEAT5단 Tier 구조 — Tier 1 KPI Hero × 5(YTD/당월/활성거래처/거래처당평균/이상알림) · Tier 2 사업계획 vs 회계마감 12M 콤보(달성률 점 색상) + 사업장 3분할 게이지 · Tier 3 거래처 이상 4분할 워닝(중단 3/급감 5/신규 7/회복 4) · Tier 4 영업대상 탭(기존 보강 P1~P3 표 + 신규 잠재 칸반 4단계) · Tier 5 거래처 분해 TOP/BOTTOM 10.
  • FEAT모달 2종 — 거래처 분포 상세(KPI ④ 클릭, 히스토그램 + Pareto + 12M 추이 + 사업장 비교 + 핵심 진단/권장 액션) · 워닝 공유 모달(Tier 3 4 카드 클릭, WARN_DATA[type] 동적 재구성 — 4 KPI + 거래처 표 + 12M 합산 막대 + 사업장 도넛 + 푸터 진단). ESC + 외부 backdrop 클릭 닫기.
  • FEAT자동 시그널 + 영업 메모 하이브리드 — 셀 윗줄(자동 시그널 색칩: 📉/🛑/🔄/📊 z-score/📦 물량/⚠ 추세) + 셀 아랫줄(영업 메모 ✎ 클릭 편집). Phase 1 메모리 only(prompt), Phase 4 V19.1 OVERRIDES 패턴 영속 예정.
  • UI스코프 격리 — 모든 CSS 셀렉터 #view-sales 하위, 함수 s_ prefix, CSS var --s-blue/--s-purple(+bg) 4종만 로컬 정의. V18.7 A-1 준수: :root 토큰 추가 0, 글로벌 토큰(--site-*, --pos/neg, --green/yellow/red) 재사용.
  • UI한국 회계 컨벤션(--pos 빨강 / --neg 파랑) + 사업장 색(DWLG 청록 / DWLT 호박 / BNCC 자홍) 그대로. 1280px 이하 반응형 — Tier 1 5장→3장 / Tier 3 4장→2장 / Tier 2·5 가로→세로 / 칸반 4컬럼→2컬럼. 모달 1100px 이하 grid 1fr.
  • UI다크/라이트 자동 추종 — MutationObserver 가 data-theme 변화 감지 시 콤보·sparkline·TOP/BOTTOM·열려있는 모달 차트 일괄 재렌더 (Canvas 색은 getComputedStyle().getPropertyValue('--*') 런타임 해석).
  • OPSv2 진입점 사전 확보 — loadSalesData() 빈 함수 + SALES_DATA 상수 자리 코멘트. Phase 2 server.js /api/sales-overview 응답으로 Object.assign 한 줄 교체.
  • OPS스모크 테스트 16번째 항목 — 빌드 산출물에 id="view-sales" + App.view.enterSales 동시 포함 정적 검증(server.js 무수정 단계 — Phase 2 결선 시 API GET 으로 교체 가능).
  • OPSSSOT — 2.리팩토링_계획/12_매출분석_v1_시안_사양서.md §10 Phase 1 체크리스트 5/5 완료 · 13_매출분석_차세대_지시문.md 가 다음 세션 Phase 2~5 진입 가이드. mockup_매출분석_v1.html 폐기 0(시안 SSOT 보존).
2026-04-28
V19.2.8 사업장 기본정보 — 헤더 1줄 통합 (2줄 → 1줄)
  • UI2줄짜리 상단 헤더(portal-hdr 제목줄 + v16-tbar 브랜드/사이트탭/편집/뱃지 줄)를 1줄로 통합 — 세로 공간 약 60px 절감. 사용자 보고 "두번째줄 부산본부 대시보드 삭제 + 사업장 클릭 버튼/편집/SERVER HEALTHY 모두 첫째줄로".
  • UI제거: v16-tbar 컨테이너 · v16-brand("부" 로고+"부산본부 대시보드"+"integrated ops v1.6") · v16-link.current("사업장 정보" 칩, 제목과 의미 중복) · v16-div 구분선. 이동: v16-sgrp(DWLG/DWLT/BNCC 탭) + v16-edit-toggle(✎ 편집) + v16-pill(● SERVER HEALTHY) → portal-hdr 우측 신규 v16-hdr-right flex 컨테이너로.
  • UI반응형 — portal-hdr 의 기존 flex-wrap:wrap 활용으로 좁은 폭에서 우측 그룹 자동 줄바꿈. 추가로 @media (max-width:1280px) 에서 사이트 버튼·편집·뱃지 폰트 1~2px 축소 + "사업장" 라벨 칩 숨김.
  • FIX.v16-2col min-height:calc(100vh - 120px) → calc(100vh - 62px) 보정 (2줄 헤더 → 1줄 헤더 높이 변화 반영).
  • OPSJS 무수정 — f-tab-DWLG/DWLT/BNCC·v16-edit-toggle·f-theme-btn ID 그대로 보존하여 f_setSite/v16_toggleEditMode/toggleTheme 영향 0. 죽은 셀렉터(v16-tbar/brand/blogo/btt/bsb/link/div/tright) CSS 동시 정리.
  • OPSportal-hdr 글로벌 클래스(30-layout.css:14-22)는 일일매출·인원·유류비·비용분석·현금흐름 등 다른 뷰가 공유하므로 무수정 — 이번 변경은 #view-facility 스코프에 한정. 타뷰 시각 회귀 0.
2026-04-28
V19.2.7 본부 일정 슬라이드쇼 — 파일명 구분자 _ / - / . / 공백 모두 허용
  • FIXhqParseTitle() 정규식 ^(\d{6})_(.+?)… → ^(\d{6})[_\-.\s]+(.+?)… — 운영팀이 260417-MBC 생방송 부라보 방영1.jpg 처럼 하이픈으로 작성한 파일이 date:'' 로 파싱 실패하여 슬라이드쇼 필터(line 2442 if (!it.date) return false)에서 즉시 제외되던 이슈 해결.
  • FIX4/17 MBC 부라보 방영 사진 3장 슬라이드쇼 정상 노출 확인. 향후 운영팀 파일명 자율성(언더스코어/하이픈/점/공백) 확보로 동일 사고 재발 차단.
2026-04-28
V19.3 현금흐름 (Cash Flow) 통합 재무 대시보드 신설 (v1 시안 1:1 이식, 데이터 연동은 v2 별도)
  • FEAT사이드바 운영관리 > 현금흐름 메뉴 신설 (비용분석 다음, 유류비 점검 위) + 표준 진입점 App.view.enterCashflow.
  • FEAT3탭 구조(SUMMARY / DWLG / BNCC) — 시안 박은경 사원 작성 부산본부_통합재무대시보드.html 1:1 이식. SUMMARY 탭 사업장별 KPI 비교 + 통합 현금흐름 차트(spanGaps 정렬) + 예금·대여·차입 합계 카드 3개.
  • FEAT차트 8개 (Chart.js 기존 로딩분 재사용) — 통합 잔액 라인 + DWLG/BNCC 잔액 라인(빨간 임계선) + DWLG/BNCC 손익(매출·원가 막대 + 영업·순이익 라인 듀얼축) + DWLG/BNCC 안정성 비율(좌 유동/우 부채·차입). 모든 축·텍스트 색은 chartTick/chartTxt 헬퍼 적용 (원칙 2, 다크 자동 대응).
  • FEAT4종 모달(KPI 상세 표 6개월 / Fund 예금·대여·차입 9컬럼 표 + D-day 배지 / PL 월별 손익 / CF 일자별 입출금·잔액·리스크 배지). 배경 클릭 + ESC 키 닫기.
  • FEATA4 인쇄 모달 — canvas 3x 재렌더 캡처(저해상 픽셀화 방지) + 편집 가능 부제(contenteditable) + 펀드 표 3섹션. @media print 로 사이드바·topbar 숨김 + body.cf-print-mode 토글.
  • UI다크 테마 자동 매핑 — [data-theme="dark"] #view-cashflow 셀렉터로 surface/text 토큰만 부드럽게 어두워짐. 글로벌 :root 미건드림(V18.7 사고 재발 방지).
  • UI스코프 격리 — 모든 CSS 셀렉터 #view-cashflow 하위, id cf- prefix, 함수 cf_ prefix, CSS var --cf- prefix. 토큰/이름 충돌 0.
  • UIv2 진입점 사전 확보 — loadCashflowData() 빈 함수 + CF_DATA / PL_DATA / FUND_DATA 데이터 상수 자리 주석. 추후 server.js /api/cashflow-info 응답으로 Object.assign 한 줄로 교체.
2026-04-27
V19.2 사업장 기본정보 신 엑셀 + 보안 통합 (V19.2 ~ V19.2.6 누적)
  • FEAT신 엑셀 양식 동기화 — 대시보드 사업장 기초정보_V1.xlsx (시트=사업장 구조, DWLG 174행). FACILITY_PATH env 오버라이드로 1명령 롤백 가능.
  • FEAT일반현황 8행(R3-R10) + 창고 세부 사항 표(R12-R17 동별 면적·내용) + 자체소방대 트리 조직도(대장→부대장→지휘조직/현장대응조직 8역할).
  • FEAT부속장비 3종 추가 (코일발 / 지게차 연장발 / 컨테이너 발판) — 총 부속 카드 6개.
  • FEAT엑셀 ⑤보안 섹션 자동 표시 (CCTV 대수·운영시간·보관기간·보안업체·야간순찰·순찰프로그램·비상연락망 7항목). v16_facSection 일반화 헬퍼로 향후 ⑥주차/⑦IT 등 확장 가능.
  • FEATNAS Assets 서브폴더 자동 매칭 — server.js facility-assets 재귀 스캔(depth ≤ 3). Assets/{site}/보안/CCTV.png 같은 서브폴더 사진을 보안 카드에 자동 노출. 6섹션 매핑(보안/주차/IT/에너지/타임카드/회의실).
  • UI보안 카드 2컬럼 grid (왼쪽 데이터 + 오른쪽 CCTV/보안 이미지) + 행 라벨 130px 고정/값 왼쪽 정렬로 가독성 개선.
  • UI현장사진 그리드는 루트 파일만 표시 (서브폴더 사진은 섹션별 카드 전용으로 분리).
  • UI엑셀 매칭 데이터 있는 섹션은 NAS JSON '권장 항목' 카드 자동 숨김 (중복 표기 제거). 자유 항목 카드는 유지.
  • UIDWLT/BNCC 시트 추가 전 안내 배너 (4섹션 통합) — override 6섹션은 시트 부재와 무관하게 정상 동작.
  • FIX일반현황 '구조' substring 매칭 사고 제거 — '응급구조' 항목 멤버 이름이 잘못 노출되던 버그.
  • FIXSMK(제연·방화셔터) 카드 우상단 수량이 '화재 수신기 연동'으로 잘못 표시되던 substring 매칭 버그 → '방화셔터 수량' 정확 매칭(2 개소).
2026-04-27 2026-04-27 V19.1.2
소재지 지도 자연 비율 표시
  • UI소재지 지도 height:180px 고정 → 이미지 자연 비율 (.has-map-img 클래스, BNCC wide 지도 잘림 해결)
2026-04-27 2026-04-27 V19.1.1
사업장 자산 사진 API
  • FEATserver.js /api/facility-assets/:site + /facility-img 정적 서빙 추가 — 3 사업장 (DWLG 7/DWLT 4/BNCC 3 이미지) NAS Assets 폴더 직접 노출
  • FEAT소재지 지도 자동 표시 — 00_ 지도 파일 매칭 시 v16-map 영역에 img 교체
  • FEAT현장사진 자동 캡션 — v16_parseAsset 가 숫자 prefix 제외 후 파일명을 제목으로
2026-04-27 2026-04-27 V19.1
V19.1 사업장 편집 오버레이
  • FEAT6개 예비 섹션(보안·주차·IT·에너지·타임카드·회의실) 활성화 — 권장 스키마 + 자유 row
  • FEATserver.js 첫 POST 라우트 도입 — facility-overrides GET/POST/revert/history
  • FEAT편집 모드 토글 + 인라인 수정 + 변경이력 + ↺ 되돌리기
  • OPS/app/data/facility_overrides.json 영속 (mtime If-Match 동시성)
  • OPSsmoke 15/15 staging+prod
2026-04-27 오늘
손익현황 onclick 회귀 픽스 (V17 Phase3 노출 누락 3종)
  • FIXapplyPreset 글로벌 노출 추가 — 상반기/하반기/전체/Q1~Q4 토글 버튼 ReferenceError 해소
  • FIXreloadAndRender 글로벌 노출 추가 — 연도 select onchange 정상화
  • FIXrefreshData = refreshPlData 별칭 추가 — 숨김 새로고침 버튼 + 에러 재시도 버튼 dead reference 해소
2026-04-26 오늘 · V19.0 유류비 V3.0 — 디자인 + 파서 정정 + 토글 활성화
유류비 점검 V3.0 — mockup_v2 디자인 포팅 + parseFuelSheet 5단계 헤더 파서 + 패널 토글 활성화
  • FEAT3-tier 레이아웃 재정렬 — Tier 1 (핵심 KPI 4 + 24개월 콤보차트) → Tier 2 (워닝 카드 3분할 + 단가 spread + 사업장 도넛) → Tier 3 (사업장 4카드 + TOP 이상치). 기존 12섹션 동일가중 → 위계 명확화.
  • FEAT핵심 KPI 4 재구성 — 매출액 대비 유류비 비중(%) · 월 예산 대비 편차(±%) · 단가 스프레드(본부 vs 시장) · YoY YTD 누적 증감액. 1·4번은 server B-2 응답 확장 후 활성, 2·3번은 B-1 데이터로 즉시 표시.
  • FEAT점검 5대 원칙 모달화 — 항상 펼친 룰박스 200px 점유 → 1줄 칩 + 클릭 시 모달. 5원칙(4-baseline/단가·수량 분리/z-score/인과분해/신뢰도 점수)을 번호 카드 + 태그로 표현, ESC/외곽클릭/✕ 닫기.
  • FEAT필터 바 button group — 기존 select 드롭다운 → 사업장/연도/월 모두 button 그룹(.f3-fb on/off 토글). 일일매출과 일관된 패턴.
  • FEAT워닝 배너 동적 톤 — Crit/Warn/Info 0건이면 녹색 톤("정상 범위") + ✓ 아이콘, ≥1건이면 빨간 톤 + ⚠ 아이콘 + 최상위 워닝 자동 노출. "최대 z" 통계 칸 추가.
  • FEAT워닝 모달 record-level 분기 — TANK_OVER/CYCLE/DUP_DAY 같은 레코드 단위 워닝은 12개월 합산 시계열 대신 당월 동일 장비 일자별 주유 막대 + 탱크용량 점선 으로 표시. 핵심지표 5셀도 룰별 구성(주유량·탱크·활용률·단가·비용 / 평균주기·최소주기·단축사례 / 일자·동일일횟수·합계). 12개월 합산 룰(PRICE_Z·MoM)은 기존 시계열 유지.
  • FEAT패널 토글 활성화 — 단가 스프레드 [사업장별/합산], 도넛 [₩비용/L수량], TOP [탱크활용률/비용/주유량] 토글 핸들러 추가. FUEL.spreadMode/donutMode/topMode + f3_setSpreadMode/f3_setDonutMode/f3_setTopMode 글로벌. 데이터 재계산 없이 차트만 리렌더.
  • FEAT사업장 4카드 본부 합산 추가 — 본부 합산(보라) + DWLG(시안)/DWLT(핑크)/BNCC(앰버). 평균단가/총비용/주유량/이상건수 4셀 + 점유율%, 단가 임계값 기반 자동 색상.
  • FIXparseFuelSheet 5단계 헤더 파서 (server.js 핵심 패치) —
    1. vehicle+suffix: 4.5톤(7325)-BNCC, 16톤(7124)-부원글로벌 → name="4.5톤", id="7325" (사업장/소속사 접미사 무시)
    2. driver: 조문성(4.5T)-04나7459 → name="조문성(4.5T)", id="04나7459" (운전자별 차량 unique)
    3. paren: 45톤장비(8711), 30톤(6395), 리치(01-1234)
    4. dash: 리치-04나8120, 30T-0004가6935, 3.5T-004나3796
    5. aggregate skip: 합계/소계/총계/취합/계/total/sum 패턴은 continue 차단
    분기 룰: driverWithVehicle 매칭 시 첫 그룹이 차종 키워드 포함 여부로 vehicle+suffix vs driver 자동 판단. 이전 버그: paren 만 매칭해 DWLG 16명 4.5T 운전자가 모두 id="4.5T" 로 합쳐졌고, BNCC 4.5톤 5대가 id="BNCC" 로 합쳐져 동일일 다중 주유 거짓 양성 8건 발생.
  • FIXFUEL_CAPACITY 키워드 보강 — 25톤(300L) · 16톤(200L) · 3.5T·3.5톤(70L) 추가. BNCC 의 25톤장비/16톤, DWLG 의 3.5T 차량 정상 인식. server.js + 55-view-fuel.js 양쪽 동기화.
  • FIX전역 토큰 오염 방어 — mockup의 .kpi-card/.warn-card/.panel/.modal 등이 20-components.css 글로벌 클래스와 충돌 → 전부 .f3- prefix + #view-fuel 스코프로 격리. 51-view-daily의 :root 오염 사고 재발 방지.
  • FIXKPI 미니 sparkline canvas 늘어짐 차단 — .f3-kpi-mini-wrap{height:32px;position:relative} + canvas position:absolute;inset:0!important. Chart.js maintainAspectRatio:false 사용 시 부모 컨테이너 고정 높이 필수.
  • FIX클라이언트 isAggregateRow 2차 방어선 — server 차단 후에도 캐시/구버전 데이터 대비 filterRecords 에서 합계/소계/총계/취합 이름 + 미확인 id 의 비정상 주유량 행 제외.
  • OPSsrc/js/55-view-fuel.js 805→1041줄 (Hero KPI / 24M 콤보 / 워닝 모달 record-level 분기 / 패널 토글). 데이터 레이어(loadFuelData/computeAll/detectFuelWarnings)는 B-1 그대로.
  • OPSsrc/css/55-view-fuel.css 210→255줄. 글로벌 토큰 사용 + 반응형 1280/900 브레이크포인트.
  • OPSsrc/server.js 830→849줄 (parseFuelSheet 5단계 패턴 + AGG_HEADER_RE 차단 + isVehicleTypeName 헬퍼 + FUEL_CAPACITY 키워드 추가). --include-server-js 배포 + 컨테이너 재시작 필수.
  • OPSSSOT: 1.기초정보/09_유류비_V2_점검모드.md V3.0 격상 · 1.기초정보/05_데이터매핑_가이드.md §5 헤더 파서 5단계 추가. 결정사항: 2.리팩토링_계획/10_유류비_결정사항.md. 시안: mockup_유류비_v2.html. B-2 인계: 09_유류비_B2_사양서.md.
  • OPS검증: prod (3001) 데이터 222건, unique 장비 22개(DWLG 14 + DWLT 3 + BNCC 5), DUP_DAY 거짓 양성 0건, 사업장 평균단가 1,836~1,840원/L 정상.
2026-04-26 V18.7.6 일자 정확매칭 + 라벨
일일매출 V18.7.6 — 4/31 가짜 일자 차단 + 일별 매출 막대 위 백만 단위 라벨
  • FIX가짜 일자 4/31 표시 제거 — V18.7.x 의 days = Array.from({length:31}) 가 모든 월에 31일 강제 표시 + endsWith('-' + d) 의 우연 매칭으로 4/31에 잘못된 매출 표시되던 이슈. d_monthDays(year, month) = new Date(year, month, 0).getDate() 헬퍼로 실제 월 일수만 (4월=30일), 매칭은 x.날짜 === d_datePrefix(year, month) + d 정확 매칭으로 변경. 통합뷰 stacked + 사업장 단독 + HKNC 모두 적용.
  • FEATDWLT/BNCC 단독 차트 막대 위 백만 단위 라벨 — DWLG HKNC 차트와 동일한 방식. datalabels: anchor:'end' align:'top' formatter: round(val/1M)+'M'. layout.padding.top:25 로 라벨 잘림 방지.
  • FEAT미래/초과 일자 콘솔 경고 — [일자검증] 오늘(N일) 이후 데이터 발견: 사업장 일자 금액 청구처 형태로 운영팀 데이터 무결성 모니터링. 월말 초과 일자(예: 4/31)도 같은 경로로 경고.
  • OPS실측 DWLT 엑셀 검증 — col 1='청구처', col 9='소계', col 10='날짜' 정상 매칭 확인. 데이터 4/01~4/23 까지만 존재. 4/31 표시는 days 배열 + endsWith 매칭 결합 버그였음.
2026-04-26 오늘 · V18.7.5 HKNC 헤더 AND 매칭
일일매출 V18.7.5 — HKNC 헤더 "20+수출" / "40+수출" AND 매칭 (사용자 제안)
  • FEATHKNC 헤더 AND 조건 — !/^\d{4}/.test(c) && c.indexOf('20')!==-1 단순 OR 매칭 → c.indexOf('20')!==-1 && c.indexOf('수출')!==-1 AND 매칭. 운영 엑셀 헤더 "20' 수출" 정확 매칭, 4자리 연도(2024)/행 0 "수출작업댓수" 자동 배제, 다른 사업장 미래 양식("20 수출 BOX" 등) 에도 호환.
  • OPS사용자 검토 단계에서 운영 엑셀 헤더 패턴("20' 수출", "40' 수출") 직접 확인 → AND 매칭이 가장 명료한 결정. 정규식 보수성과 가독성 동시 확보.
2026-04-26 오늘 · V18.7.4 HKNC 진짜 누적막대
일일매출 V18.7.4 — HKNC stack 누적 정상화 + 헤더 정규식 운영 매칭
  • FIXy.stacked:true — V18.7.3 의 y.stacked:false 가 같은 stack 그룹(curr/prev) 의 누적을 막아 4 막대(20ft·40ft × 당월·전월) 가 한 X 자리에 grouped 4 분할로 그려져 시각 구분 불가했던 이슈 해결. 이제 curr stack(진한) + prev stack(옅은) 두 누적 막대가 X 한 자리 옆에 분명히 분리.
  • FIXHKNC 헤더 정규식 운영 매칭 — V18.7.3 의 /^20(ft)?$/i 가 운영 엑셀의 실제 헤더 "20' 수출"(작은따옴표+공백) 을 못 잡던 이슈 (fallback 으로 동작 중이었음) → !/^\d{4}/.test(c) && c.indexOf('20')!==-1 로 변경. 4자리 연도(2024 등) 는 명시 제외. 콘솔 디버그 [HKNC] sheetName headerRow:N date:N pt20:N pt40:N 추가.
  • OPS실측 NAS HKNC 엑셀 헤더 = ('구분', "20' 수출", "40' 수출") 형식 확인. 데이터 파싱 자체는 정상이었으나 정규식 강화 영향으로 fallback 의존이 잠복 위험이었음.
2026-04-26 오늘 · V18.7.3 HKNC 가독성 회복
일일매출 V18.7.3 — HKNC 도넛/막대 차트 V18.6.1 가독성 회복
  • FIXHKNC 막대 X축 31일 전부 표시 — V18.7 의 autoSkip:true,maxTicksLimit:16 으로 일부 일자(4/16~) 가 가려지던 이슈 → V18.6.1 회복(autoSkip:false,maxRotation:45). 4/01~4/31 모든 일자 라벨 표시.
  • FIX전월 막대 가시화 — V18.7 의 backgroundColor:'transparent', borderWidth:1 로 전월 막대가 사실상 보이지 않던 이슈 → 토큰 색의 rgba .20 채움 + .55 테두리 + 라벨(.70) 으로 V18.6.1 톤 회복. 신규 헬퍼 toRgba(hex, alpha) 추가.
  • FIX도넛 라벨 위계 회복 — V18.7 의 "당월/N 전월" 한 줄 → V18.6.1 의 "당월 N (큰 숫자)" + 도넛 아래 "N 전월(작은 회색)" 두 줄 분리 (.hknc-kpi-row). 20ft/40ft 둘 다 시각 구분 명확.
  • OPS차트 색은 토큰 기반 유지 (siteColor('DWLG'/'DWLT')) — 다크/라이트 자동 적응. toRgba 가 토큰 hex 를 rgba 로 변환해 다크/라이트 양쪽에서 옅은 색 일관 유지.
2026-04-26 오늘 · V18.7.2 헤더 회복
일일매출 V18.7.2 — portal-hdr 글로벌 회복 + "부산본부 관리팀" 칩 위치 시안 매칭
  • FIXportal-hdr 글로벌 회복 — V18.7 에서 51-view-daily.css 의 :root 정리 시 함께 사라진 .portal-hdr/.portal-hdr-l/.portal-title/.portal-sub/.back-btn 정의를 30-layout.css 에 정식 등록. 일일매출 외 인원·유류비·비용분석·사업장 등 다른 뷰의 헤더도 함께 정상화.
  • FIX"부산본부 관리팀" 칩 위치 — 컨트롤 패널 좌측 → 헤더 우측(서버뱃지 옆) 으로 이관. 새 글로벌 클래스 .badge-team.
  • FEAT컨트롤 패널 좌측 영업일 경과 — "26년 4월 26일(일요일) · 영업일 N/M일 경과(P%)" 시안과 매칭.
2026-04-26 오늘 · V18.7.1 KPI 톤 회복
일일매출 V18.7.1 — KPI 카드 톤 회복 (시안 vs 운영 갭 보정)
  • FIXKPI 라벨 톤 회복 — 시안의 11px uppercase var(--text2) 가 실제 운영 환경에서 너무 옅어 보이던 이슈 → 14px var(--text) weight 700 + letter-spacing -.2px 로 복원. 기존 V18.6.1 의 "당월 총 매출금액" 톤과 유사하게.
  • FIXKPI 숫자 폰트 26px 복원 — 시안의 22px 가 작게 느껴지던 위계 문제 해결. 카드 padding 18px 22px / top stripe 4px / sub padding-top 12px 로 V18.6.1 톤 회복.
  • FIXKPI sub 두 토큰(왼/오) 분리 — "전월: X · 증감: Y" 한 줄 → "전월: X" 왼쪽 + "증감: Y" 오른쪽 정렬 (justify-content:space-between). 정보 밀도 ↑, 가독성 ↑.
  • FIX5장 grid breakpoint 1280→1480px — 5장 grid 가 좁은 화면(13~15인치 노트북) 에서 카드 폭 부족했던 이슈 → 1480 미만은 3칸, 1100 미만은 2칸으로 자연스럽게 wrap.
  • OPS본 패치 범위는 CSS + JS d_kpiCard 시그니처(subLeft, subRight) 확장만. 데이터 흐름·차트·SSE·server.js 모두 V18.7 그대로 유지. 빌드 산출물 md5 갱신.
2026-04-26 오늘 · V18.7 일일매출 리뉴얼
일일매출 V18.7 — 12개 결정사항 일괄 적용 (디자인 + 데이터 파이프라인)
  • FEATCSS 토큰 글로벌화 (A-1) — 51-view-daily.css 의 :root/body 글로벌 오염 제거. --pos/--neg/--surface/--muted/--site-dwlg/--site-dwlt/--site-bncc/--site-total 8개 토큰을 00-tokens.css 다크/라이트 양쪽 정의. 라이트 테마 가독성 회복.
  • FEATKPI 5장 그리드 (A-3) — 통합뷰: 당월총매출 · 일평균 · 잔여영업일 추정도달 · 청구처합계 · 신규확보. 사업장뷰: 당월매출+점유율 · MoM · YoY · 신규 · 이탈. businessDaysInMonth/elapsedBusinessDays 헬퍼 추가.
  • FEAT일별 차트 누적막대 (A-2) — 통합뷰 일별 추이를 사업장 stack 으로 변경. 31일 × 1막대 → 라벨 가독성 회복. 차트 5:5 grid 비율로 변경.
  • FEAT병렬 fetch (B-2) — loadDailyData 의 직렬 await 7회 → Promise.allSettled 1 wave (curr 3 + prev 3 + hknc 동시). 5~7s → 1~2s 단축 예상.
  • FEAT혼합 파싱 (B-1) — 1차 고정 인덱스(가이드 §2-4) → 키워드 → 숫자 휴리스틱 폴백. parsedVia 메타데이터를 d-file-summary 에 표기 ("매칭 경로: fixed | keyword | heuristic").
  • FEAT두 패널 사전 생성 (B-4) — d-bottom-overview/d-bottom-site 항상 DOM 보유, display 토글로 전환. innerHTML 통째 교체 + Chart.js destroy/create 깜빡임 제거.
  • FEAT무결성 배너 (B-6) — 엑셀 합계행 vs 계산 합계 차이 10원 초과 시 노란 배너 + 사업장 칩. 클릭 시 상세(파일명·스킵행) 모달.
  • FIX5분 룰 reload (B-3) — enterDaily 가 lastFetchAt > 5분 이면 자동 reload. SSE 끊김 시 refresh-btn.urgent 깜빡임 강조.
  • FIXHKNC 정규식 강화 (B-5) — '20' 단순 검색 → /^20(ft)?$/i. '2024' 같은 4자리 연도 텍스트가 pt20Idx 로 잡히던 위험 제거. 헤더 스캔 5행 → 15행.
  • FIXY축 자동 nice scale (A-5) — Math.ceil(max * 1.005 / 1억) * 1억 강제 라운드 제거 → Chart.js suggestedMax = max * 1.10. 5천만 매출이 1억 라운드로 막대 절반만 차지하던 문제 해결.
  • FIX색 hex 인라인 제거 (A-4) — #3d8eff/#dceeff 등 모두 토큰 read (siteColor()). .kpi-lbl 의 color:#dceeff → var(--text2). 라이트 테마에서 안 보이던 KPI 라벨 가독성 회복.
  • FIX미매칭 합계 청구처명 기반 (B-6 부록) — matchedOtherAmts set 의 amount 기반 매칭이 동일금액 두 거래처 수집 누락 → 청구처명 정규화 set 으로 변경.
  • FIX~$ 임시파일 필터 (server.js) — 엑셀 열려 있을 때 NAS 에 생기는 ~$DWLG_*.xlsx 가 findDailyFiles 결과에 포함되어 빈 파싱이 발생하던 위험 제거. !f.startsWith('~') 추가.
  • OPS모바일 모드 활성화 — isMobileMode 가 영구 false 였던 dead code → window.matchMedia('(max-width:768px)') + change 리스너로 동적 갱신. 모바일에서 KPI/테이블 "만" 단위 표기 작동.
  • OPS헤더 sub 갱신 + scan-status 조건부 페이드 — d-hdr-sub 가 SSE 후에도 갱신 안되던 문제 → d_setHdrSub() 매 fetch 후 호출. d-scan-status 정상 1.5s / 경고 5s 페이드.
  • OPSSSE 인증 변경(B-7)은 보류 — EventSource 헤더 한계로 폴리필 도입이 필요해 별도 보안 패치 PR 로 분리. 현 세션 변경 범위는 디자인 + 데이터 신뢰성으로 한정.
  • OPS검증 — node --check 14 모듈 + preflight + 인라인 2 블록 OK. 빌드 산출물 md5 0e3c7915d50e834a431fa55d1d5ae450 (564,368 B, V18.6.1 +6,604 B). 51-view-daily.js 895 → 1009줄, 00-tokens.css +6줄(8 토큰), 20-components.css +30줄(KPI grid · integrity bar), 51-view-daily.css 67 → 14줄(빈 슬롯). 결정사항/시안 SSOT: 2.리팩토링_계획/05_일일매출_결정사항.md · 06_일일매출_디자인시안.html.
2026-04-26 오늘 · V18.6.1 야간 staging
인원현황 — 1명 장비 묶기 + DWLT 기사→리치 분류
  • FEAT장비 카드 — 인원 1명짜리 장비(리치/30t/7t 등) 는 별도 카드로 분리하지 않고 한 카드 안에 세로 한 줄로 묶음(라벨 · 인원수 헤더 + 행마다 "장비명 + 이름"). 2명 이상(4.5t 10명·2.5t 등) 은 기존 별도 카드 유지. 1명짜리 통합 카드를 먼저 배치 → 다인원 카드 순.
  • FIXDWLT OS직원 "기사" 직책이 OS직원 풀에 머물던 이슈 — DWLT 는 리치 전용 사업장이므로 getEquipType() 에 site 인자 추가, 직책에 "기사" 포함 시 (대홍/지입 키워드 없어도) site === 'DWLT' 면 자동으로 리치 분류 → 리치기사 그룹으로 이동. parsePersonnelExcel 호출부도 site 전달.
  • OPS장비 정렬 순서 equipOrder = ['리치','7t','30t','4.5t','2.5t'] → ['리치','30t','7t','4.5t','2.5t'] (큰 톤 → 작은 톤 자연 순서). 통합 카드 헤더 라벨은 " · " 로 구분 (예: 리치 · 30t · 7t 3명).
2026-04-26 오늘 · 야간 staging
인원현황 조직도 — 정규직/OS직원 세로 스택 배치
  • FIX인원현황 사업장별 조직도에서 정규직(2/3) + OS직원(1/3) 좌우 분할 시 DWLG 정규직 19명이 가로 스크롤로 잘려 운영팀 일부 인원이 화면에 노출되지 않던 이슈 해결. renderSite()의 regOsHtml flex row → 단순 concat 으로 변경 → 부모의 flex-direction:column / gap:14px가 그대로 적용되어 정규직(위) → OS직원(아래) 세로 스택으로 배치.
  • OPS각 site 의 정규직 블록이 카드 폭 전체(1/3 page) 사용 → 영업팀·운영팀·관리팀 3카드가 가로 스크롤 없이 표시. OS직원·장비·경비/미화 등 후속 그룹은 기존 흐름 유지(경비+미화는 좌우 그대로).
2026-04-26 오늘 · 야간 staging
워닝 모달 — 컨텍스트/권장 액션 섹션 제거
  • REMOVE비용분석·유류비 워닝 모달의 💡 컨텍스트 (자동 분석) + 🎯 권장 액션 두 섹션 일괄 삭제. HTML 슬롯(rd-modal-context/rd-modal-actions, fuel2-modal-context/fuel2-modal-actions) 제거 + JS 채우는 코드 + generateContext/generateActions 함수 제거.
  • OPS모달 5섹션 → 3섹션(헤더·핵심지표·12개월 추이·거래처분해)으로 간소화. 본부장 의사결정에 직접 필요한 데이터만 노출. 자연어 컨텍스트는 B-3 단계에서 정밀하게 재도입 검토.
2026-04-26 오늘 · 야간 staging
비용분석 모달 — 거래처 분해를 업체명 단위 합산으로
  • FIX모달 거래처 분해에서 전월 금액이 모두 0/NEW 로 표시되던 이슈 우회 — 적요(예: '쇼링료/3월') 가 매월 달라 server.js 의 ${Client}|${Note} 키 매칭이 분리되던 것을 업체명(Client) 단위 합산으로 client side 에서 우회. 같은 거래처의 모든 적요 행을 합산해 당월 vs 전월 비교.
  • FEAT당월·전월 Details 배열에서 각각 Client 로 group by 한 후 outer join. 1월(m=0)은 자동으로 전년 12월(m=11) Details 참조. 표에 (N/M건) 표시 — 당월 적요 N행 + 전월 적요 M행 수.
  • OPS08 SSOT 이슈 #1 갱신 — V18.5 client-side 우회로 운영 해결. server.js 측 키 정규화 (B-2) 는 선택 사항으로 표기. server.js 무변경.
2026-04-26 오늘 · 야간 staging
비용분석 — 차트 24개월화 (전년+올해 동시 표시)
  • FEAT매출 대비 비율 추이(band)·고정비 추이·변동비 산점도 모두 24개월(전년 12 + 올해 12)로 확장. 연도별 색상 분리(전년=옅음, 올해=강조), Chart.js segment 옵션으로 한 라인 안에 자연스러운 톤 전환.
  • FEAT24개월 헬퍼 추가 — getPrevYear()·months24Labels()·get24mSeries(accessor)·segPrevDim(). 모든 시계열 차트가 같은 패턴으로 통일.
  • OPSMA·±2σ band·평균은 24개월 valid 기준으로 재산정. 변동비 산점도는 회귀선이 24개 점 합산으로 산출 → 매출-변동비 동조성 가시성 향상. 워닝 모달의 12개월 차트는 이미 24개월화 완료(drawModalChart).
2026-04-26 오늘 · 야간 hotfix
[CRITICAL HOTFIX] 로그인 깨짐 — 복구
  • FIXsrc/HTML 끝부분 </script></body></html> 누락으로 main inline script 미닫힘 → 브라우저 파싱 비정상 → doLogin is not defined 로그인 불가. 끝부분 복구 + 모듈 추출 안내 코멘트 정리.
  • OPSbuild.log 분석으로 V18.3 만 inline_blocks=1 (V18.2까지 모두 2)인 것 단서로 추적. node --check src inline 2 blocks OK 회복 확인.
2026-04-26 오늘 · 야간 staging
비용분석 — 용어/5대원칙 팝업화 (룰박스 컴팩트)
  • UI룰박스가 화면을 너무 차지하던 문제 해결 — 룰박스 본체를 한 줄(타이틀 + 📖 용어·원칙 보기 + ⚙ 사업장별 기준 편집)로 축소.
  • FEAT용어 해설 8칩 + 5대 원칙 카드 5개를 신규 팝업 모달(rd-rules-modal)로 분리. 📖 버튼 클릭 시 풀화면 팝업, ESC/외곽클릭/✕ 닫기. body overflow:hidden 처리.
  • OPS기존 보드/차트 영역으로 화면 확보. 본부장 의사결정에 필요한 KPI/워닝/차트가 first-fold 에 더 들어옴.
2026-04-26 오늘 · 야간 staging
비용분석 — 용어 해설 + 사업장별 KPI 기준 편집
  • FEAT5대 원칙 룰박스 상단에 용어 해설 영역(8칩: MoM·YoY·MA12·Plan·σ·z-score·waterfall·등급) 추가. 5대 원칙 카드 본문은 본부장이 즉시 이해 가능한 한글 표현으로 전면 재작성.
  • FEAT사업장별 KPI 기준 구조 도입 — KPI_TARGETS_BY_BRANCH (DWLG/Terminal/CFS 별 cogsRatio/cogsCritical/sgaRatio/sgaCritical/opRatio/opCritical 6값). 전체는 사업장별 매출액 가중평균으로 자동 계산.
  • FEAT기준 편집 모달 신규 — 사업장 3 × 임계값 6 매트릭스 인풋, 입력 즉시 가중평균 미리보기, localStorage 저장(busan_dashboard_kpi_targets_v1), Default 복원 버튼. ESC/외곽클릭 닫기. 룰박스 헤더의 ⚙ 버튼으로 진입.
  • FIXrenderAll() 진입부에서 KPI_TARGET = computeKpiTarget(RD.selBranch) 재계산 → 모든 KPI 카드/보드/차트가 사업장 전환 즉시 해당 기준 반영.
  • OPScss 추가 ~125줄(.rd-glossary, .rd-pill, .rd-target-editor-*, .rd-tgt-*). B-2 진입 시 server.js env 로 이관 권장 (08 SSOT §7 갱신).
2026-04-26 오늘 · 야간 staging
비용분석/유류비 — 'CFO' 문구 일괄 제거
  • FIXHTML view-rowdata 헤더 💰 비용분석 (RAW) — CFO 점검 모드 → 💰 비용분석 (RAW), 룰박스 타이틀 ⚙️ CFO 점검 5대 원칙 → ⚙️ 점검 5대 원칙.
  • FIXview-fuel은 본문 텍스트에 'CFO' 노출 없었음. dev-history 팝업의 두 뷰 dev-row 4건에서 CFO 표현 제거.
  • OPSSSOT 파일명 변경: 08_비용분석_V18_CFO점검모드.md → 08_비용분석_V18_점검모드.md, 09_유류비_V2_CFO점검모드.md → 09_유류비_V2_점검모드.md. 모든 참조(CLAUDE.md, 03·05·08·09 SSOT, JS·CSS 코멘트) 동기화.
  • OPSstaging (3002) 재배포 + smoke 검증. prod 승격 대기.
2026-04-26 오늘 · 야간 staging
유류비 점검 V2.0 — 점검 모드 (B-1)
  • FEATsrc/js/55-view-fuel.js 전면 재작성 (248→553줄). 5대 원칙 유류 도메인 적용 + 12섹션 구조. 워닝 룰 7개(단가 z-score·단가 MoM·수량 MoM·비용 MoM·탱크 초과·주유 주기·동일일 중복) + 신뢰도 점수 1~4.
  • FEATsrc/css/55-view-fuel.css 신규 작성 (~210줄). #view-fuel 스코프 + .fuel2- prefix. 워닝 배너·룰 박스·KPI 미니그리드·목표 보드·워닝 카드 3분할·TOP 이상치·사업장 4카드·모달 5섹션.
  • FEATHTML #view-fuel 슬롯 12섹션 확장 (워닝배너·룰박스·KPI·목표보드·워닝카드·인과분해 placeholder·단가 ±2σ band·산점도·TOP·사업장비교·드릴다운·모달).
  • OPS새 SSOT 1.기초정보/09_유류비_V2_점검모드.md 작성 (15섹션, 본부장 결재 항목 5가지 명시). 08번(비용분석 V18) 패턴 미러링.
  • FIX기존 V1 의 트렌드 차트 필터 정합성 문제 해결 (KPI는 필터·트렌드는 전체 → 모두 일관). 사업장 점유율 도넛을 주유량→비용 점유율 로 교체 (의사결정 적합).
  • OPSB-1 frontend-only 구현 (server.js 무변경). B-2(가동시간·거래처 컬럼·trendsBySite)·B-3(시장 ref·Plan 연동·인과분해 waterfall) 인계는 09 SSOT §11 명시.
  • OPSstaging (3002) 배포 + smoke 15/15 통과 (md5 326500493e00cb7ba5dcc0371e3419d3, 518,393 B). prod 승격은 본부장 결재 후 진행.
2026-04-26 오늘 · 야간
V18.0 · 비용분석 (RAW) 점검 모드 — mockup v3 적용 (B-1)
  • FEAT비용분석 뷰 전면 재설계 — 단순 차트 2개·테이블 1개 → 점검 5대 원칙 적용 풀 대시보드. 워닝 배너 + 룰 박스 + KPI 4개(4-baseline 미니그리드) + 목표·임계값 보드 + 워닝 카드 3분할(Critical/Warning/Info) + 인과분석 placeholder + 매출 대비 비율 추이(±2σ band) + 변동/고정비 분리 + TOP10 + 사업장 비교 + 드릴다운 테이블.
  • FEAT워닝 모달 시스템 — 워닝 카드 클릭 시 5섹션 상세 팝업(핵심지표·12개월 추이 ±2σ band·거래처 분해·자연어 컨텍스트·권장 액션). 거래처 분해는 server.js Details[m][typeKey][acc] 활용. ESC/외곽클릭/✕ 닫기 + 차트 자동 destroy.
  • FEAT워닝 검출 (B-1 임시 룰) — MoM 매출비% ±2%p / ±5%p 임계 + 절대금액 ±20% / ±50%. 신규 계정(이전 3개월 0) 자동 Info. 변동/고정비 마스터(운반·외주·연료=변동, 임차·감가=고정) 하드코딩(B-2 단계 회계팀 협의 후 보정). KPI 목표값 환경변수화 준비(KPI_TARGET 객체).
  • FEATsrc/css/54-view-rowdata.css 신규 작성(280줄, mockup v3 톤 #view-rowdata 스코프). 모달은 .rd-modal-* 별도 클래스. 다른 뷰와 충돌 0.
  • FEATsrc/js/54-view-rowdata.js 전면 재작성(260→760줄). 기존 /api/rowdata-json (server.js buildRowdataJson) 응답 그대로 활용 + 추가 API 0개. 차트 6개(band·variable·fixed·waterfall placeholder·cogsTop·sgaTop) + 모달 차트 1개. SSE 자동 갱신 유지.
  • OPSB-2/B-3 단계 안내 명시 — 워닝 카드·드릴다운 테이블·KPI 미니그리드에 "B-2", "B-3" 표시로 다음 단계에서 추가될 기능을 사용자에게 미리 노출. B-2: server.js 4-baseline(YoY/MA12/Plan) + z-score + 변동/고정 분류 + 신뢰도 점수. B-3: 인과분석 waterfall + 사업계획.xlsx 연동 + 거래처 단가 추적 + 자연어 컨텍스트 자동 생성.
2026-04-26 오늘 · 저녁
V17.4.1 · 비용분석 데이터 파일명 ROW → RAW 변경 (404 픽스)
  • FIX비용분석 (ROW) → 비용분석 (RAW) — 사용자 측에서 데이터 파일명을 ROW-비용분석.xlsx → RAW-비용분석.xlsx 로 변경. server.js 가 기존 파일명을 못 찾아 /api/rowdata-info 가 404 반환 → 사용자에게 "로드 실패 · API 오류 404" 표시되던 이슈 해결.
  • FIXserver.js ROWDATA_FILENAME 상수 변경(line 61). HTML 섹션 타이틀 💰 비용분석 (ROW) → 💰 비용분석 (RAW), 로딩 메시지 2개(HTML + JS), 코멘트 6곳 동기화. 내부 식별자(view-rowdata · RD · rd_* · /api/rowdata-*)는 회귀 위험 회피 위해 호환성 그대로 유지.
  • FIXdocker-compose.yml 환경변수 — ROWDATA_FILENAME=ROW-비용분석.xlsx 가 양쪽 컨테이너에 박혀 있어 server.js default 를 덮어씀. RAW-비용분석.xlsx 로 변경 + docker compose up -d 로 재생성(restart 만으로는 env 반영 안됨). docker-compose.yml.bak_<ts> 백업 동시 생성.
  • FIXSERVER_HOST 하드코딩 제거 — 00-app-init.js 의 SERVER_HOST: '1.254.6.240:3001' 평문 → window.location.host 동적 결정(http:// 진입 시) + 1.254.6.240:3001 폴백(file:// 진입 시, 원칙 13 보존). 이전: staging 3002 페이지가 항상 prod 3001 API 호출하여 staging 검증 무의미했음. 이제 같은 origin 자동 매칭.
  • OPS문서 동기화 — 1.기초정보/03_모듈별_기능기획.md 모듈 6 타이틀 + 데이터 출처, 1.기초정보/05_데이터매핑_가이드.md 6장 타이틀 + 파일 경로(원칙 5·10).
  • OPS배포 — staging(3002) 먼저 빌드/배포 → 사용자 검증 → prod(3001) 승격. scripts/smoke.py 15/15 통과 확인. 인라인 <script> 2블록 + 모듈 14개 node --check OK(원칙 19).
2026-04-26 오후
V17.4 Phase 4·5 완료 · 파이프라인 강화 (sentinel/node--check/SFTP/롤백/preflight 분리/smoke)
  • FEATbuild.py V17.4 sentinel을 양쪽 코멘트 포함 형태(/*__BUSAN_INLINE_*__*/)로 변경. 부재/중복/잔존 모두 빌드 중단(원칙 19 자동화). node --check 모듈 14개 + preflight.js + src 인라인 2블록 자동. dev-row 검증 추가. bindfs invalidate 범위 src/HTML→src/HTML+css/*+js/*+preflight 확장.
  • FEATdeploy.py V17.4 SFTP 우선 + base64 폴백 (실패 시 자동 전환), 자동 롤백(헬스체크 실패 시 backup 자동 cp + 재시작 + 재헬스체크), --rollback N(직전 N번째 백업) / --rollback-to TAG(git tag 빌드 재생성) 옵션, SynologyDrive 동시 복사(SYNCDRIVE_LOCAL 자격증명 활용).
  • FEATpreflight.js 분리 file:// 진입용 IIFE 셸을 src/preflight.js(53줄, 3,041 B)로 분리. build.py가 /*__BUSAN_INLINE_PREFLIGHT__*/ sentinel에 주입. 원칙 13(file:// 더블클릭 보존) 그대로.
  • FEATsmoke.py 신설 15개 자동 검증 (8 GET API + 4 SSE 첫바이트<3s + Auth 401 + build/NAS md5 일치 + preflight IIFE 본문 포함). scripts/smoke.py --target=staging 1 명령 + --json 옵션.
  • OPSscripts/new-entry.py 신설 — dev-history 엔트리 CLI(원칙 17 자동화, 이전 row latest 클래스 자동 제거). scripts/new-view.py 신설 — 신규 뷰 IIFE 패턴 스캐폴딩(JS+CSS 양쪽).
  • OPSKPI 실측 — 빌드 583ms(< 2s 충족), 배포 ~5s(< 30s 충족). 빌드 산출물 387,827→388,175 B(+348 = preflight 헤더 코멘트). md5 c2d3a705…→2842ef2a…. Phase 4·5 완료, Build-Inline Hybrid 100% 완수.
2026-04-25 오늘 · 오후
V17.3 Phase 3 · JS 14개 파일 분리 (Build-Inline Hybrid 완성) + I-4 종결
  • FEAT인라인 <script> 메인 블록(약 5,400줄)을 src/js/*.js 14개 파일로 분리. 인프라 7개(00-app-init · 10-common · 20-api · 30-chart · 40-theme · 41-auth · 42-view) + 뷰별 7개(50-view-pl · 51-view-daily · 52-view-personnel · 54-view-rowdata · 55-view-fuel · 60-view-facility · 90-devhistory). Phase 2 의 CSS 분리(14파일)와 함께 Build-Inline Hybrid 완성: 개발은 모듈 파일로, 배포는 단일 HTML 로(원칙 13 SynologyDrive 로컬 더블클릭 보존).
  • FEATscripts/build.py 가 {{INLINE_CSS}} + {{INLINE_JS}} 양쪽 placeholder 처리. JS 도 prefix 정렬 concat(00→10→…→90). 뷰별 모듈은 IIFE 래핑 + window.App.view.enterXxx Phase 3 표준 진입점 + window.* onclick 호환성 글로벌 노출 패턴 적용. 인프라 7개는 평면 전역 + onclick 핸들러 호환성 유지.
  • FEATI-4 종결: 50-view-pl.js 상단 const PLAN_SHEET_NAME = '26년 사업계획 집계'; 상수 분리. 매년 1월 여기 한 곳만 수정하면 됨(이전: HTML 본문 2곳 직접 수정 필요). server.js 는 별도 변경(deploy.py --include-server-js 활용).
  • OPSscripts/deploy.py 에 --include-server-js 옵션 추가(Task #15) — V17.2.1 paramiko 일회성 스크립트 우회 정식화. scripts/_safe_commit.py 신설 — bindfs sandbox .git/index.lock 잔존 우회(plumbing + ref 직접 갱신 + reflog append + .git/index sync). 다음 세션 인계 시점부터는 cp /tmp/git-idx-phase3-* .git/index 강제 sync 가 매 commit 후 권장.
  • OPSHTML 6,949 → 1,490줄 (-78.6%), 372,895 → 384,417 B(빌드 산출물, 모듈 코멘트 포함으로 약간 증가). 메인 인라인 <script> 본문이 14개 src/js/*.js 파일로 분산되어 단일 HTML 편집 시 무관한 기능까지 깨지는 문제(CLAUDE.md §6) 완전 해결. 인라인 <script> 2블록 node --check 양 블록 OK(원칙 19).
  • OPS검증 — staging(3002) 모듈 4개(daily/personnel/fuel/rowdata) 시점 + 7개 모두 추출 후 시점에 사용자 육안 검증, 8개 뷰 + 다크/라이트 토글 시각 회귀 0 확인. Git 태그 v17.3. 이슈 I-1·I-2·I-3·I-4 모두 종결. 외부 UI 동작 V17.2.1 과 완전 동일(JS 모듈 분리·IIFE 래핑만, 동작 변경 0).
2026-04-25 오후
V17.2.1 · I-2 가마감 dead code 제거 (Phase 3 사전 정리)
  • REMOVEHTML 가마감 블록 일괄 삭제 — enterGamagam()·GM 상태 객체·loadGamagamData()·refreshGamagamData()·동적 컬럼 파싱·차트·SSE(App.api.connectSSE) 등 메인 305줄 + 진입점 5줄 + 전역 플래그(gamagamLoaded) 3곳 + showView views 배열의 'view-gamagam' 엔트리. 총 306줄 감소. 메뉴/onclick/링크 등 사용자 진입 경로 0개를 사전 검증 후 제거.
  • REMOVEserver.js 가마감 영역 일괄 삭제 — DATA4_DIR 상수, findGamagamFiles(), /api/gamagam-info·/api/gamagam-file·/api/gamagam-watch 3개 라우트, sseClients4 Set, chokidar 폴더 감시, /health 의 data4 키, 부팅 로그 1줄. 957 → 830줄 (-127줄). node --check OK.
  • OPSsrc/css/53-view-gamagam.css 1줄 코멘트로 비움 — 슬롯 보존하여 향후 재활성화 시 같은 prefix 재사용 가능. build.py CSS concat 영향 0. 개발이력(#dev-history-source) 안 가마감 언급 3곳(line 199·410·412)은 역사 기록으로 의도적 보존.
  • OPSHTML 7,259 → 6,949줄 (-310), 385,410 → 369,915 B (-15.5KB / -4%). md5 86b9ea14… → 86a64d9e…. 인라인 <script> 2블록(preflight 50줄 · main 3,833줄) node --check 통과(원칙 19).
  • OPSscripts/deploy.py 가 HTML 전용 업로드 한계 — server.js 변경분은 paramiko 일회성 스크립트로 staging+prod NAS 직접 업로드(/volume1/docker/busan-dashboard*/server.js). 컨테이너 재시작 시 새 server.js 로드. 다음 세션 deploy.py 에 --include-server-js 옵션 추가 예정.
  • OPS이슈 I-2(가마감 뷰 DOM 부재 dead code) ✅ 종결(2026-04-25). Git 태그 v17.2.1. 외부 UI 동작 V17.2 와 완전 동일(가마감은 원래 사용자 진입 경로 0이라 시각/기능 회귀 없음).
2026-04-25 오늘 · 정오
V17.2 Phase 2 · 인라인 CSS 14개 파일 분리 (Build-Inline Hybrid)
  • FEATHTML 인라인 <style> 블록 3개(메인 1551줄 + 두번째 252줄 + nested v16-facility 151줄, 총 1953줄)를 src/css/*.css 14개 파일로 분리. 00-tokens · 10-base · 20-components · 30-layout · 40-view-login · 41-view-menu · 50~55-view-* · 60-view-facility · 90-devhistory. 단일 HTML 편집 시 무관한 기능까지 깨지는 문제(CLAUDE.md §6) 해결의 핵심 단계.
  • FEATscripts/build.py 가 src/css/*.css 정렬 concat 후 {{INLINE_CSS}} placeholder 치환. ORDER 는 prefix 정렬(00→10→20→30→40→41→50→…→90)로 cascading 우선순위 최대 보존. 빌드 후 단일 HTML 산출(원칙 13 SynologyDrive 로컬 더블클릭 보존).
  • FEATnested <style> 버그 정리 — line 2259 빈 <style> + line 2261 <style id="v16-facility-design"> + line 2415 orphan </style> 의 잘못된 중첩 구조를 단일 <style> 블록으로 통합.
  • OPSscripts/deploy.py upload_via_b64 함수를 16KB 청크 base64 append 패턴으로 정식 반영(인계 §2-A). paramiko exec_command 단일 heredoc 500KB+ EOFError 회피. scripts/build.py 시작부에 mv 2회 rename 으로 bindfs 캐시 invalidate 추가(인계 §2-B, Windows Edit 후 Linux sandbox stale capping 방지).
  • OPS검증 — staging(3002) 먼저 배포 → 사용자 육안 검증(다크/메뉴/슬라이드쇼) prod(3001) V17.1 과 시각 동일 확인. 셀렉터 카운트 일치(.menu-card 15·.login-box 5·v16- 269·:root[data-theme] 5). 인라인 <script> 2블록 node --check OK(원칙 19).
  • OPSHTML 크기 377,607 → 382,586 B (+5KB CSS 영역 코멘트만), 줄 수 7,140 → 7,243 (+103). md5 a69a3b61… → dd485cf0…. Git 태그 v17.2. 외부 UI 동작 V17.1 과 완전 동일(CSS 분리·코멘트 추가만).
2026-04-24 어제 · 오후
V17.1 Phase 1 · App 네임스페이스 + 공통 헬퍼 3종 도입
  • FEATwindow.App.* 네임스페이스 신설 — App.common/api/chart/theme/auth/view/util 7개 스텁 + 기존 평면 전역(SERVER_HOST·SERVER_PW)을 App.common SSOT 와 동기화. 원칙 6(전역 상태 네임스페이스) 충족.
  • FEATApp.api.apiFetch(path, opts) 도입 — URL 자동 prefix, 인증 헤더 자동, 실패 시 err.status/err.response 보존 예외. 기존 fetch 16 사용자 호출 중 15 치환(로그인 1개만 입력 pw 특수성 때문에 raw 유지). 원칙 9 충족.
  • FEATApp.api.connectSSE(path, {onmessage,onopen,onerror}, label) 도입 — 지수 백오프(1→30s) 재연결, 호출자 close() 취소 가능. 5개 SSE(손익·일일·인원·가마감·비용분석) 전부 이관. 이슈 I-3(가마감·비용분석 SSE 재연결 누락) 종결. 원칙 8 충족.
  • FEATApp.chart.chartTxt/chartTick/chartSurf — 기존 chartTxt/chartTick/chartSurf 함수를 App.chart 에 이관(별칭). return chart*( 패턴 22 사이트 App.chart.* 네임스페이스로 일괄 치환. 원칙 2(Canvas var() 미해석) 재확인.
  • OPS배포 전 인라인 <script> 2블록 node --check 전부 OK (원칙 19). scripts/build.py copy-through + 청크 base64 업로드 (SFTP 미가용 NAS 환경 대비). staging(3002) 먼저 배포 → md5 bit-exact 검증 후 운영(3001) 승격. 자동 백업 _backup_auto_20260424_081815.html.
  • OPSHTML 크기 371 → 375KB (+3.7KB), 줄 수 7,024 → 7,125 (+101). md5 f9acbb9d… → 2c362cc9…. Git 태그 v17.1. 외부 UI 동작 V16.1 과 완전 동일(구조 리팩토링만).
2026-04-22 심야
v1.6 · 사업장 기본정보 전면 재설계
  • UI사업장 기본정보 뷰(view-facility)를 전면 재설계. 상단 `v16-tbar`(브랜드 · DWLG/DWLT/BNCC 3버튼 · 데이터 연동 pill) + 좌측 2열 구조(232px `v16-sbar` + `v16-main`) 도입. 좌측 사이드바에 활성 4개(기본정보/하역설비/소방설비/편의시설) + 비활성 6개(보안·주차·IT·에너지·근태·회의실 "준비중")를 명시적으로 노출해 향후 확장 로드맵을 UI 로 가시화. 기존 V15 표 레이아웃 대비 정보 밀도 약 40% 향상.
  • FEAT기본정보 섹션에 KPI 4종(부지 면적·연면적·층수·준공) + 위치 카드 + "일반현황/건축·규모" 2-카드 + 현장사진 4-그리드 + 라이트박스 줌(`v16_openImage`). 하역설비 섹션에 6대 지게차 표(작업/용도/제원/부착물/유류/검사일) + 3개 보조장비(DRM/YRP/FRK) 카드. 소방설비 섹션에 9종 법정 설비 + 자위소방대 조직도(3단계). 편의시설에 탭 6종(탈의·휴게·식당·주차·정자·트럭수리).
  • FEAT디자인 CSS 는 `#view-facility` 스코프 + `v16-*` 클래스 프리픽스로 완전 격리 — 기존 손익/일일/인원/비용 등 7개 뷰의 스타일과 상호 간섭 없음. V15 기존 `enterFacility()`·`loadFacilityData()`·`/api/facility-info`·`/api/facility-assets/:site` 는 그대로 재사용(데이터 플로우 0 변경). 엑셀 파일(`04_사업장_기본정보_입력양식.xlsx`) 갱신 시 페이지도 자동 반영.
  • OPS배포 전 `node --check` 로 인라인 `<script>` 본문 구문 검증(원칙 19 준수). 기존 V15 대비 추가 JS 약 +600 줄, 추가 CSS 약 +19KB. 전체 HTML 크기 ~331KB. 사이드바 버전 표기 `Integrated Ops v1.6`.
2026-04-22
오늘 (V15)
FIXV15 슬라이드쇼 자동전환 차단 해제 (prefers-reduced-motion 가드 제거)
  • V15 FIX: startHqAutoplay() 내부 prefers-reduced-motion: reduce 가드가 Windows 전역 "애니메이션 표시" 비활성화 PC 에서 자동전환을 차단하던 버그 해결. V14 배포 후 사용자 PC 에서 재현 확인. 접근성 설정은 추후 수동 토글로 대체 예정
  • 진단 로그 추가: autoplay 시작/스킵 시 console.info("[hq] ...") 출력 — F12 DevTools Console 탭 에서 [hq] autoplay start — n=6, interval=3000ms 확인 가능
  • 방어적 초기화: startHqAutoplay() 진입 시 HQ.paused = false 명시적 리셋 — 이전 세션의 터치/포커스 이벤트가 paused=true 로 남아 있던 회귀 방지
2026-04-22
어제 (V14)
UIFIXFEATV14 슬라이드쇼 5일 필터 + V13 메뉴 그리드 제거 + doLogin 구문 에러 복구
  • CRITICAL 복구: V13 배포본 HTML 이 openDevHistoryWindow 내부 CSS 템플릿 문자열 중간(line 5937)에서 단일따옴표 쌍이 깨지며 파일이 297,263 bytes 로 잘려 있었음 → doLogin is not defined 참조 오류로 로그인 불가. V12 pristine 백업(312,038 bytes)에서 복구 후 재작업
  • V14 5일 필터: loadHqSlides() 내 클라이언트 사이드에서 오늘 포함 최근 5일(오늘·어제·2·3·4일 전) 사진만 표시. 파일명 YYMMDD_내용.jpg 의 날짜 접두사를 Date 객체로 파싱, _cutoff 와 비교해 필터링. 필터 후 0 장이면 컴포넌트 숨김
  • V13 메뉴 그리드 제거: "데이터 현황 / 운영 관리" 6 개 .mcard 블록 삭제. 좌측 사이드바 내비게이션과 중복이었음. 랜딩이 환영 박스 + 본부 일정 슬라이드쇼 2단 구성으로 단순화. .hq-slides-track max-height 380 → 560 px
  • V13 라이트박스: hqOpenOriginal 을 window.open('_blank') 에서 인페이지 오버레이로 전환. 고정 오버레이 위 max-width:80vw/max-height:80vh. 이미지·배경·✕·ESC 중 무엇이든 누르면 닫힘
  • V13 자동전환 버그 수정: mouseenter → HQ.paused = true 가 대면적 슬라이드쇼에서 자동 전환을 영구 정지시키던 회귀. 호버 기반 pause 제거, 포커스(키보드)·터치·visibilitychange·라이트박스 오픈 시에만 pause
  • 교훈: ①대용량 HTML 에 인라인 CSS를 문자열 concat 으로 쓸 땐 따옴표 충돌 위험이 높으므로 template literal + 별도 변수 또는 외부 CSS 로 분리, ②배포 전 반드시 node --check 로 추출한 <script> 본문 검증 (CLAUDE.md 원칙 19 신설), ③대면적 UI 에 호버 pause 금지 (원칙 18)
2026-04-22
오늘 (이전)
UIFEATV12 본부 일정 사진 슬라이드쇼 — 랜딩 페이지 카드형 스와이프
  • V12 "반갑습니다, 부산본부 관리팀" 환영 박스 바로 아래 에 카드형 사진 슬라이드쇼(#hq-slides) 신설 — 3 초 자동 재생 + 좌/우 슬라이드 전환(translateX) + 사진 클릭 시 원본 새 탭
  • 사진 소스: /facility/본부 일정 사진/ (NAS File Station 직접 업로드, :ro 볼륨) — 파일명 규칙 YYMMDD_내용.jpg 에서 날짜 접두사·확장자 자동 제거 후 캡션 표시, 내용만 출력
  • 서버 측: server.js 에 /hq-photos(static 서빙, maxAge:5m) + /api/hq-photos(인증 필수, @eaDir·시스템 파일 필터, 파일명 역순 = 날짜 내림차순) 2 개 엔드포인트 추가
  • 접근성: W3C ARIA APG carousel 패턴(role="region", aria-roledescription="carousel", aria-live="polite") + 키보드 내비게이션(←/→/Home/End) + 포커스 시 자동재생 일시정지 + prefers-reduced-motion 준수 + 좌/우 화살표·점(dots) 네비게이터
  • 인터랙션: 터치 스와이프(threshold 40 px) + 호버/포커스 시 자동재생 일시정지, 휠·키보드 시 재시작 — 0 장 시 컴포넌트 숨김, 1 장 시 화살표·점 숨김 등 적응형 렌더
  • 교훈: 이미지 서빙은 기존 /images 라우트 재사용 금지 (FACILITY_DIR/Assets 고정) → 별도 /hq-photos 라우트로 분리, 회귀 없이 병행 운용
2026-04-22
이전
UIFIXV7 file:// 배너 오버레이 + V8 개발 이력 팝업 창 재구성
  • V7 file:// 자동 리다이렉트(V5 방식)가 최신 Chrome/Edge "file:// → http:// unsafe load" 정책에 차단되어 빈 화면이 되는 회귀 발생 → IIFE + DOMContentLoaded 기반 상단 주황 배너 오버레이(#local-preview-banner) + "NAS 에서 열기" 앵커로 사용자 클릭 기반 이동 으로 전환. 로컬 파일에서도 본 UI 정상 렌더
  • V8 V6 에서 메뉴 뷰 최하단에 인라인 렌더링되던 <details> 개발 이력 섹션을 사이드바 "정보 > 개발 이력 ↗" 버튼 + 새 팝업 창(980×860) 으로 승격. window.open() + document.write 로 독립 CSS 렌더링, 본 창의 다크/라이트 테마 동기화, 인쇄/닫기 버튼, @media print 인쇄 친화 CSS 포함
  • 데이터 소스는 <div id="dev-history-source" hidden> 로 본 문서에 보존 — 팝업은 이 DOM 을 outerHTML 로 복제해 렌더
  • 교훈: 새 창의 document.write 는 "이미 파싱된 현재 문서 덮어쓰기" 와 달리 about:blank 상태라 정책 제한 없음. 단, 사용자 제스처 핸들러 안에서만 호출해야 팝업 차단 회피
2026-04-21
어제
UI모던 미니멀 디자인 이식 & 당일 안정화 (V1–V6)
  • V1 모던 미니멀 CSS 오버레이 이식 — 사이드바 + 카드 그리드, Pretendard Variable 폰트, 이모지 → SVG mask-image 전환
  • V2 반응형 4구간(데스크톱·태블릿·모바일·초소형) + inline-style override 패턴 적용
  • V3 backdrop-filter 제거 + 인터랙션 레이어 명시 — 메뉴 카드 클릭 미반응 치명 버그 해결
  • V4 display:flex !important 제거 — 로그인 화면과 메뉴 화면이 동시 표시되던 이중 노출 버그 해결
  • V5 file:// 진입 시 안내 화면 + 5중 폴백(카운트다운·meta refresh·수동 버튼·setInterval·throw) — 로컬 사본 더블클릭 UX 개선
  • V6 개발 이력 타임라인 섹션 신설 (현재 섹션)
2026-04-20
성능·테마
PERF로딩 성능 최적화 & 테마 일관성 정비
  • CDN defer 인시던트 & 긴급 롤백 — Chart·XLSX 전역 TDZ 장애 → DOMContentLoaded 안전 호출 규칙 수립
  • pre-warm 상시화 — /api/fuel-info 응답 ~0.015 s 상시 달성, 재시작 후 25 s 자동 워밍
  • 라이트 모드 차트 라벨 가독성 — Chart.js Canvas 에 CSS var() 금지 원칙 수립, chartTxt()/chartTick() 헬퍼 도입
  • 뷰 겹침·테마 토글 일관성 — showView() views 배열 정비, WCAG AA 텍스트 대비 달성
  • 청구처 순위 정렬 기준 토글 신규 기능 — 영업/대표 관점 UX 반영
2026-04-17
운영
FEAT유류비 점검 뷰 추가
  • 장비별 주유량·주기 분석 · 사업장별 비교 리포트
  • 36개 월별 파일 pre-warm 루틴 추가 (서버 기동 시 순차 로딩)
2026-04-16
정비
FIX사업장 기본정보 뷰 보완
  • 시설 현황·인프라·보관능력·전경사진 섹션 정비
  • facility 볼륨 마운트 경로 점검 (I-1 이슈 지속 추적)
2026-04-10
기획
FEAT사업장 기본정보 뷰 기획 & 초기 구현
  • DWLG·DWLT·BNCC 3개 사업장 시설 데이터 구조 정의
  • facility 폴더 마운트 및 이미지·JSON 서빙 엔드포인트 설계
2026-04-09
데이터
OPS정부지원사업 메일링 · rowdata 점검
  • gov-mailer 컨테이너 운영 루틴 정비 — 정부지원사업 알림 자동화
  • rowdata JSON 캐시 워커(Worker Thread) 정합성 점검
2026-04-08
기능
FEAT일일매출현황 수정 · 정부지원사업 챗봇 POC
  • 일일매출 청구처별 분석 로직 · SSE 실시간 갱신 (15 s 재연결)
  • 정부지원사업 챗봇 응답 포맷 초안 검토
2026-04-07
수정
FIX가마감 작업 · 로그인 비밀번호 오류 수정
  • 가마감 뷰 SSE 연동 초기 설계 (현재는 dead code — 재활성화 대기)
  • 로그인 x-dashboard-password 헤더 전달 오류 수정
2026-04-06
배포
OPSNAS 배포 자동화 (paramiko 기반)
  • nas_oneui_deploy.py 스크립트 도입 — base64 청크 업로드(60 KB) + md5 검증 + docker restart + health check 일원화
  • SSH 키 인증(id_ed25519_nas) 전환 — 비밀번호 입력 제거
2026-04-04
구현
FEAT대시보드 구현 초안 & 데이터 구체화
  • 손익현황·일일매출·인원현황 3대 핵심 뷰 HTML 구조 작성
  • 엑셀 시트 → JSON 매핑 규칙 정의 (연도 경로 패턴 포함)
2026-04-03
시작
OPS프로젝트 착수 — NAS 인프라 설계
  • Synology DS720+ 단일 Docker 컨테이너(Node.js) 아키텍처 확정
  • /volume1 데이터 마운트 (data1·data2·data3·data4·data5 + facility) 구조 정의
  • 포트 3001 게시 · 내부망 단독 서비스 결정
📊 부산본부 손익현황
NAS 서버 연결 대기중
연결 중...
데이터 불러오는 중...
NAS 서버에 연결하고 있습니다
⚠️
연결 실패
서버에 접근할 수 없습니다.
연도: |
기간
선택: — Ctrl+클릭으로 복수선택
부산본부 전체 요약
사업장별 P&L 현황
전년 동기 대비 (YoY)
100% 이상 달성
90~99% 근접
90% 미만 미달
단위: 억원 (소수 1자리)
—
—
🔄 파일이 변경되었습니다. 데이터를 다시 불러옵니다...
—
—
전년(억원) = 전년도 실적
당기(억원) = 당기 실적
달성율 = 당기실적 ÷ 당기계획
전년比 = (당기 − 전년) ÷ |전년| × 100  |  ▲ 이익증가 / 비용감소  ▼ 이익감소 / 비용증가
👥 부산본부 인원현황
NAS 서버 연결 중...
연결 중
인원 데이터 불러오는 중...
비상연락망 파일을 가져오고 있습니다
⚠️
로드 실패
📊 인원 현황 요약
🎂 이달의 생일자
🏢 사업장별 인원 편성
📅 부산본부 일일매출현황
NAS 서버 연결 중...
🏢 부산본부 관리팀
연결 중
데이터 불러오는 중...
NAS에서 엑셀 파일을 가져오고 있습니다
⚠️
로드 실패
🔄 파일이 변경되어 데이터를 다시 불러옵니다...
⏳ 데이터 분석 중...
당월 데이터 대기 중...
전월 데이터 대기 중...
HKNC 작업현황 대기 중...
⚠️ 합계 불일치 감지 엑셀 합계행 vs 계산 합계의 차이가 임계값 초과:
사업장별 매출 달성율
전월(목표)
당월(달성)
부산본부 일일 매출 추이 (사업장 누적)
DWLG
DWLT
BNCC
📊 청구처 순위 정렬 기준:
선택한 월의 매출액이 큰 순으로 정렬
일일 매출 추이
HKNC 작업 비율 (당월 누적)
20ft 수출
0
당월
0
전월
40ft 수출
0
당월
0
전월
HKNC 일일 작업현황
전월 20ft
당월 20ft
전월 40ft
당월 40ft
청구처별 매출 순위 (Top 15)
🏢 부산본부 사업장 기본정보
각 사업장별 주요 시설·장비·소방·편의시설 통합 현황
사업장
SERVER HEALTHY
DWLG
현재 선택 사업장
섹션
i기본 정보
⚙장비 운영 현황
🔥소방시설
◈편의시설
사용자 편집 (V19.1)
🛡보안
⌛
사업장 정보를 불러오는 중...
미저장 변경: 0개
📊 비용분석 (RAW)
V22.31 — 실제 RAW 16M · 거래처 라이프사이클 · 일별 전표 흐름
연결 중
사업장
기간
단위
§1 HEADLINE KPI 5분 헬스체크 매출·원가·원가율·괴리·워닝 — 한 줄로 본 본부 현황
📈 매출 (당월)
—
💸 원가합계 (당월)
—
📉 원가율
—%
⚡ MoM 매출-원가 괴리
—%p
매출보다 원가가 더 빨리 움직이면 위험
🚨 이상 워닝 (당월)
0 Critical 0 Warn
단건 이상전표 0 건 · 적요 공란 0%
§2 수익성 시계열 매출 vs 원가 추이 + 원가율 듀얼축 — 좌: 매출/원가(백만원), 우: 원가율(%) · 호버 시 MoM/괴리 표시
§3 비용 구조 최근월 항목별 구성 + MoM% 트리맵 좌: 비중 도넛 · 우: 면적=금액, 색=MoM% (빨강↑/파랑↓) — D3 squarified
🍩최근월 구성비 (Top 8 + 기타)
🗺️비용 히트맵 — 면적=금액 · 색=MoM%
§4 항목별 추이 14개월 스택 영역 + 전월 비교 계절성·일회성 식별 — 토글로 절대값/비중 전환
📈항목별 월별 스택 (상위 8개)
📊전월 vs 당월 (Top 12 · MoM%) —
§5 거래처 분석 집중도 + Pareto + 월별 추이 TOP 5 집중도가 50%↑ 면 단일 의존 리스크 — 외주 다변화 검토 필요
🏆거래처 의존도 (TOP 15 누계 + Pareto) —
🥧TOP 5 집중도
TOP 5 점유율 0%
— 본부 전체 외주비 중 TOP 5 거래처 의존도
§6 이상징후 위험도 점수 + 이상 대형 단건 전표 ⬆ 비용이 늘어난 항목만 노출 (절감은 제외) · 4가지 기준(전월 대비·평균 대비·최고기록·평소 패턴) 동시 충족 시 🔴위험 · 1가지만 충족 시 🔵참고 / 우측: 평소의 5배 & 1천만원↑ 거래
⚠️비용 급증 항목 (전월 → 당월)
🔴 위험 Critical
0
🟡 주의 Warning
0
🔵 참고 Info
0
🔎유난히 큰 단건 거래 (평소보다 5배 & 1천만원 이상)
일자항목거래처 금액(천원)배율적요
§7 회계 품질 모니터링 결산 신뢰성 지표 사유 입력률(거버넌스) · 월말 집중도(결산 미루기) · 적요 공란(추적성)
📝사유 입력률
78%
31건 중 24건 입력 완료 · 14일+ 미입력 2건
📅월말(25–31일) 집중도
42%
전월 38% 대비 +4%p · 결산 미루기 의심도 ↑
⬜적요 공란 비율
3.2%
전월 4.1% 대비 -0.9%p · 추적성 양호
§8 거래처 라이프사이클 신규 / 활성 / 코어 / 간헐 / 이탈 종적 신규 = 최근 3M 진입 · 이탈 = 4M+ 미거래 · 코어 = 12M+ 지속 · 외주 다변화/단일 의존 리스크 모니터링
📅거래처 활동 타임라인 (TOP 60 거래액 순) —
🥧전체 거래처 상태 분포
§9 일별 전표 흐름 월말 집중도 심화판 — 16M × 31일 히트맵 + 월말 비중 월말(25–31일) 집중도가 80%↑ 이면 결산 후처리 의심 — 일중 분산 권장
🔥일별 전표 금액 히트맵 (16M × 31일) —
📊월별 월말(25-31일) 집중도
비용분석(RAW) V21 · 기존 v19 변동성 점검 + 첨부 원가분석 데이터 탐색 베스트믹스 · 데이터: RAW-비용분석.xlsx (16M, 60,781행 분개) · 정적 mockup
계정과목 14개월 트렌드
📈—
2025-01 ~ 2026-04 (16개월) · 본부 통합
ESC · 바깥 클릭 · ✕ 버튼 — 어느 방식으로든 닫을 수 있어요
유류비 점검
주유 데이터 분석 중...
부산본부 관리팀
연결됨
사업장
연도
월
윈도우: 12개월 (B-2 후 24개월) 📅 갱신: —
📖 점검 5대 원칙4-baseline · 단가/수량 분리 · z-score 임계값 · 인과분해 · 신뢰도 점수 상세 보기 →
⚠
데이터 로딩 중…
4-baseline (MoM·YoY·MA12·Plan) 신뢰도 점수 + z-score 통계 임계값 기반 자동 검출. B-2 활성 후 거래처/가동시간 룰 추가.
0
Critical
0
Warning
0
Info
—
최대 z
사업장 비용 분포 본부 합산 + 사업장별 4 카드
단가 스프레드 · 사업장 비용 점유율
단가 스프레드 (본부 vs 시장)
사업장 비용 점유율
12개월 추이
12개월 추이 — 비용(막대) · 단가(라인 ±2σ band)
비용 (₩) 단가 (원/L) ±2σ band * 시장 경유가 라인은 B-3 활성
워닝 분류 Critical · Warning · Info
📖 점검 5대 원칙
1
4-baseline 동시 비교
단일 MoM(전월) 비교는 외부 시장 충격을 본부 운영 부실로 오해할 위험. MoM(급변) · YoY(계절성 제거) · MA12(장기추세) · Plan(예산약속) 4축을 동시에 보고, 3개 이상 동일 방향 이탈 = 🔴 Critical, 2개 이탈 = 🟡 Warning, 1개 = 관찰.
베이스라인 4축
2
단가(원/L) vs 수량(L) 분리
"왜 비용이 늘었나" 를 단가 충격(외부 시장 + 협상 효율) 인지 수량 증가(운영 활동량) 인지 구분. 유류비 = 단가 × 수량 분해는 항상 가능하므로, 두 축을 별도 KPI 와 별도 차트로 표현.
변동요인 분리
3
통계적 임계값 (z-score)
단가는 24개월 시리즈에서 z-score 산정 — |z|>2 = Warning, |z|>3 = Critical. 수량/비용은 사업장별 MoM ±30% Warning, ±60% Critical. 탱크 용량 초과는 always Critical, 동일일 다중 주유는 Info(입력 오류 가능성).
통계 룰박스
4
인과분해 (Variance Decomposition)
워닝 발생 시 자동 분해. 예: "3월 유류비 +12% = 단가 +8% (시장 +6% × 본부 spread +2%) + 수량 +4%". waterfall 차트로 표시 — "어디서 늘어났나" 를 그래프로 한눈에.
waterfall 분해
5
신뢰도 점수 + 자연어 컨텍스트
워닝마다 4/4 즉시 검토 · 3/4 Critical · 2/4 Warning · 1/4 관찰 신뢰도 점수. 자연어 컨텍스트 자동 생성 — 예: "본부 단가 +18% 인데 시장 +6% → 협상 효율 악화 또는 입고 시점 불리". False positive 를 신뢰도로 1차 필터링.
False positive 필터
⚠️
제목
📊 핵심 지표 (당월·전월·MoM·12개월 평균·z-score)
📈 12개월 추이 (±2σ band)
🏷 거래처/주유소 분해
ESC 또는 외곽 클릭으로 닫기 · B-1 단계는 단가 z + MoM 임시 룰. B-2/B-3 진행 시 4-baseline + 거래처 분해 + 인과분해 활성
현금흐름
DWLG · BNCC 통합 재무 안정성 + 자금흐름 + 예금·대여·차입
부산본부 관리팀

부산본부 재무현황 대시보드

KPI 기준: 2025-10 ~ 2026-03 (직전 6개월)
현금흐름: 2026-03-31 ~ 2026-09-30  |  기준일: 2026-03-31
사업장별 재무 안정성 KPI (2026-03 기준)
DWLG
유동비율
16.9%
▲ +0.6%p (10월比)
부채비율
158.2%
▼ −8.9%p (10월比)
차입의존비율
42.2%
▼ −15.4%p (10월比)
BNCC
유동비율
323.7%
▼ −500.8%p (10월比)
부채비율
87.7%
▲ +72.9%p (10월比)
차입의존비율
0.0%
6개월 연속
현금흐름 현황 2026-03-31 ~ 2026-09-30 · Y축: 억원
부산본부 현금흐름
DWLG 최저 0.25억 (05-22) · 리스크 6건  |  BNCC 최저 1.91억 (07-10) · 리스크 1건
DWLG 잔액 BNCC 잔액 DWLG 최소유지기준 (5억) BNCC 최소유지기준 (2억)
DWLG
현재잔액 (03-31)
14.58억
최저잔액 (05-22)
0.25억
유동성 리스크 / 기준 5억
6건
BNCC
현재잔액 (04-27)
4.77억
최저잔액 (07-10)
1.91억
유동성 리스크 / 기준 2억
1건
예금 · 대여 · 차입 통합 요약
예금 합계  📋 클릭
—
DWLG 0건 / BNCC 0건
대여 합계  📋 클릭
72억원
DWLG 50억(2건) + BNCC 22억(2건)
차입 합계  📋 클릭
237.2억원
DWLG 40건 / BNCC 0건
출처: DWLG·BNCC 재무제표/손익계산서/자금조달운영/cashflow 파일  |  기준일: 2026-04-26
현금흐름 현황 2026-03-31 ~ 2026-09-30
현재잔액 · 2026-03-31
14.58억
1,458,344,513원
예측 기산일 기준 잔액
최저잔액
0.25억
25,257,893원 · 2026-05-22
기준선(5억) 대비 −4.75억
유동성 리스크 발생
6건
기준선(5억) 미달 구간 존재
5/20·5/22·6/20·7/20·8/20·9/20
DWLG 현금흐름
Y축: 억원 · 빨간 실선: 최소유지기준 5억  |  📋 클릭하면 상세 내역 확인
현금잔액 최소유지기준(5억)
재무 안정성 KPI (직전 6개월: 2025-10 ~ 2026-03)
유동비율 · 2026-03
16.9%
▲ +0.6%p (10월 대비)
유동성장기부채(건설자금 연간상환분 ~2,239억) 기인
부채비율 · 2026-03
158.2%
▼ −8.9%p (10월 대비)
10~11월 167%대 → 12월 이후 158%대 안정화
차입의존비율 · 2026-03
42.2%
▼ −15.4%p (10월 대비)
금융기관 차입금 ÷ 총자산 기준
월별 손익 추이  📋 클릭 → 데이터 보기
막대=매출·원가, 선=영업이익·순이익(우축) · 단위: 백만원
매출 매출원가 영업이익 당기순이익
* 2025-12 당기순이익 17.4M: 법인세비용 환급(280M) 반영
안정성 지표 추이
좌축=유동비율, 우축=부채비율·차입의존비율
유동(좌) 부채(우) 차입의존(우)
예금 · 대여 · 차입 현황
예금  📋 클릭
—
해당 없음 (0건)
대여  📋 클릭
50억원
2건 · (주)대우로지스틱스 · 4.6%
차입  📋 클릭
237.2억원
40건 · 신한은행 · 4.57~5.78%
출처: DWLG_재무제표.xlsx · DWLG_손익계산서.xlsx · DWLG_자금조달운영.xlsx · cashflow_single_template_DWLG.xlsx  |  현금흐름 최소유지기준: 5억원
현금흐름 현황 2026-03-31 ~ 2026-09-30
현재잔액 · 2026-04-27
4.77억
476,690,918원
예측 기산일 기준 잔액
최저잔액
1.91억
191,410,918원 · 2026-07-10
기준선(2억) 대비 −0.09억
유동성 리스크 발생
1건
기준선(2억) 미달 구간 존재
7/10 (1.91억) · 기준선 대비 −0.09억
BNCC 현금흐름
Y축: 억원 · 빨간 실선: 최소유지기준 2억  |  📋 클릭하면 상세 내역 확인
현금잔액 최소유지기준(2억)
재무 안정성 KPI (직전 6개월: 2025-10 ~ 2026-03)
유동비율 · 2026-03
323.7%
▼ −500.8%p (10월 대비)
권장 기준 200% 이상 (12월~이후 IFRS16 반영 기준)
부채비율 · 2026-03
87.7%
▲ +72.9%p (10월 대비)
권장 기준 200% 이하 · 추세적 개선 중
차입의존비율 · 2026-03
0.0%
6개월 연속 0%
금융기관 차입금 없음
월별 손익 추이  📋 클릭 → 데이터 보기
막대=매출·원가, 선=영업이익·순이익(우축) · 단위: 백만원
매출 매출원가 영업이익 당기순이익
* 2025-12 순손실 155M: 리스이자비용 일시 반영(122M) 포함
안정성 지표 추이
좌축=유동비율, 우축=부채비율
유동비율(좌) 부채비율(우)
예금 · 대여 · 차입 현황
예금  📋 클릭
—
해당 없음 (0건)
대여  📋 클릭
22억원
2건 · (주)대우로지스틱스 · 4.6%
차입  📋 클릭
—
해당 없음 (0건)
출처: BNCC_재무제표.xlsx · BNCC_손익계산서.xlsx · BNCC_자금조달운영.xlsx · BNCC_자금흐름.xlsx  |  현금흐름 최소유지기준: 2억원
A4 미리보기 · 소제목을 클릭하여 직접 수정하세요
기준일자 :
1. 현금흐름 상세
2. 현금흐름 차트
3. 예금·대여·차입 현황
부산본부 재무현황 대시보드
화면 바깥 클릭 또는 닫기 버튼으로 닫힙니다