HBTM220_근무 관리 (연장·휴일/외근)
(Hereby) 내부 결재 > 근무 관리 (연장·휴일/외근)
| 앱 | 내부 결재 (HBTM) |
| 대상 사용자 | 조건1 [TEAM_LEADER] / 조건2 [SUPER_ADMIN] |
| 프로토타입 | TBU |
| 관련 정책 | [[POL_결재워크플로우]] |
| Status | review |
| Owner | 카나 (PM) |
개요
- 근무 관리 탭(/team-manager/work)은 서브탭 2종("연장·휴일 근무" / "외근") + 기간 quick filter 6종 + 목록으로 구성된 근무 승인 큐다.
- 연장·휴일은 overtime_records, 외근은 offsite_work_records를 각각 소비한다. 외근 목록은 "장소" 컬럼(offsite 고유), 연장 목록은 "유형(평일·야간·토요일)" 컬럼을 가진다.
- 상태는 승인 단계(대기/승인/반려/취소) 외에 보고서 단계("보고서 대기" / "보고서 승인" / "보고서 반려")를 포함하는 2단계 상태머신이다.
- row action "보기"로 상세를 연다. 외근 상세 모달의 "승인"은 컨펌 없이 즉시 실행되며, 승인 시 직원 앱([[HBEA421_보고서 제출]])에 "외근 보고서 작성" 버튼이 트리거된다. 승인 후에는 취소 경로가 없다(연차/휴가 큐의 "취소" 액션과 비대칭).
- 신청 원천은 [[HBEA420_근무 신청 (연장·휴일/외근)]], 승인된 연장은 HR 급여시트([[HBHR220_연장근무 내역 시트]])로 반영된다.
문서 히스토리
| 날짜 | 작성자 | 팀 | 변경 내용 |
|---|---|---|---|
| 2026-07-06 | 카나 | PM | 최초 작성 |
| 2026-07-10 | 카나 | PM | 라이브 기능 검토 반영 |
| 2026-07-12 | 카나 | PM | 실행 검증 반영 (콸 승인) |
| 2026-07-13 | 카나 | PM | 버튼 행동 명세 보강 |
| 2026-07-21 | 카나 | PM | GC700 깊이 재작성 + 확정 스타일 |
| 2026-07-21 | 카나 | PM | TC 크로스체크 확정분 반영 (🟡) |
| 2026-07-28 | 카나 | PM | 코드 변경 반영(8299588) |
| 2026-07-29 | 카나 | PM | 코드 변경 반영(7c2eab6) |
Data I/O
Server → Client
| 변수 | 엔티티.컬럼 | 값 없을 때 | 비고 |
|---|---|---|---|
| $ | overtime_records[] (team scope) | 빈 목록 | 연장·휴일 근무 목록. category = weekday/night/weekend |
| $ | offsite_work_records[] (team scope) | 빈 목록 | 외근 목록. location NOT NULL |
| $ | 기간 quick filter 선택 값 | "전체" (추정) | 6종 프리셋 중 활성 값 |
| $ | offsite_work_records.workDate / startTime / endTime / workHours / typeId / location / reason | - | 외근 상세 모달 필드 |
| $ | offsite_work_records.approvedBy / approvedAt / auditLog(jsonb) | - | 보고서 결재 모달 승인 내역(✓결재자·결재 일시 — 감사추적) |
| $ | offsite_work_records.report(jsonb) | - | 외근 보고서 섹션(실제 외근 시간·제출일) |
Client → Server
| 액션 | 전달 값 | tRPC procedure | 권한 토큰 |
|---|---|---|---|
| 상세 보기 | requestId | sdk.teamManager.work.detail() (추정, read-only) | work:read (추정) |
| 연장 승인 | ids[] | teamManager.approveOvertime(ids[]) (TC 확정: 02:2.4) | work:approve (추정) |
| 외근 승인 | requestId | sdk.teamManager.work.approve() (추정) | work:approve (추정) |
| 외근 반려 | requestId, reason? | sdk.teamManager.work.reject() (추정) | work:approve (추정) |
| 연장 보고서 승인 | ids[] | teamManager.approveOvertimeReport(ids[]) (TC 확정: 10:10.3) — overtimeHours←actualHours 재기록 | work:approve (추정) |
| 보고서 승인 (외근) | requestId, reportId? | sdk.teamManager.work.approveReport() (추정) | work:approve (추정) |
| 보고서 반려 | requestId, reportId?, reason? | sdk.teamManager.work.rejectReport() (추정) | work:approve (추정) |
큐 조회 =
teamManager.listOvertime({status: TeamManagerStatusFilter.PENDING}). 연장 파이프라인: 유저overtime.request(PENDING) → 팀장teamManager.approveOvertime(ids[])→ 유저overtime.submitReport(id, actualHours, note)→ 팀장teamManager.approveOvertimeReport(ids[])(overtimeHours←actualHours 재기록). 급여 집계 게이트: approved/report_ 만 집계, PENDING 제외*(2026-06-11 게이트, [[HBHR220_연장근무 내역 시트]]). (TC 확정: 02:2.4 / 10:10.1~10.3)
외부 통합 (cross-app)
| 외부 시스템 | 데이터 | 방향 | 비고 |
|---|---|---|---|
| 직원 앱 [[HBEA420_근무 신청 (연장·휴일/외근)]] | 연장·외근 신청 | EA → TM | 신청 원천(대기 큐 진입) |
| 직원 앱 [[HBEA421_보고서 제출]] | 외근 보고서 작성·제출 | TM → EA → TM | 승인 시 "외근 보고서 작성" 트리거 → 제출 시 "보고서 대기"로 복귀 |
| HR 급여시트 [[HBHR220_연장근무 내역 시트]] | 승인된 연장근무 | TM → HR | 급여 입력 반영 |
화면 플로우
근무 관리 사양표
| 번호 | 항목/기능명 | 타입 | 사양 및 기능 설명 | 연관/영향 범위 | 비고 |
|---|---|---|---|---|---|
| 00 | 노출 조건 | - | 조건1 [TEAM_LEADER] 이상 인증 세션 · "근무 관리" 탭 클릭 시 /team-manager/work 진입 | 인증 세션 | token 내부 key TBU |
| 01 | 초기 상태 | - | 진입 시 "연장·휴일 근무" 서브탭 활성 + 기간 quick filter 기본값으로 목록 조회 | 30 목록 | 기본 필터 값 TBU |
| 10 | 서브탭 | Tab | • 연장·휴일 근무 큐와 외근 큐를 전환하는 2종 탭 원문 "연장·휴일 근무" / "외근" • 클릭 시: 해당 큐로 전환 → 목록 재렌더 - CASE01 [연장·휴일 근무]: 30 목록(overtime_records) - CASE02 [외근]: 40 목록(offsite_work_records, 장소 컬럼) | 30 / 40 | |
| 20 | 기간 quick filter | Button group | • 목록 조회 기간을 프리셋으로 좁히는 기능 원문 6종 "이번 주" / "지난 1주" / "이번 달" / "지난 1개월" / "지난 3개월" / "전체". 날 유틸 칩을 공용 SubTab(sm·line·radius 4)으로 통일(보조 필터 위계) • 클릭 시: 선택 기간으로 목록 재조회 | 30 / 40 목록 재조회 | 프리셋 SubTab 통일 (코드 반영: 8299588) |
| 25 | 검색 | Input | • 연장·휴일 근무 / 외근 목록 검색. placeholder "사번 / 이름 / 이메일" (원문). 근태 현황과 동일 규약: 176×32px, 0.3초 debounce, 필터 줄 오른쪽 끝 고정. 서버 페이지네이션 목록이라 teamManager.overtime.list / offsiteWork.list에 search 파라미터로 재조회 • 입력 시: 해당 직원 필터 - CASE01 [결과 없음]: Empty | 30 / 40 목록 재조회 (search 파라미터) | 신규 (코드 반영: 8299588) · TBU: 검색 결과 없음 문구 |
| 30 | 연장·휴일 목록 | Table | • 연장·휴일 근무 승인 큐 컬럼: 사번 / 이름 / 근무일 / 연장 시간 / 유형 / 상태 / 관리 • row "보기"로 상세 진입 • 근무일이 익일 종료면 "(익일 퇴근)" 접미 표기, 연장 시간을 소수 대신 h:mm(8:30)으로 표기 | overtime_records · nextDayWorkSuffix · fmtHM(item.overtimeHours) | "(익일 퇴근)" 접미·h:mm 표기 (코드 반영: 7c2eab6) |
| ㄴ31 | 연장 유형 | Badge | • 연장근무 분류 표기 원문 "평일" / "야간" / "토요일" • 표시 전용 | overtime_records.category (weekday/night/weekend) | category enum ↔ 라벨 매핑 TBU |
| ㄴ32 | 연장 상태 | Badge | • 승인·보고서 단계 상태 표기 "대기" / "승인" / "반려" / "취소" / "보고서 대기" / "보고서 승인" / "보고서 반려" • 표시 전용 | overtime_records.status | 보고서 단계 포함 |
| 40 | 외근 목록 | Table | • 외근 승인 큐 컬럼: 사번 / 이름 / 근무일 / 외근 시간 / 장소 / 유형 / 상태 / 관리 ("장소" = 외근 고유 컬럼) • row "보기"로 상세 진입 • 근무일이 익일 종료면 "(익일 퇴근)" 접미 표기, 외근 시간을 소수 대신 h:mm(8:30)으로 표기 | offsite_work_records (location) · nextDayWorkSuffix · fmtHM(item.workHours) | "(익일 퇴근)" 접미·h:mm 표기 (코드 반영: 7c2eab6) |
| ㄴ41 | 외근 상태 | Badge | • 승인·보고서 단계 상태 표기 "대기" / "승인" / "반려" / "취소" / "보고서 대기" / "보고서 승인" / "보고서 반려" • 표시 전용 | offsite_work_records.status | 보고서 단계 포함 |
| ㄴ42 | 목록 행 체크박스 · 일괄 결재 바 | Checkbox | • 목록 행별 선택 체크박스 — 일괄 처리용. 일괄 결재 바는 필터 줄이 아니라 표 바로 위 별도 줄로 이동(연장·외근 공통) — 행 체크 시 검색창이 밀리던 문제를 없앰. 일괄 승인 액션: bulkApproveOvertime / bulkApproveOvertimeReport • 체크 시: 선택 행 대상 일괄 결재 바 노출 | offsite_work_records · overtime_records | 일괄 결재 바 위치 확정(표 위 별도 줄) (코드 반영: 8299588) · TBU: 일괄 반려 동작 |
| 50 | 보기 | Button | • 해당 행의 상세 모달을 여는 기능. 외근은 "외근 상세 — 외근 신청서" 모달로 열린다 • 클릭 시: 상세 모달 오픈 - CASE01 [대기/승인 건]: 60 외근 상세 모달 - CASE02 [보고서 대기 건]: 상세 모달이 확장되어 오픈(63 보고서 결재) | 60 / 63 | |
| 60 | 외근 상세 모달 | Modal | • 외근 신청 내역 상세 모달. 제목 "외근 상세 — 외근 신청서" • 필드: 직원 / 근무일 / 시작·종료 시간 / 외근 시간 / 유형 / 장소 / 사유 • 하단 버튼 "닫기" / "반려" / "승인" • 근무일이 익일 종료면 "(익일 퇴근)" 접미, 외근 시간·보고서 실제 외근 시간을 h:mm(8:30)으로 표기 | offsite_work_records · nextDayWorkSuffix · fmtHM(workHours/report.actualHours) | "(익일 퇴근)" 접미·h:mm 표기 (코드 반영: 7c2eab6) |
| ㄴ61 | 승인 | Button | • 외근 신청을 승인하고 보고서 flow를 트리거하는 기능 — ★위험도=즉시 실행 • 클릭 시: 즉시 실행(컨펌 없음), 성공: 상태 대기→승인 - STEP1 offsite_work_records.status 승인 전이 - STEP2 EA 신청자 화면에 "외근 보고서 작성" 버튼 노출 트리거 | [[HBEA421_보고서 제출]] | 즉시 실행 · destructive |
| ㄴ62 | 승인 후 상세 | Modal | • 승인 완료 건 재조회 시 상세 모달은 "닫기"만 노출 — 외근은 승인 취소 경로 없음(연차/휴가 큐의 "취소" 액션과 비대칭) • 닫기 클릭 시: 모달 닫힘 | offsite_work_records | 취소 경로 없음 (OQ) |
| ㄴ63 | 보고서 결재 모달 | Modal | • "보고서 대기" 건 "보기" 시 확장되는 결재 모달. 구성: ① 외근 신청서 + ② 승인 내역(✓ 결재자·결재 일시 — 감사추적) + ③ 외근 보고서 섹션(실제 외근 시간·제출일) + 하단 "보고서 반려" / "보고서 승인" • 보고서 대기 건 "보기" 클릭 시: 본 확장 모달 오픈 | offsite_work_records.report · auditLog · [[HBEA421_보고서 제출]] | 감사추적 = auditLog(jsonb NOT NULL) |
| ㄴ64 | 보고서 승인 | Button | • 제출된 외근 보고서를 승인하는 기능 • 클릭 시: 즉시 실행(컨펌 없음), 성공: 상태 "보고서 승인" | offsite_work_records.status | 즉시 실행 |
| ㄴ65 | 보고서 반려 | Button | • 제출된 외근 보고서를 반려하는 기능 • 클릭 시: 보고서 반려 처리, 성공: 상태 "보고서 반려" | offsite_work_records.status · rejectionReason | 반려 사유 UI TBU |
| 70 | 52시간 가드 | Info | • docs 기준 연장 승인 시 주 52시간 경고 존재. 6/30 라이브 리스트·상세에는 경고 UI 미노출 • 노출 조건: TBU | attendances + overtime 집계 | docs 확정 / live UI 미노출 (TBU) |
권한·상태 분기 요약
| 시나리오 | 트리거 | 결과 | 비고 |
|---|---|---|---|
CASE01 [상세 보기] | row "보기" | 외근 상세 모달(대기/승인) 또는 확장 보고서 결재 모달(보고서 대기) | read-only |
CASE02 [외근 승인] | 외근 상세 모달 "승인" | 즉시 실행 → 상태 승인 + "외근 보고서 작성" 트리거 | 컨펌 없음 · destructive |
CASE03 [외근 반려] | 외근 상세 모달 "반려" | 상태 반려 | destructive |
CASE04 [보고서 승인] | 보고서 결재 모달 "보고서 승인" | 즉시 실행 → 상태 "보고서 승인" | 컨펌 없음 |
CASE05 [보고서 반려] | 보고서 결재 모달 "보고서 반려" | 상태 "보고서 반려" | destructive |
조건1 [TEAM_LEADER] | 근무 관리 탭 진입 | 팀 scope 큐 노출 | token 내부 key TBU |
Open Question
- 외근 승인 후 승인 취소 경로 부재 — 연차/휴가 큐는 승인/반려 건에 "취소" 액션이 있으나 외근은 승인 후 "닫기"만 존재. 큐 간 액션 비대칭이 의도된 정책인지 = OQ
- 목록 행 체크박스 일괄 처리 액션(일괄 승인/반려) 동작 및 액션 버튼 소재 = TBU
- 52시간 경고 UI 라이브 노출 여부 (docs 명문화 / live 리스트·상세 미노출) = TBU
- 연장 유형(평일·야간·토요일) ↔ overtime_types / category enum 매핑 = TBU
- 보고서 반려 사유 입력 UI 존재 여부 (offsite_work_records.rejectionReason) = TBU
- 권한 토큰(work:approve 등) 내부 key = TBU