- CEO 대시보드: 예상비용, 현황이슈, 일별매출/매입 등 모달 API 연동 확대 - dashboard transformers 리팩토링 (hr, sales-purchase, production-logistics 분리) - useCEODashboard 훅 대폭 확장 (모달 데이터 fetching 로직) - DailyReport: USD 섹션 추가 및 레이아웃 개선 - VendorManagement/ApprovalBox: 소폭 개선 - VacationManagement: 소폭 수정 - component-registry previews 업데이트 - claudedocs: 대시보드 API 스펙, 분석 문서 추가
23 KiB
CEO Dashboard 백엔드 API 명세서
작성일: 2026-03-03
기획서: SAM_ERP_Storyboard_D1.7_260227.pdf p33~60
프론트엔드 타입: src/lib/api/dashboard/types.ts
대상: 백엔드 팀 (Laravel sam-api)
공통 규칙
응답 형식
{
"success": true,
"message": "조회 성공",
"data": { ... }
}
인증
- 모든 API는
Authorization: Bearer {access_token}필수 - Next.js API route 프록시(
/api/proxy/...) 경유
캐싱
sam_stat테이블 5분 캐시 (기존 구현 유지)- 대시보드 API는 실시간성보다 성능 우선
날짜/기간 파라미터 규칙
- 날짜:
YYYY-MM-DD(예:2026-03-03) - 월:
YYYY-MM(예:2026-03) - 분기:
year=2026&quarter=1 - 기본값: 파라미터 미지정 시 당월/당분기 기준
검수 중 발견된 누락 API
N1. 오늘의 이슈 — 과거 이력 저장 및 조회
우선순위: 상
페이지: p34
현상: GET /api/v1/today-issues/summary?date=2026-02-17 호출 시 항상 {"items":[], "total_count":0} 반환. 과거 이슈를 저장하는 구조가 없어서 이전 이슈 탭이 항상 빈 목록.
요구사항:
- 이슈 이력 테이블 필요 (예:
dashboard_issue_history)- 매일 자정(또는 배치) 시점에 당일 이슈 스냅샷 저장
- 또는 이슈 발생 시점에 이력 테이블에 INSERT
- 기존 API 수정:
GET /api/v1/today-issues/summarydate파라미터가 있을 때 해당 날짜의 이력 데이터 반환date파라미터가 없으면 기존대로 실시간 집계
Response (기존 TodayIssueApiResponse와 동일):
{
"items": [
{
"id": "issue-20260302-001",
"badge": "수주",
"notification_type": "sales_order",
"content": "대한건설 수주 3건 접수",
"time": "14:30",
"date": "2026-03-02",
"path": "/ko/sales/order-management",
"needs_approval": false
}
],
"total_count": 5
}
Laravel 힌트:
- 배치 저장 방식:
App\Console\Commands\SnapshotDailyIssues(Schedule::daily) - 또는 이벤트 기반: 수주/채권/재고 변동 시
dashboard_issue_historyINSERT
N2. 자금현황 — 전일 대비 변동률 (daily_change)
우선순위: 중
페이지: p33
현상: GET /api/v1/daily-report/summary 응답에 daily_change 필드가 없음. 프론트엔드에서 하드코딩 fallback 값(+5.2%, +2.1%, +12.0%, -8.0%)을 사용 중.
요구사항:
- 기존 API 수정:
GET /api/v1/daily-report/summary - 응답에
daily_change객체 추가 - 각 항목의 전일 대비 변동률(%) 계산 로직:
cash_asset_change_rate: (오늘 현금성자산 - 어제 현금성자산) / 어제 현금성자산 × 100foreign_currency_change_rate: (오늘 외국환 - 어제 외국환) / 어제 외국환 × 100income_change_rate: (오늘 입금 - 어제 입금) / 어제 입금 × 100expense_change_rate: (오늘 지출 - 어제 지출) / 어제 지출 × 100
- 어제 데이터 없을 시 해당 필드
null(프론트에서 fallback 처리)
Response (기존 응답에 daily_change 추가):
{
"date": "2026-03-03",
"day_of_week": "화",
"cash_asset_total": 1250000000,
"foreign_currency_total": 85000,
"krw_totals": { "income": 45000000, "expense": 32000000, "balance": 1250000000 },
"daily_change": {
"cash_asset_change_rate": 5.2,
"foreign_currency_change_rate": 2.1,
"income_change_rate": 12.0,
"expense_change_rate": -8.0
}
}
Laravel 힌트:
DailyReportService에서 전일 데이터 조회 추가sam_stat캐시 테이블에 전일 스냅샷 있으면 활용- 프론트 타입:
DailyChangeRate(src/lib/api/dashboard/types.ts:23)
N3. 일일일보 — daily-accounts에 입출금관리 데이터 미반영
우선순위: 상
페이지: 일일일보 페이지 (/ko/accounting/daily-report)
현상: 입금관리/출금관리에서 당일 거래를 등록하면 대시보드 자금현황(daily-report/summary)의 합계에는 즉시 반영되지만, 일일일보 페이지의 계좌별 상세 테이블(daily-report/daily-accounts)에는 표시되지 않음. (출금 테스트로 확인됨, 입금도 동일 구조로 미반영 추정)
영향 범위:
| 데이터 | 관리 테이블 | summary (합계) | daily-accounts (상세) |
|---|---|---|---|
| 입금 | deposits (/api/v1/deposits) |
✅ 반영 추정 | ❌ 미반영 추정 |
| 출금 | withdrawals (/api/v1/withdrawals) |
✅ 반영 확인 | ❌ 미반영 확인 |
| 외국환 (USD) | 별도 관리 페이지 미확인 | ✅ 반영 | ❓ 확인 필요 |
원인 분석:
GET /api/v1/daily-report/summary→krw_totals에deposits/withdrawals테이블 데이터 포함 ✅GET /api/v1/daily-report/daily-accounts→bank_accounts단위 집계만 반환,deposits/withdrawals테이블 미포함 ❌
데이터 흐름:
입금관리 등록 → deposits 테이블 INSERT (bank_account_id 포함)
출금관리 등록 → withdrawals 테이블 INSERT (bank_account_id 포함)
├─ summary API → krw_totals.income/expense에 합산 → 대시보드 ✅
└─ daily-accounts API → bank_accounts 기준만 조회 → 일일일보 상세 ❌
요구사항:
GET /api/v1/daily-report/daily-accounts수정- 각 계좌별로
deposits테이블의 당일 income과withdrawals테이블의 당일 expense를 합산 - 또는 입금/출금 등록 시 해당 계좌의 거래 내역(
bank_account_transactions)에도 자동 반영
해결 방안 (택 1):
- 방안 A (daily-accounts 쿼리 수정):
bank_accountsLEFT JOINdeposits/withdrawalsWHERE date = 당일 → 계좌별 income/expense에 합산 - 방안 B (트랜잭션 연동): 입금/출금 등록 시
bank_account_transactions에도 INSERT → daily-accounts가 자연스럽게 포함
Response (기존 DailyAccountItemApi[]와 동일, 데이터만 보완):
[
{
"id": "acc_1",
"category": "우리은행 123-456",
"match_status": "matched",
"carryover": 50000000,
"income": 1000000,
"expense": 50000,
"balance": 50950000,
"currency": "KRW"
}
]
Laravel 힌트:
DailyReportService의getDailyAccounts()메서드 확인deposits테이블:deposit.bank_account_id로 해당 계좌 income 합산withdrawals테이블:withdrawal.bank_account_id로 해당 계좌 expense 합산- USD 계좌도 동일 패턴 적용 필요
N4. 현황판 purchases(발주) — path 오류 + 데이터 정합성 이슈
우선순위: 중 페이지: p34 (현황판)
이슈 A: path 하드코딩 오류
현상: purchases 항목의 실제 데이터는 purchases 테이블(매입, 공통)에서 조회하면서, path는 건설 모듈 경로로 하드코딩되어 있음.
문제 코드 (StatusBoardService.php — getPurchaseStatus()):
$count = Purchase::query()
->where('tenant_id', $tenantId)
->where('status', 'draft')
->count();
return [
'id' => 'purchases',
'label' => '발주',
'path' => '/construction/order/order-management', // ← 매입 데이터인데 건설 경로
];
- 데이터 출처:
purchases테이블 (모든 테넌트 공통 매입 테이블) - path:
/construction/order/order-management(건설 전용 페이지) - 데이터와 path가 불일치 — 매입 draft 건수를 보여주면서 건설 발주 페이지로 링크
현재 프론트 임시 대응: status-issue.ts에서 /accounting/purchase(매입관리)로 오버라이드 중
요구사항:
- path를
/accounting/purchase로 변경 (데이터 출처와 일치시키기) - 또는 테넌트 업종에 따라 path 동적 분기 (건설:
/construction/order/order-management, 기타:/accounting/purchase) - 라벨도 재검토: "발주"가 맞는지, "매입(임시저장)"이 더 정확한지
이슈 B: 데이터 정합성 의심
현상: StatusBoard API에서 purchases count=9건 반환, 하지만 매입관리 페이지(/accounting/purchase)에서 전체 조회 시 1건만 표시.
확인 사항 (DB 직접 확인 필요):
-- 현재 테넌트의 purchases 테이블 전체 건수
SELECT COUNT(*), status FROM purchases WHERE tenant_id = {현재 테넌트 ID} GROUP BY status;
-- draft 상태 건수 (StatusBoard가 조회하는 조건)
SELECT COUNT(*) FROM purchases WHERE tenant_id = {현재 테넌트 ID} AND status = 'draft';
가능한 원인:
- StatusBoard와 매입관리 페이지가 다른 tenant_id 스코프로 조회
- DummyDataSeeder가 다른 tenant_id로 데이터 생성
- 매입관리 API에 추가 필터 조건이 있어서 draft 건이 제외됨
- StatusBoard가 실제와 다른 데이터를 집계
기대 결과: StatusBoard 9건 클릭 → 매입관리 페이지에서 9건 확인 가능해야 함
신규 API (10개)
1. 매출 현황 Summary
우선순위: 중 페이지: p39
GET /api/v1/dashboard/sales/summary
Query Params:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| year | int | N | 조회 연도 (기본: 당해) |
| month | int | N | 조회 월 (기본: 당월) |
Response (SalesStatusApiResponse):
{
"cumulative_sales": 312300000,
"achievement_rate": 94.5,
"yoy_change": 12.5,
"monthly_sales": 312300000,
"monthly_trend": [
{ "month": "2026-08", "label": "8월", "amount": 250000000 },
{ "month": "2026-09", "label": "9월", "amount": 280000000 }
],
"client_sales": [
{ "name": "대한건설", "amount": 95000000 },
{ "name": "삼성테크", "amount": 78000000 }
],
"daily_items": [
{
"date": "2026-02-01",
"client": "대한건설",
"item": "스크린 외",
"amount": 25000000,
"status": "deposited"
}
],
"daily_total": 312300000
}
Laravel 힌트:
- 매출:
sales_orders합계 (confirmed 상태) - 달성률: 매출 목표 대비 (
sales_targets테이블) - YoY: 전년 동월 대비 변화율
- 거래처별: GROUP BY vendor_id → TOP 5
- status 코드:
deposited(입금완료),unpaid(미입금),partial(부분입금)
2. 매입 현황 Summary
우선순위: 중 페이지: p40
GET /api/v1/dashboard/purchases/summary
Query Params:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| year | int | N | 조회 연도 (기본: 당해) |
| month | int | N | 조회 월 (기본: 당월) |
Response (PurchaseStatusApiResponse):
{
"cumulative_purchase": 312300000,
"unpaid_amount": 312300000,
"yoy_change": -12.5,
"monthly_trend": [
{ "month": "2026-08", "label": "8월", "amount": 180000000 }
],
"material_ratio": [
{ "name": "원자재", "value": 55, "percentage": 55, "color": "#3b82f6" },
{ "name": "부자재", "value": 35, "percentage": 35, "color": "#10b981" },
{ "name": "소모품", "value": 10, "percentage": 10, "color": "#f59e0b" }
],
"daily_items": [
{
"date": "2026-02-01",
"supplier": "한국철강",
"item": "철판 외",
"amount": 45000000,
"status": "paid"
}
],
"daily_total": 312300000
}
Laravel 힌트:
- 매입:
purchase_orders합계 - 미결제: 결제 미완료 건 합계
- 원자재/부자재/소모품:
item_categories기준 분류 - status 코드:
paid(결제완료),unpaid(미결제),partial(부분결제)
3. 생산 현황 Summary
우선순위: 상 페이지: p41
GET /api/v1/dashboard/production/summary
Query Params:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| date | string | N | 조회 일자 (기본: 오늘, YYYY-MM-DD) |
Response (DailyProductionApiResponse):
{
"date": "2026-02-23",
"day_of_week": "월요일",
"processes": [
{
"process_name": "스크린",
"total_work": 10,
"todo": 3,
"in_progress": 4,
"completed": 3,
"urgent": 2,
"sub_line": 1,
"regular": 5,
"worker_count": 8,
"work_items": [
{
"id": "wo_1",
"order_no": "SO-2026-001",
"client": "대한건설",
"product": "스크린 A형",
"quantity": 50,
"status": "in_progress"
}
],
"workers": [
{
"name": "김철수",
"assigned": 5,
"completed": 3,
"rate": 60
}
]
}
],
"shipment": {
"expected_amount": 150000000,
"expected_count": 12,
"actual_amount": 120000000,
"actual_count": 9
}
}
Laravel 힌트:
- 공정:
work_processes테이블 (스크린, 슬랫, 절곡 등) - 작업:
work_ordersJOINwork_process_id - status:
pending→ todo,in_progress,completed - urgent: 납기 3일 이내
- 출고:
shipments테이블 (당일 예상 vs 실적)
4. 출고 현황 (생산 현황에 포함)
우선순위: 하 페이지: p41
생산 현황 API의 shipment 필드로 포함됨. 별도 API 불필요.
5. 미출고 내역
우선순위: 하 페이지: p42
GET /api/v1/dashboard/unshipped/summary
Query Params:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| days | int | N | 납기 N일 이내 (기본: 30) |
Response (UnshippedApiResponse):
{
"items": [
{
"id": "us_1",
"port_no": "P-2026-001",
"site_name": "강남 현장",
"order_client": "대한건설",
"due_date": "2026-02-25",
"days_left": 2
}
],
"total_count": 7
}
Laravel 힌트:
shipment_itemsWHERE shipped_at IS NULL AND due_date >= NOW()- days_left: DATEDIFF(due_date, NOW())
- ORDER BY due_date ASC (납기 임박 순)
6. 시공 현황
우선순위: 중 페이지: p42
GET /api/v1/dashboard/construction/summary
Query Params:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| month | int | N | 조회 월 (기본: 당월) |
Response (ConstructionApiResponse):
{
"this_month": 15,
"completed": 5,
"items": [
{
"id": "cs_1",
"site_name": "강남 현장",
"client": "대한건설",
"start_date": "2026-02-01",
"end_date": "2026-02-28",
"progress": 85,
"status": "in_progress"
}
]
}
Laravel 힌트:
constructions테이블- status:
in_progress,scheduled,completed - completed: 최근 7일 이내 완료 건
7. 근태 현황
우선순위: 중 페이지: p43
GET /api/v1/dashboard/attendance/summary
Query Params:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| date | string | N | 조회 일자 (기본: 오늘, YYYY-MM-DD) |
Response (DailyAttendanceApiResponse):
{
"present": 42,
"on_leave": 3,
"late": 1,
"absent": 0,
"employees": [
{
"id": "emp_1",
"department": "생산부",
"position": "과장",
"name": "김철수",
"status": "present"
}
]
}
Laravel 힌트:
attendancesWHERE date = :date- status:
present,on_leave,late,absent - employees: 이상 상태(late, absent, on_leave) 위주 표시
8. 일별 매출 내역
우선순위: 하 페이지: p47 (설정 팝업에서 별도 ON/OFF)
매출 현황 API의 daily_items로 이미 포함. 별도 API 필요 시:
GET /api/v1/dashboard/sales/daily
Query Params:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| start_date | string | N | 시작일 (기본: 당월 1일) |
| end_date | string | N | 종료일 (기본: 오늘) |
| page | int | N | 페이지 (기본: 1) |
| per_page | int | N | 건수 (기본: 20) |
9. 일별 매입 내역
우선순위: 하
매입 현황 API의 daily_items로 이미 포함. 별도 API 필요 시:
GET /api/v1/dashboard/purchases/daily
(매출 일별과 동일 구조)
10. 접대비 상세
우선순위: 상 페이지: p53-54
GET /api/v1/dashboard/entertainment/detail
Query Params:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| year | int | N | 연도 |
| quarter | int | N | 분기 (1-4) |
| limit_type | string | N | annual/quarterly |
| company_type | string | N | large/medium/small |
Response:
{
"summary": {
"total_used": 10000000,
"annual_limit": 40120000,
"remaining": 30120000,
"usage_rate": 24.9
},
"limit_calculation": {
"base_limit": 36000000,
"revenue_additional": 4120000,
"total_limit": 40120000,
"revenue": 2060000000,
"company_type": "medium"
},
"quarterly_status": [
{
"quarter": 1,
"label": "1분기",
"limit": 10030000,
"used": 3500000,
"remaining": 6530000,
"exceeded": 0
}
],
"transactions": [
{
"id": 1,
"date": "2026-01-15",
"user_name": "홍길동",
"merchant_name": "강남식당",
"amount": 350000,
"counterpart": "대한건설",
"receipt_type": "법인카드",
"risk_flags": ["high_amount"]
}
]
}
수정 API (6개)
1. 가지급금 Summary (수정)
현재: 카드/가지급금/법인세/종합세 변경: 카드/경조사/상품권/접대비/총합계 (5카드)
GET /api/proxy/card-transactions/summary
Response 변경:
{
"cards": [
{ "id": "cm1", "label": "카드", "amount": 3123000, "sub_label": "미정리 5건", "count": 5 },
{ "id": "cm2", "label": "경조사", "amount": 3123000, "sub_label": "미증빙 5건", "count": 5 },
{ "id": "cm3", "label": "상품권", "amount": 3123000, "sub_label": "미증빙 5건", "count": 5 },
{ "id": "cm4", "label": "접대비", "amount": 3123000, "sub_label": "미증빙 5건", "count": 5 },
{ "id": "cm_total", "label": "총 가지급금 합계", "amount": 350000000 }
],
"check_points": [
{
"id": "cm-cp1",
"type": "warning",
"message": "법인카드 사용 총 850만원이 가지급금으로 전환되었습니다.",
"highlights": [{ "text": "850만원", "color": "red" }]
}
],
"warning_banner": "가지급금 인정이자 4.6%, 법인세 및 연말정산 시 대표자 종합세 가중 주의"
}
Laravel 힌트:
- 분류:
card_transactions.category기준 (card/congratulation/gift_card/entertainment) - 미정리/미증빙:
evidence_status = 'pending'COUNT
2. 접대비 Summary (수정)
현재: 매출/한도/잔여한도/사용금액 변경: 주말심야/기피업종/고액결제/증빙미비 (리스크 4종)
GET /api/proxy/entertainment/summary
Response 변경:
{
"cards": [
{ "id": "et1", "label": "주말/심야", "amount": 3123000, "sub_label": "5건", "count": 5 },
{ "id": "et2", "label": "기피업종 (유흥, 귀금속 등)", "amount": 3123000, "sub_label": "불인정 5건", "count": 5 },
{ "id": "et3", "label": "고액 결제", "amount": 3123000, "sub_label": "5건", "count": 5 },
{ "id": "et4", "label": "증빙 미비", "amount": 3123000, "sub_label": "5건", "count": 5 }
],
"check_points": [...]
}
리스크 감지 로직 (p60 참조):
- 주말/심야: 토
일, 22:0006:00 거래 - 기피업종: MCC 코드 기반 (유흥업소 7273, 귀금속 5944, 골프장 7941 등)
- 고액 결제: 설정 금액(기본 50만원) 초과
- 증빙 미비: 적격증빙(세금계산서/카드매출전표) 없는 건
3. 복리후생비 Summary (수정)
현재: 한도/잔여한도/사용금액 변경: 비과세한도초과/사적사용의심/특정인편중/항목별한도초과 (리스크 4종)
GET /api/proxy/welfare/summary
Response 변경:
{
"cards": [
{ "id": "wf1", "label": "비과세 한도 초과", "amount": 3123000, "sub_label": "5건", "count": 5 },
{ "id": "wf2", "label": "사적 사용 의심", "amount": 3123000, "sub_label": "5건", "count": 5 },
{ "id": "wf3", "label": "특정인 편중", "amount": 3123000, "sub_label": "5건", "count": 5 },
{ "id": "wf4", "label": "항목별 한도 초과", "amount": 3123000, "sub_label": "5건", "count": 5 }
],
"check_points": [...]
}
리스크 감지 로직:
- 비과세 한도 초과: 항목별 비과세 기준 초과 (식대 20만원, 교통비 10만원 등)
- 사적 사용 의심: 주말/야간 + 비업무 업종 조합
- 특정인 편중: 직원별 사용액 편차 > 평균의 200%
- 항목별 한도 초과: 설정 금액 초과
4. 가지급금 Detail (수정)
기존 LoanDashboardApiResponse에 AI분류 컬럼 추가.
GET /api/v1/loans/dashboard
Response 추가 필드:
{
"items": [
{
"...기존 필드...",
"ai_category": "카드",
"evidence_status": "미증빙"
}
]
}
5. 복리후생비 Detail (수정)
기존 WelfareDetailApiResponse에 계산방식 파라미터 추가.
GET /api/proxy/welfare/detail?calculation_type=fixed&fixed_amount_per_month=200000
(기존 구현 유지, 계산 파라미터만 반영 확인)
6. 부가세 Detail (수정)
기존 VatApiResponse에 신고기간 파라미터 반영.
GET /api/proxy/vat/summary?period_type=quarter&year=2026&period=1
(기존 구현 유지, 기간별 필터링 확인)
리스크 감지 로직 참고 (p58-60)
MCC 코드 기피업종
| MCC | 업종 | 분류 |
|---|---|---|
| 7273 | 유흥업소 | 기피업종 |
| 5944 | 귀금속 | 기피업종 |
| 7941 | 골프장 | 기피업종 |
| 5813 | 주점 | 기피업종 |
| 7011 | 호텔/리조트 | 주의업종 |
리스크 판별 규칙
규칙1: 시간대 이상 → 22:00~06:00 또는 토~일
규칙2: 업종 이상 → MCC 기피업종 해당
규칙3: 금액 이상 → 설정 금액 초과 (기본 50만원)
규칙4: 빈도 이상 → 월 10회 이상 동일 업종
규칙5: 증빙 미비 → 적격증빙 없음
리스크 등급:
- 2개 이상 해당 → 🔴 고위험
- 1개 해당 → 🟡 주의
- 0개 → 🟢 정상
계산 공식 참고
가지급금 인정이자 (p58)
인정이자 = 가지급금잔액 × (4.6% / 365) × 경과일수
법인세 추가 = 인정이자 × 19%
대표자 소득세 = 인정이자 × 35%
접대비 손금한도 (p59)
기본한도:
일반법인: 1,200만원/년
중소기업: 3,600만원/년
수입금액별 추가:
100억 이하: 수입금액 × 0.2%
100~500억: 2,000만원 + (수입금액-100억) × 0.1%
500억 초과: 6,000만원 + (수입금액-500억) × 0.03%
복리후생비 (p60)
방식1 (정액): 직원수 × 월정액 × 12
방식2 (비율): 연봉총액 × 비율%
비과세 한도:
식대: 20만원/월
교통비: 10만원/월
경조사: 5만원/건
건강검진: 연간 총액/12 환산
교육훈련: 8만원/월
복지포인트: 10만원/월
우선순위 정리
| 우선순위 | API | 이유 |
|---|---|---|
| 🔴 상 | 접대비 summary 수정, 복리후생비 summary 수정 | D1.7 카드 구조 변경 |
| 🔴 상 | 가지급금 summary 수정 | D1.7 카드 구조 변경 |
| 🔴 상 | 접대비 detail 신규 | 모달 확장 |
| 🟡 중 | 매출 현황, 매입 현황, 시공 현황, 근태 현황 | 신규 섹션 |
| 🟡 중 | 생산 현황 | 복잡한 공정 집계 |
| 🟢 하 | 미출고 내역, 일별 매출/매입 | 단순 조회 |