포털 · 메인 메뉴
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-kpigrid 규칙 누락 (이전.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". JSopenAccountModal/openVendorModal두 함수의getElementById4개 호출도 동일 갱신 (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-overlaybase 규칙 +cv21FadeIn/cv21PopInkeyframes 추가. 다크/라이트 양쪽 토큰 사용. - 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중) —
cyclePtsLimit0.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 × cyclePtsLimit3중 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-eqwrapper +f3-spread-trend-chartcanvas 모두 제거. 워닝 분류는 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_renderSectionamen 분기에서 직전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_updateSectionImage4 위치의<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 + pathprefix. 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/--borderCSS 변수 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모듈 변수 +memoExtractClientsFromRawwrapper + 호출 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 Date2026-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-388datetime 분기). 기존 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 외부 도메인 → 내부 IP1.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:avoidtr 단위 +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_openMemoHistory2줄 글로벌 노출. 이벤트 위임 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클래스 토글 — 재렌더링 없이 CSSdisplay: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-completionsbody{client, completed:bool}+If-Matchetag 충돌 감지 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모듈 변수.loadSalesDataPromise.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-completions200 검증 항목 신설.API_PATHS11→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채널 동시 연결. 원칙 8App.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-toastCSS 재사용 (손익현황·일일매출 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/connectSalesSSE4 헬퍼 + 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.padding12/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 === sitefilter. 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.jsresponsive: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-notesbody{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 마감 월로 복귀. - FEAT
refreshAll()함수 신설 — 사용자 월 변경 시 전체 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:2371buildRowdataJson)db[yr][branch].Details[m].Sales[accName][...].Client4-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) / 당년 실적 (PLas_) / 전년 계획 / 전년 실적 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양쪽 동일 함수 참조. - FIX
showView('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 RAW23.33억): 차이 1.25억 = DWLT 4월기타거래처(결산)단일 행 -1.22억 단독 출처. PL 엑셀(영업/매출관리 기준)은 결산조정 미반영, RAW-비용분석(회계전표 기준)은 결산행 포함 → 매월 마감 시 자연 갭 발생. KPI/콤보/사업장 게이지는 이미 RAW 기준이라 정상 표시되나,s_checkIntegrity가 두 합계를 직접 비교해 ±5% 룰 false positive. - FEAT결산조정 보정 로직 신설 —
s_checkIntegrity가 RAWdb[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줄 ·renderHeroKpiKPI ①/④ 계산 ~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줄 ·renderSpreadChartsum 모드 라벨 +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월 전환. - FEAT
trendsBySite응답 신설 — 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.jsapiFetch + 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 --check4 모듈(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_SCHEMA6 → 1 key ·FAC_OVERRIDE_SECTIONS6 → 1 entry ·FAC_SECTION_SUBFOLDER6 → 1 ·FAC_SECTION_IMG_KEYWORDS6 → 1 ·FAC_EXCEL_SECTION_MAP6 → 1 ·V16_SECTION_LABEL6 → 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')무조건 호출 → 다른 사용자 편집으로 SSEoverrides-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_connectSSEoverrides-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 만 이동 + 토스트 안내. - FIX
v16_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장일 때는.singlegrid 로 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 → 신규photoListclosure 캡처 + 클릭한 인덱스로 진입. ← / → 키로 8장 사진 순환 가능. 사업장 기본정보 탭 사용성 향상. - FEAT라이트박스 키보드/접근성 보강 — ESC 닫기 (이전) + ← 이전 + → 다음. 첫/마지막 사진에서 해당 화살표 visibility:hidden 자동. close 버튼 호버 시 빨강 톤.
aria-label3개 (닫기/이전 사진/다음 사진). - 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.jsOK. 빌드 산출물: 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_json3시트 (~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=+sseClientsFacilitySet +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개 시각 노이즈. 신규:
validAddonsfilter 적용 + 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) 으로 "회귀잔차" 라벨이 그대로 노출되던 사각지대.detectVariabilityvariable/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.jsOK. - OPS변경 파일 4개 —
src/server.js(_lsRegressionvalid 플래그 +8줄 ·detectVariabilityfallback 분기 +10줄 · 응답 필드 4개 추가 +6줄 ·computeCostSummaryma12DevPct 추가 +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.jsline 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}가 JSstyle.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 워밍 자동 대기
- FIX
s_currentYearMonth데이터 기반 회계마감 월 fallback — 사용자 보고(2026-05-08, 4월 마감 진행 중): 매출 분석 진입 시 KPI ① 당월 매출 0원 / 12M 끝 라벨 '26.04' 빈 막대. 원인: 캘린더 기반setMonth(d.getMonth() - 1)가 5월 진입 시 무조건 4월 반환 → 4월 마감 미반영(Sales=0) 데이터로 KPI 망가짐. 픽스: 비용분석 V19autoLatestMonth패턴 차용 —_rdCache.db[year].total.{Sales,TotalCOGS}역순 검색 → 첫 양수 월 자동 선택. 결과: 5월 초(4월 마감 전) → 3월 자동 표시. 4월 마감 후 SSE rowdata-watch 자동 갱신 →_rdCache재할당 → 자동 4월 전환 (사용자 개입 0). - OPS
scripts/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`) 다발./health200 은 워밍 완료를 보장하지 않음. 픽스:wait_for_warmup함수 신설 — 헬스체크 통과 후/api/fuel-info5초 간격 폴링, 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-jsondb.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.jsOK. staging smoke 21/21 PASS (1차, 이전 deploy 에서는 워밍 미완료로 11/21). - OPS변경 파일 2개 —
src/js/57-view-sales.js3 위치 (모듈변수_rdCache추가 /loadSalesData의_rdCache = rd갱신 /s_currentYearMonth데이터 기반 분기, +18줄 / -7줄) +scripts/deploy.pywait_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.jsOK. - OPS변경 파일 1개 —
src/server.jsline 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 워닝으로 모달 노출. 원인:
detectVariabilityInfo 룰 (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.jsOK. - OPS변경 파일 1개 —
src/server.jsline 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 가prevByClientmap 으로 우회 매칭만 처리. 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 의
_byClientmap 그룹화 ~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.jsOK. - OPS변경 파일 1개 —
src/server.js4 위치 (메인 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.jsOK. - 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_drawStabMiniChart.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/부산본부_대시보드.htmlline 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.jsOK / staging 재배포 후 V19 7 라우트 모두 정상:cost-classifications90개 계정 자동 분류 (창고일반 기타 → semi-variable r²=0.52 cv=0.32 등) /cost-warnings29건 워닝 (Critical 0 / Warning 14 / Info 15, notesMissingAged 14건) /cost-summary3 카테고리 (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_renderEquipcats배열에 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.jss_clientSeries직후 신규 12줄. - FEATdrop 분기 4 flag — 12M slope + 3M slope 이중 분기 —
s_computeAnomaliesdrop 분기 (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_renderExistingClientsdrop 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.mdV20.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:nowrap3속성 추가.<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_classifyPipelineStatuscurrentStatus 분기 제거) / 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 양쪽). environmentPIPELINE_DIR=/sales-pipelineenv 오버라이드. server.js +상수 + 헬퍼 + 2 라우트 (GET /api/sales-pipeline+/api/sales-pipeline-watchSSE) + chokidar + /healthpipeline: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_computeAnomaliesdrop 분기에prev11 mean/std기반 zScore 산출 +isStatOutlierflag (|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) 위반 → fetchthrow '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 + tagv20.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-28V20.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-28V20.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-28V20.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 필터 적용. - OPS
s_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-28V20.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-28V20.1 매출 분석 — Phase 2 데이터 결선 (사업계획 vs 회계마감 + 거래처 TOP·BOTTOM 실데이터)- FEAT
loadSalesData()빈 함수 → 두 기존 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% 이내 → 미표시. - FIX
MONTHS_24하드코드 라벨 →s_months24Labels()동적 슬라이딩 (current month back 12). enterSales 시점이 5월/6월/...로 변하면 콤보차트 X축 자동 갱신. - UI
s_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-28V20.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-28V19.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-rightflex 컨테이너로. - 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-btnID 그대로 보존하여f_setSite/v16_toggleEditMode/toggleTheme영향 0. 죽은 셀렉터(v16-tbar/brand/blogo/btt/bsb/link/div/tright) CSS 동시 정리. - OPS
portal-hdr글로벌 클래스(30-layout.css:14-22)는 일일매출·인원·유류비·비용분석·현금흐름 등 다른 뷰가 공유하므로 무수정 — 이번 변경은#view-facility스코프에 한정. 타뷰 시각 회귀 0.
2026-04-28V19.2.7 본부 일정 슬라이드쇼 — 파일명 구분자 _ / - / . / 공백 모두 허용- FIX
hqParseTitle()정규식^(\d{6})_(.+?)…→^(\d{6})[_\-.\s]+(.+?)…— 운영팀이260417-MBC 생방송 부라보 방영1.jpg처럼 하이픈으로 작성한 파일이date:''로 파싱 실패하여 슬라이드쇼 필터(line 2442if (!it.date) return false)에서 즉시 제외되던 이슈 해결. - FIX4/17 MBC 부라보 방영 사진 3장 슬라이드쇼 정상 노출 확인. 향후 운영팀 파일명 자율성(언더스코어/하이픈/점/공백) 확보로 동일 사고 재발 차단.
2026-04-28V19.3 현금흐름 (Cash Flow) 통합 재무 대시보드 신설 (v1 시안 1:1 이식, 데이터 연동은 v2 별도)- FEAT사이드바 운영관리 > 현금흐름 메뉴 신설 (비용분석 다음, 유류비 점검 위) + 표준 진입점
App.view.enterCashflow. - FEAT3탭 구조(SUMMARY / DWLG / BNCC) — 시안 박은경 사원 작성
부산본부_통합재무대시보드.html1: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하위, idcf-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-27V19.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.1V19.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 핵심 패치) —
- vehicle+suffix:
4.5톤(7325)-BNCC,16톤(7124)-부원글로벌→ name="4.5톤", id="7325" (사업장/소속사 접미사 무시) - driver:
조문성(4.5T)-04나7459→ name="조문성(4.5T)", id="04나7459" (운전자별 차량 unique) - paren:
45톤장비(8711),30톤(6395),리치(01-1234) - dash:
리치-04나8120,30T-0004가6935,3.5T-004나3796 - aggregate skip:
합계/소계/총계/취합/계/total/sum패턴은continue차단
- vehicle+suffix:
- 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}+ canvasposition:absolute;inset:0!important. Chart.jsmaintainAspectRatio:false사용 시 부모 컨테이너 고정 높이 필수. - FIX클라이언트 isAggregateRow 2차 방어선 — server 차단 후에도 캐시/구버전 데이터 대비
filterRecords에서 합계/소계/총계/취합 이름 + 미확인 id 의 비정상 주유량 행 제외. - OPS
src/js/55-view-fuel.js805→1041줄 (Hero KPI / 24M 콤보 / 워닝 모달 record-level 분기 / 패널 토글). 데이터 레이어(loadFuelData/computeAll/detectFuelWarnings)는 B-1 그대로. - OPS
src/css/55-view-fuel.css210→255줄. 글로벌 토큰 사용 + 반응형 1280/900 브레이크포인트. - OPS
src/server.js830→849줄 (parseFuelSheet 5단계 패턴 + AGG_HEADER_RE 차단 + isVehicleTypeName 헬퍼 + FUEL_CAPACITY 키워드 추가).--include-server-js배포 + 컨테이너 재시작 필수. - OPSSSOT:
1.기초정보/09_유류비_V2_점검모드.mdV3.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('수출')!==-1AND 매칭. 운영 엑셀 헤더 "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-total8개 토큰을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.allSettled1 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.jssuggestedMax = max * 1.10. 5천만 매출이 1억 라운드로 막대 절반만 차지하던 문제 해결. - FIX색 hex 인라인 제거 (A-4) —
#3d8eff/#dceeff등 모두 토큰 read (siteColor())..kpi-lbl의color:#dceeff→var(--text2). 라이트 테마에서 안 보이던 KPI 라벨 가독성 회복. - FIX미매칭 합계 청구처명 기반 (B-6 부록) —
matchedOtherAmtsset 의 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 --check14 모듈 + preflight + 인라인 2 블록 OK. 빌드 산출물 md50e3c7915d50e834a431fa55d1d5ae450(564,368 B, V18.6.1 +6,604 B).51-view-daily.js895 → 1009줄,00-tokens.css+6줄(8 토큰),20-components.css+30줄(KPI grid · integrity bar),51-view-daily.css67 → 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()의regOsHtmlflex 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행 수. - OPS
08 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] 로그인 깨짐 —