Files
2026-06-11 18:47:38 +09:00

587 lines
22 KiB
PHP

<?php
$sub_menu = "800200";
include_once('./_common.php');
auth_check($auth[$sub_menu], 'r');
$g5['title'] = '견적 통계 및 리포트';
// EstimateManager 로드
require_once G5_PATH . '/adm/order_manage/classes/EstimateManager.class.php';
$estimate_manager = new EstimateManager();
// 필터 파라미터
$date_from = isset($_GET['date_from']) ? clean_xss_tags($_GET['date_from']) : date('Y-m-01'); // 이번 달 첫째 날
$date_to = isset($_GET['date_to']) ? clean_xss_tags($_GET['date_to']) : date('Y-m-d'); // 오늘
$report_type = isset($_GET['report_type']) ? clean_xss_tags($_GET['report_type']) : 'overview';
// CSV 내보내기 처리
if (isset($_GET['export']) && $_GET['export'] === 'csv') {
$export_type = clean_xss_tags($_GET['export_type'] ?? 'estimates');
$csv_data = $estimate_manager->exportStatisticsCSV($export_type, $date_from, $date_to);
if ($csv_data) {
$filename = "order_statistics_{$export_type}_" . date('Y-m-d') . ".csv";
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: no-cache, must-revalidate');
// UTF-8 BOM 추가 (엑셀에서 한글 깨짐 방지)
echo "\xEF\xBB\xBF";
echo $csv_data;
exit;
} else {
alert('CSV 내보내기에 실패했습니다.');
}
}
// 통계 데이터 조회
$estimate_stats = $estimate_manager->getEstimateStatistics($date_from, $date_to);
$dealer_performance = $estimate_manager->getDealerPerformance($date_from, $date_to);
$revenue_stats = $estimate_manager->getRevenueStatistics($date_from, $date_to);
$expert_visit_stats = $estimate_manager->getExpertVisitStatistics($date_from, $date_to);
include_once(G5_ADMIN_PATH . '/admin.head.php');
?>
<div class="local_desc01 local_desc">
<p>
견적 요청 현황, 대리점 성과, 매출 분석, 전문가 방문 통계 등 종합적인 비즈니스 인사이트를 제공합니다.<br>
데이터를 CSV 파일로 내보내어 상세 분석이 가능합니다.
</p>
</div>
<!-- 필터 및 내보내기 영역 -->
<div class="local_sch01 local_sch">
<form name="fsearch" method="get">
<fieldset>
<legend>기간 설정</legend>
<label for="date_from" class="sound_only">시작일</label>
<input type="date" name="date_from" id="date_from" value="<?php echo $date_from; ?>" class="frm_input">
~
<label for="date_to" class="sound_only">종료일</label>
<input type="date" name="date_to" id="date_to" value="<?php echo $date_to; ?>" class="frm_input">
<input type="submit" value="조회" class="btn_submit">
<div style="float: right;">
<select id="export_type" class="frm_input">
<option value="estimates">견적 통계</option>
<option value="dealers">대리점 성과</option>
<option value="revenue">매출 현황</option>
<option value="expert_visits">전문가 방문</option>
</select>
<button type="button" onclick="exportCSV()" class="btn btn_02">CSV 내보내기</button>
</div>
</fieldset>
</form>
</div>
<!-- 탭 메뉴 -->
<div class="tab-menu">
<button class="tab-btn active" onclick="showTab('overview')">전체 개요</button>
<button class="tab-btn" onclick="showTab('estimates')">견적 통계</button>
<button class="tab-btn" onclick="showTab('dealers')">대리점 성과</button>
<button class="tab-btn" onclick="showTab('revenue')">매출 분석</button>
<button class="tab-btn" onclick="showTab('expert_visits')">전문가 방문</button>
</div>
<!-- 전체 개요 탭 -->
<div id="overview-tab" class="tab-content active">
<div class="stats-grid">
<!-- 견적 현황 카드 -->
<div class="stats-card">
<h3>견적 현황</h3>
<div class="stats-number">
<?php echo number_format($estimate_stats['total_statistics']['total_estimates']); ?></div>
<div class="stats-label">총 견적 요청</div>
<div class="stats-detail">
완료: <?php echo number_format($estimate_stats['total_statistics']['completed_estimates']); ?>건 |
선택: <?php echo number_format($estimate_stats['total_statistics']['selected_estimates']); ?>건 |
전환율: <?php echo $estimate_stats['total_statistics']['conversion_rate']; ?>%
</div>
</div>
<!-- 매출 현황 카드 -->
<div class="stats-card">
<h3>매출 현황</h3>
<div class="stats-number">
<?php echo number_format($revenue_stats['total_revenue']['total_revenue'] ?? 0); ?>원</div>
<div class="stats-label">총 매출</div>
<div class="stats-detail">
주문: <?php echo number_format($revenue_stats['total_revenue']['total_orders'] ?? 0); ?>건 |
평균: <?php echo number_format($revenue_stats['total_revenue']['avg_order_value'] ?? 0); ?>원
</div>
</div>
<!-- 대리점 현황 카드 -->
<div class="stats-card">
<h3>대리점 현황</h3>
<div class="stats-number"><?php echo count($dealer_performance); ?></div>
<div class="stats-label">활성 대리점</div>
<div class="stats-detail">
<?php
$total_bids = array_sum(array_column($dealer_performance, 'total_bids'));
$selected_bids = array_sum(array_column($dealer_performance, 'selected_bids'));
$avg_success_rate = $total_bids > 0 ? round(($selected_bids / $total_bids) * 100, 2) : 0;
?>
총 입찰: <?php echo number_format($total_bids); ?>건 |
평균 성공률: <?php echo $avg_success_rate; ?>%
</div>
</div>
<!-- 전문가 방문 카드 -->
<div class="stats-card">
<h3>전문가 방문</h3>
<div class="stats-number">
<?php echo number_format($expert_visit_stats['visit_statistics']['total_requests'] ?? 0); ?></div>
<div class="stats-label">방문 요청</div>
<div class="stats-detail">
완료: <?php echo number_format($expert_visit_stats['visit_statistics']['completed_visits'] ?? 0); ?>건 |
매출: <?php echo number_format($expert_visit_stats['visit_statistics']['total_visit_revenue'] ?? 0); ?>원
</div>
</div>
</div>
<!-- 월별 트렌드 차트 -->
<div class="chart-container">
<h3>월별 견적 요청 트렌드</h3>
<canvas id="monthlyTrendChart" width="800" height="400"></canvas>
</div>
</div>
<!-- 견적 통계 탭 -->
<div id="estimates-tab" class="tab-content">
<div class="tbl_head01 tbl_wrap">
<table>
<caption>상태별 견적 분포</caption>
<thead>
<tr>
<th scope="col">상태</th>
<th scope="col">건수</th>
<th scope="col">비율</th>
</tr>
</thead>
<tbody>
<?php
$total_estimates = $estimate_stats['total_statistics']['total_estimates'];
foreach ($estimate_stats['status_distribution'] as $status):
$percentage = $total_estimates > 0 ? round(($status['count'] / $total_estimates) * 100, 2) : 0;
?>
<tr>
<td class="td_left"><?php echo get_text($status['status']); ?></td>
<td class="td_num"><?php echo number_format($status['count']); ?>건</td>
<td class="td_num"><?php echo $percentage; ?>%</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="chart-container">
<h3>월별 견적 전환율</h3>
<canvas id="conversionChart" width="800" height="400"></canvas>
</div>
</div>
<!-- 대리점 성과 탭 -->
<div id="dealers-tab" class="tab-content">
<div class="tbl_head01 tbl_wrap">
<table>
<caption>대리점별 성과 분석</caption>
<thead>
<tr>
<th scope="col">대리점</th>
<th scope="col">레벨</th>
<th scope="col">총 입찰</th>
<th scope="col">선택된 입찰</th>
<th scope="col">성공률</th>
<th scope="col">평균 입찰금액</th>
<th scope="col">총 매출</th>
</tr>
</thead>
<tbody>
<?php if (empty($dealer_performance)): ?>
<tr>
<td colspan="7" class="empty_table">대리점 성과 데이터가 없습니다.</td>
</tr>
<?php else: ?>
<?php foreach ($dealer_performance as $dealer): ?>
<tr>
<td class="td_left">
<strong><?php echo get_text($dealer['dealer_name']); ?></strong><br>
<small><?php echo $dealer['dealer_id']; ?></small>
</td>
<td class="td_num">레벨 <?php echo $dealer['dealer_level']; ?></td>
<td class="td_num"><?php echo number_format($dealer['total_bids']); ?>건</td>
<td class="td_num"><?php echo number_format($dealer['selected_bids']); ?>건</td>
<td class="td_num">
<span
class="<?php echo $dealer['success_rate'] >= 30 ? 'text-success' : ($dealer['success_rate'] >= 15 ? 'text-warning' : 'text-danger'); ?>">
<?php echo $dealer['success_rate']; ?>%
</span>
</td>
<td class="td_num"><?php echo number_format($dealer['avg_bid_amount']); ?>원</td>
<td class="td_num"><?php echo number_format($dealer['total_revenue']); ?>원</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- 매출 분석 탭 -->
<div id="revenue-tab" class="tab-content">
<div class="stats-grid">
<div class="stats-card">
<h4>총 주문</h4>
<div class="stats-number">
<?php echo number_format($revenue_stats['total_revenue']['total_orders'] ?? 0); ?>건</div>
</div>
<div class="stats-card">
<h4>총 매출</h4>
<div class="stats-number">
<?php echo number_format($revenue_stats['total_revenue']['total_revenue'] ?? 0); ?>원</div>
</div>
<div class="stats-card">
<h4>평균 주문금액</h4>
<div class="stats-number">
<?php echo number_format($revenue_stats['total_revenue']['avg_order_value'] ?? 0); ?>원</div>
</div>
<div class="stats-card">
<h4>결제 완료율</h4>
<?php
$payment_rate = ($revenue_stats['total_revenue']['total_orders'] ?? 0) > 0
? round((($revenue_stats['total_revenue']['paid_orders'] ?? 0) / $revenue_stats['total_revenue']['total_orders']) * 100, 2)
: 0;
?>
<div class="stats-number"><?php echo $payment_rate; ?>%</div>
</div>
</div>
<div class="tbl_head01 tbl_wrap">
<table>
<caption>결제 단계별 현황</caption>
<thead>
<tr>
<th scope="col">결제 단계</th>
<th scope="col">건수</th>
<th scope="col">매출</th>
</tr>
</thead>
<tbody>
<?php foreach ($revenue_stats['payment_breakdown'] as $payment): ?>
<tr>
<td class="td_left"><?php echo get_text($payment['status']); ?></td>
<td class="td_num"><?php echo number_format($payment['count']); ?>건</td>
<td class="td_num"><?php echo number_format($payment['revenue']); ?>원</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="chart-container">
<h3>월별 매출 트렌드</h3>
<canvas id="revenueChart" width="800" height="400"></canvas>
</div>
</div>
<!-- 전문가 방문 탭 -->
<div id="expert_visits-tab" class="tab-content">
<div class="stats-grid">
<div class="stats-card">
<h4>총 방문 요청</h4>
<div class="stats-number">
<?php echo number_format($expert_visit_stats['visit_statistics']['total_requests'] ?? 0); ?>건</div>
</div>
<div class="stats-card">
<h4>완료된 방문</h4>
<div class="stats-number">
<?php echo number_format($expert_visit_stats['visit_statistics']['completed_visits'] ?? 0); ?>건</div>
</div>
<div class="stats-card">
<h4>방문 매출</h4>
<div class="stats-number">
<?php echo number_format($expert_visit_stats['visit_statistics']['total_visit_revenue'] ?? 0); ?>원</div>
</div>
<div class="stats-card">
<h4>평균 방문비</h4>
<div class="stats-number">
<?php echo number_format($expert_visit_stats['visit_statistics']['avg_visit_fee'] ?? 0); ?>원</div>
</div>
</div>
<div class="tbl_head01 tbl_wrap">
<table>
<caption>전문가별 방문 성과</caption>
<thead>
<tr>
<th scope="col">전문가</th>
<th scope="col">배정된 방문</th>
<th scope="col">완료된 방문</th>
<th scope="col">완료율</th>
</tr>
</thead>
<tbody>
<?php if (empty($expert_visit_stats['expert_performance'])): ?>
<tr>
<td colspan="4" class="empty_table">전문가 방문 데이터가 없습니다.</td>
</tr>
<?php else: ?>
<?php foreach ($expert_visit_stats['expert_performance'] as $expert): ?>
<tr>
<td class="td_left">
<strong><?php echo get_text($expert['expert_name']); ?></strong><br>
<small><?php echo $expert['expert_id']; ?></small>
</td>
<td class="td_num"><?php echo number_format($expert['assigned_visits']); ?>건</td>
<td class="td_num"><?php echo number_format($expert['completed_visits']); ?>건</td>
<td class="td_num">
<span
class="<?php echo $expert['completion_rate'] >= 80 ? 'text-success' : ($expert['completion_rate'] >= 60 ? 'text-warning' : 'text-danger'); ?>">
<?php echo $expert['completion_rate']; ?>%
</span>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<style>
.tab-menu {
margin: 20px 0;
border-bottom: 1px solid #ddd;
}
.tab-btn {
background: none;
border: none;
padding: 10px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
margin-right: 10px;
}
.tab-btn.active {
border-bottom-color: #007cba;
color: #007cba;
font-weight: bold;
}
.tab-content {
display: none;
padding: 20px 0;
}
.tab-content.active {
display: block;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stats-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #007cba;
}
.stats-card h3,
.stats-card h4 {
margin: 0 0 10px 0;
color: #333;
font-size: 14px;
}
.stats-number {
font-size: 24px;
font-weight: bold;
color: #007cba;
margin: 10px 0;
}
.stats-label {
color: #666;
font-size: 12px;
margin-bottom: 5px;
}
.stats-detail {
color: #888;
font-size: 11px;
}
.chart-container {
background: white;
padding: 20px;
border-radius: 8px;
border: 1px solid #ddd;
margin: 20px 0;
}
.chart-container h3 {
margin: 0 0 20px 0;
color: #333;
}
.text-success {
color: #28a745;
}
.text-warning {
color: #ffc107;
}
.text-danger {
color: #dc3545;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// 탭 전환
function showTab(tabName) {
// 모든 탭 비활성화
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
// 선택된 탭 활성화
event.target.classList.add('active');
document.getElementById(tabName + '-tab').classList.add('active');
}
// CSV 내보내기
function exportCSV() {
const exportType = document.getElementById('export_type').value;
const dateFrom = document.getElementById('date_from').value;
const dateTo = document.getElementById('date_to').value;
const url = `?export=csv&export_type=${exportType}&date_from=${dateFrom}&date_to=${dateTo}`;
window.location.href = url;
}
// 차트 데이터 준비
const monthlyTrendData = <?php echo json_encode($estimate_stats['monthly_trend']); ?>;
const monthlyRevenueData = <?php echo json_encode($revenue_stats['monthly_revenue']); ?>;
// 월별 트렌드 차트
if (document.getElementById('monthlyTrendChart')) {
const ctx1 = document.getElementById('monthlyTrendChart').getContext('2d');
new Chart(ctx1, {
type: 'line',
data: {
labels: monthlyTrendData.map(item => item.month),
datasets: [{
label: '총 견적수',
data: monthlyTrendData.map(item => item.total_count),
borderColor: '#007cba',
backgroundColor: 'rgba(0, 124, 186, 0.1)',
tension: 0.4
}, {
label: '선택된 견적수',
data: monthlyTrendData.map(item => item.selected_count),
borderColor: '#28a745',
backgroundColor: 'rgba(40, 167, 69, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
}
// 전환율 차트
if (document.getElementById('conversionChart')) {
const ctx2 = document.getElementById('conversionChart').getContext('2d');
new Chart(ctx2, {
type: 'bar',
data: {
labels: monthlyTrendData.map(item => item.month),
datasets: [{
label: '전환율 (%)',
data: monthlyTrendData.map(item => item.conversion_rate),
backgroundColor: 'rgba(255, 193, 7, 0.8)',
borderColor: '#ffc107',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
max: 100
}
}
}
});
}
// 매출 차트
if (document.getElementById('revenueChart')) {
const ctx3 = document.getElementById('revenueChart').getContext('2d');
new Chart(ctx3, {
type: 'line',
data: {
labels: monthlyRevenueData.map(item => item.month),
datasets: [{
label: '월별 매출 (원)',
data: monthlyRevenueData.map(item => item.revenue),
borderColor: '#dc3545',
backgroundColor: 'rgba(220, 53, 69, 0.1)',
tension: 0.4,
yAxisID: 'y'
}, {
label: '주문 건수',
data: monthlyRevenueData.map(item => item.order_count),
borderColor: '#6f42c1',
backgroundColor: 'rgba(111, 66, 193, 0.1)',
tension: 0.4,
yAxisID: 'y1'
}]
},
options: {
responsive: true,
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
beginAtZero: true
},
y1: {
type: 'linear',
display: true,
position: 'right',
beginAtZero: true,
grid: {
drawOnChartArea: false,
},
}
}
}
});
}
</script>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');
?>