36000000, // 대기업: 연 3,600만원 'medium' => 36000000, // 중견기업: 연 3,600만원 'small' => 24000000, // 중소기업: 연 2,400만원 ]; /** * 접대비 현황 요약 조회 * * @param string|null $limitType 기간 타입 (annual|quarterly, 기본: quarterly) * @param string|null $companyType 기업 유형 (large|medium|small, 기본: medium) * @param int|null $year 연도 (기본: 현재 연도) * @param int|null $quarter 분기 (1-4, 기본: 현재 분기) * @return array{cards: array, check_points: array} */ public function getSummary( ?string $limitType = 'quarterly', ?string $companyType = 'medium', ?int $year = null, ?int $quarter = null ): array { $tenantId = $this->tenantId(); $now = Carbon::now(); // 기본값 설정 $year = $year ?? $now->year; $limitType = $limitType ?? 'quarterly'; $companyType = $companyType ?? 'medium'; $quarter = $quarter ?? $now->quarter; // 기간 범위 계산 if ($limitType === 'annual') { $startDate = Carbon::create($year, 1, 1)->format('Y-m-d'); $endDate = Carbon::create($year, 12, 31)->format('Y-m-d'); $periodLabel = "{$year}년"; } else { $startDate = Carbon::create($year, ($quarter - 1) * 3 + 1, 1)->format('Y-m-d'); $endDate = Carbon::create($year, $quarter * 3, 1)->endOfMonth()->format('Y-m-d'); $periodLabel = "{$quarter}사분기"; } // 연간 시작일 (매출 계산용) $yearStartDate = Carbon::create($year, 1, 1)->format('Y-m-d'); $yearEndDate = Carbon::create($year, 12, 31)->format('Y-m-d'); // 매출액 조회 (연간) $annualSales = $this->getAnnualSales($tenantId, $yearStartDate, $yearEndDate); // 접대비 한도 계산 $annualLimit = $this->calculateLimit($annualSales, $companyType); $periodLimit = $limitType === 'annual' ? $annualLimit : ($annualLimit / 4); // 접대비 사용액 조회 $usedAmount = $this->getUsedAmount($tenantId, $startDate, $endDate); // 잔여 한도 $remainingLimit = max(0, $periodLimit - $usedAmount); // 카드 데이터 구성 $cards = [ [ 'id' => 'et_sales', 'label' => '매출', 'amount' => (int) $annualSales, ], [ 'id' => 'et_limit', 'label' => "{{$periodLabel}} 접대비 총 한도", 'amount' => (int) $periodLimit, ], [ 'id' => 'et_remaining', 'label' => "{{$periodLabel}} 접대비 잔여한도", 'amount' => (int) $remainingLimit, ], [ 'id' => 'et_used', 'label' => "{{$periodLabel}} 접대비 사용금액", 'amount' => (int) $usedAmount, ], ]; // 체크포인트 생성 $checkPoints = $this->generateCheckPoints( $periodLabel, $periodLimit, $usedAmount, $remainingLimit, $tenantId, $startDate, $endDate ); return [ 'cards' => $cards, 'check_points' => $checkPoints, ]; } /** * 연간 매출액 조회 */ private function getAnnualSales(int $tenantId, string $startDate, string $endDate): float { // orders 테이블에서 확정된 수주 합계 조회 $amount = DB::table('orders') ->where('tenant_id', $tenantId) ->where('status_code', 'confirmed') ->whereBetween('received_at', [$startDate, $endDate]) ->whereNull('deleted_at') ->sum('total_amount'); return $amount ?: 30530000000; // 임시 기본값 (305억) } /** * 접대비 한도 계산 */ private function calculateLimit(float $annualSales, string $companyType): float { // 기본 한도 (기업 규모별) $baseLimit = self::COMPANY_TYPE_LIMITS[$companyType] ?? self::COMPANY_TYPE_LIMITS['medium']; // 매출 기반 한도 (0.3%) $salesBasedLimit = $annualSales * self::DEFAULT_LIMIT_RATE; // 기본 한도 + 매출 기반 한도 (실제 세법은 더 복잡하지만 간소화) return $baseLimit + $salesBasedLimit; } /** * 접대비 사용액 조회 */ private function getUsedAmount(int $tenantId, string $startDate, string $endDate): float { // TODO: 실제 접대비 계정과목에서 조회 // expense_accounts 또는 card_transactions에서 접대비 항목 합계 $amount = DB::table('expense_accounts') ->where('tenant_id', $tenantId) ->where('account_type', 'entertainment') ->whereBetween('expense_date', [$startDate, $endDate]) ->whereNull('deleted_at') ->sum('amount'); return $amount ?: 10000000; // 임시 기본값 } /** * 거래처 누락 건수 조회 */ private function getMissingVendorCount(int $tenantId, string $startDate, string $endDate): array { // TODO: 거래처 정보 누락 건수 조회 $result = DB::table('expense_accounts') ->where('tenant_id', $tenantId) ->where('account_type', 'entertainment') ->whereBetween('expense_date', [$startDate, $endDate]) ->whereNull('vendor_id') ->whereNull('deleted_at') ->selectRaw('COUNT(*) as count, COALESCE(SUM(amount), 0) as total') ->first(); return [ 'count' => $result->count ?? 0, 'total' => $result->total ?? 0, ]; } /** * 체크포인트 생성 */ private function generateCheckPoints( string $periodLabel, float $limit, float $used, float $remaining, int $tenantId, string $startDate, string $endDate ): array { $checkPoints = []; $usageRate = $limit > 0 ? ($used / $limit) * 100 : 0; $usedFormatted = number_format($used / 10000); $limitFormatted = number_format($limit / 10000); $remainingFormatted = number_format($remaining / 10000); // 사용률에 따른 체크포인트 if ($usageRate <= 75) { // 정상 운영 $remainingRate = round(100 - $usageRate); $checkPoints[] = [ 'id' => 'et_cp_normal', 'type' => 'success', 'message' => "{$periodLabel} 접대비 사용 {$usedFormatted}만원 / 한도 {$limitFormatted}만원 ({$remainingRate}%). 여유 있게 운영 중입니다.", 'highlights' => [ ['text' => "{$usedFormatted}만원", 'color' => 'green'], ['text' => "{$limitFormatted}만원 ({$remainingRate}%)", 'color' => 'green'], ], ]; } elseif ($usageRate <= 100) { // 주의 (85% 이상) $usageRateRounded = round($usageRate); $checkPoints[] = [ 'id' => 'et_cp_warning', 'type' => 'warning', 'message' => "접대비 한도 {$usageRateRounded}% 도달. 잔여 한도 {$remainingFormatted}만원입니다. 사용 계획을 점검해 주세요.", 'highlights' => [ ['text' => "잔여 한도 {$remainingFormatted}만원", 'color' => 'orange'], ], ]; } else { // 한도 초과 $overAmount = $used - $limit; $overFormatted = number_format($overAmount / 10000); $checkPoints[] = [ 'id' => 'et_cp_over', 'type' => 'error', 'message' => "접대비 한도 초과 {$overFormatted}만원 발생. 초과분은 손금불산입되어 법인세 부담이 증가합니다.", 'highlights' => [ ['text' => "{$overFormatted}만원 발생", 'color' => 'red'], ], ]; } // 거래처 정보 누락 체크 $missingVendor = $this->getMissingVendorCount($tenantId, $startDate, $endDate); if ($missingVendor['count'] > 0) { $missingTotal = number_format($missingVendor['total'] / 10000); $checkPoints[] = [ 'id' => 'et_cp_missing', 'type' => 'error', 'message' => "접대비 사용 중 {$missingVendor['count']}건({$missingTotal}만원)의 거래처 정보가 누락되었습니다. 기록을 보완해 주세요.", 'highlights' => [ ['text' => "{$missingVendor['count']}건({$missingTotal}만원)", 'color' => 'red'], ['text' => '거래처 정보가 누락', 'color' => 'red'], ], ]; } return $checkPoints; } }