diff --git a/salesmanagement/api/get_performance.php b/salesmanagement/api/get_performance.php index a989045..1b9e37a 100644 --- a/salesmanagement/api/get_performance.php +++ b/salesmanagement/api/get_performance.php @@ -44,30 +44,64 @@ try { if (!$member) return null; - // 이 멤버의 직접 실적 가져오기 (기존 sales_record + 신규 sales_tenant_products) + // 이 멤버의 직접 실적 가져오기 + // 1) 영업 실적 (manager_id = 본인): 영업 수당 대상 (상위 오버라이딩 O) + // 2) 매니저 수익 (sales_manager_id = 본인): 매니저 수당 대상 (상위 오버라이딩 X) $sql = " - SELECT id, customer_name as customer, contract_date as contractDate, amount - FROM sales_record - WHERE member_id = ? AND status = 'completed' AND contract_date BETWEEN ? AND ? - UNION ALL - SELECT p.id, t.tenant_name as customer, p.contract_date as contractDate, p.contract_amount as amount + /* [Type: sales] 본인이 영업한 건 (가입비/계약금) -> 영업 수당 20%, 상위 5% */ + SELECT 'sales' as type, p.id, t.tenant_name as customer, p.contract_date as contractDate, p.contract_amount as amount, 0 as fixed_commission FROM sales_tenant_products p JOIN sales_tenants t ON p.tenant_id = t.id - WHERE (t.sales_manager_id = ? OR (t.sales_manager_id IS NULL AND t.manager_id = ?)) + WHERE t.manager_id = ? AND p.contract_date BETWEEN ? AND ? + + UNION ALL + + /* [Type: manager] 본인이 매니징한 건 (구독료 수당) -> 매니저 수당 100%, 상위 오버라이딩 없음 */ + SELECT 'manager' as type, p.id, t.tenant_name as customer, p.contract_date as contractDate, p.contract_amount as amount, p.commission_amount as fixed_commission + FROM sales_tenant_products p + JOIN sales_tenants t ON p.tenant_id = t.id + WHERE t.sales_manager_id = ? + AND p.contract_date BETWEEN ? AND ? + + UNION ALL + + /* [Type: sales] 기타 영업 기록 (기존 호환) */ + SELECT 'sales' as type, id, customer_name as customer, contract_date as contractDate, amount, 0 as fixed_commission + FROM sales_record + WHERE member_id = ? AND status = 'completed' AND contract_date BETWEEN ? AND ? "; + + // 파라미터 바인딩 순서 주의: manager_id, dates, sales_manager_id, dates, member_id, dates $stmt = $pdo->prepare($sql); - $stmt->execute([$parentId, $startDate, $endDate, $parentId, $parentId, $startDate, $endDate]); + $stmt->execute([$parentId, $startDate, $endDate, $parentId, $startDate, $endDate, $parentId, $startDate, $endDate]); $directContracts = $stmt->fetchAll(PDO::FETCH_ASSOC); - $directSales = 0; - $rate = 0; - if ($parentId == $targetUserId) $rate = 0.20; - else if ($depth == 1) $rate = 0.05; - else if ($depth == 2) $rate = 0.03; + $directSales = 0; // 매출 합계 (영업 매출만 집계할지, 전체 매출 집계할지? 보통 매출은 다 보여줌) + + // 수당 계산 (건별 계산 후 합산) + $myDirectCommission = 0; // targetUserId가 이 노드(parentId)를 통해 받는 수당 foreach ($directContracts as &$c) { - $c['commission'] = $c['amount'] * $rate; + $itemComm = 0; + if ($c['type'] === 'manager') { + // 매니저 수당: 본인(depth=0)인 경우에만 가져감 (오버라이딩 불가) + if ($parentId == $targetUserId) { + $itemComm = $c['fixed_commission']; + } else { + $itemComm = 0; + } + } else { + // 영업 수당: 본인 20%, 상위 5%, 상위2 3% + if ($parentId == $targetUserId) $itemComm = $c['amount'] * 0.20; + else if ($depth == 1) $itemComm = $c['amount'] * 0.05; + else if ($depth == 2) $itemComm = $c['amount'] * 0.03; + } + + $c['commission'] = $itemComm; // 화면 표시용 (이 건으로 targetUserId가 번 돈) + $myDirectCommission += $itemComm; + + // 매출 집계는 본인이 수행한 것만 (영업이든 매니징이든 매출액 자체는 표시) $directSales += $c['amount']; } unset($c); @@ -81,8 +115,7 @@ try { $totalSales = $directSales; $totalContractCount = count($directContracts); - $commission = $directSales * $rate; - $subtreeCommission = $commission; + $subtreeCommission = $myDirectCommission; // 내 직속 수당 + 하위로부터 올라온 오버라이딩 수당 합계 foreach ($childrenIds as $childId) { $childNode = buildOrgTree($pdo, $childId, $depth + 1, $startDate, $endDate, $targetUserId); @@ -90,24 +123,10 @@ try { $children[] = $childNode; $totalSales += $childNode['totalSales']; $totalContractCount += $childNode['contractCount']; - $subtreeCommission += $childNode['commission']; // Aggregate commissions + $subtreeCommission += $childNode['commission']; // 하위 재귀 호출 결과(오버라이딩 된 값들) 합산 } } - // 수당 계산 (targetUserId 기준) - // targetUserId == parentId 이면 본인의 직접 판매 (20%) - // targetUserId 가 parentId의 부모이면 (depth=1) 관리자 수당 (5%) - // targetUserId 가 parentId의 조부모이면 (depth=2) 교육자 수당 (3%) - - $commission = 0; - if ($parentId == $targetUserId) { - $commission = $directSales * 0.20; - } else if ($depth == 1) { - $commission = $directSales * 0.05; - } else if ($depth == 2) { - $commission = $directSales * 0.03; - } - return [ 'id' => 'node_' . $member['id'], 'real_id' => $member['id'], @@ -118,8 +137,8 @@ try { 'directSales' => $directSales, 'totalSales' => $totalSales, 'contractCount' => $totalContractCount, - 'directCommission' => $commission, - 'commission' => $subtreeCommission, + 'directCommission' => $myDirectCommission, // 이 노드 자체에서 발생한 targetUser의 수입 + 'commission' => $subtreeCommission, // 이 노드 및 하위 전체에서 발생한 targetUser의 총 수입 'contracts' => $directContracts, 'children' => $children ]; @@ -138,46 +157,51 @@ try { if ($rootNode) { $directNode = [ 'id' => 'root-direct', - 'name' => '내 직접 판매', + 'name' => '내 직접 실적 (영업+매니징)', 'depth' => 0, - 'role' => '판매자', + 'role' => '본인', 'isDirect' => true, 'totalSales' => $rootNode['directSales'], 'contractCount' => count($rootNode['contracts']), - 'commission' => $rootNode['directSales'] * 0.20, + 'commission' => $rootNode['directCommission'], 'contracts' => $rootNode['contracts'], 'children' => [] ]; - // Root node's children should include this direct node + actual children $actualChildren = $rootNode['children']; $rootNode['children'] = array_merge([$directNode], $actualChildren); - - // Root node should keep its aggregated commission as its primary commission value $rootNode['isDirect'] = false; } - // 전체 누적 실적 계산 (전체 기간) + // 전체 누적 실적 계산 (재귀) - 로직 동일하게 수정 function calculateTotalStats($pdo, $parentId, $targetUserId, $depth) { $sql = " - SELECT amount FROM sales_record WHERE member_id = ? AND status = 'completed' + SELECT 'sales' as type, p.contract_amount as amount, 0 as fixed_commission + FROM sales_tenant_products p JOIN sales_tenants t ON p.tenant_id = t.id WHERE t.manager_id = ? UNION ALL - SELECT p.contract_amount as amount - FROM sales_tenant_products p - JOIN sales_tenants t ON p.tenant_id = t.id - WHERE (t.sales_manager_id = ? OR (t.sales_manager_id IS NULL AND t.manager_id = ?)) + SELECT 'manager' as type, p.contract_amount as amount, p.commission_amount as fixed_commission + FROM sales_tenant_products p JOIN sales_tenants t ON p.tenant_id = t.id WHERE t.sales_manager_id = ? + UNION ALL + SELECT 'sales' as type, amount, 0 as fixed_commission FROM sales_record WHERE member_id = ? AND status = 'completed' "; $stmt = $pdo->prepare($sql); $stmt->execute([$parentId, $parentId, $parentId]); - $amounts = $stmt->fetchAll(PDO::FETCH_COLUMN); - - $directSales = array_sum($amounts); - $count = count($amounts); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + $directSales = 0; + $count = count($rows); $commission = 0; - if ($parentId == $targetUserId) $commission = $directSales * 0.20; - else if ($depth == 1) $commission = $directSales * 0.05; - else if ($depth == 2) $commission = $directSales * 0.03; + + foreach ($rows as $r) { + $directSales += $r['amount']; + if ($r['type'] === 'manager') { + if ($parentId == $targetUserId) $commission += $r['fixed_commission']; + } else { + if ($parentId == $targetUserId) $commission += $r['amount'] * 0.20; + else if ($depth == 1) $commission += $r['amount'] * 0.05; + else if ($depth == 2) $commission += $r['amount'] * 0.03; + } + } $stats = [ 'totalSales' => $directSales, @@ -190,13 +214,15 @@ try { $children = $stmt->fetchAll(PDO::FETCH_COLUMN); foreach ($children as $childId) { - if ($depth < 2) { // 수당은 2단계 하위까지만 + if ($depth < 2) { + // 수당 합산은 2단계까지만 (sales 기준) + // manager 수당은 어차피 depth > 0 이면 0이므로 합산해도 상관없음 $childStats = calculateTotalStats($pdo, $childId, $targetUserId, $depth + 1); $stats['totalSales'] += $childStats['totalSales']; $stats['totalCommission'] += $childStats['totalCommission']; $stats['totalCount'] += $childStats['totalCount']; } else { - // 실적만 합산 + // 3단계 이하는 매출만 합산 (오버라이딩 X) $childStats = calculateTotalStats($pdo, $childId, $targetUserId, $depth + 1); $stats['totalSales'] += $childStats['totalSales']; $stats['totalCount'] += $childStats['totalCount'];