format('Y-m-d'); // 수주 집계 (orders) $orderStats = DB::connection('mysql') ->table('orders') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->whereNull('deleted_at') ->selectRaw(' COUNT(*) as order_count, COALESCE(SUM(total_amount), 0) as order_amount, COALESCE(SUM(quantity), 0) as order_item_count, SUM(CASE WHEN status_code = ? THEN 1 ELSE 0 END) as order_draft_count, SUM(CASE WHEN status_code = ? THEN 1 ELSE 0 END) as order_confirmed_count, SUM(CASE WHEN status_code = ? THEN 1 ELSE 0 END) as order_in_progress_count, SUM(CASE WHEN status_code = ? THEN 1 ELSE 0 END) as order_completed_count, SUM(CASE WHEN status_code = ? THEN 1 ELSE 0 END) as order_cancelled_count ', ['draft', 'confirmed', 'in_progress', 'completed', 'cancelled']) ->first(); // 매출 집계 (sales) $salesStats = DB::connection('mysql') ->table('sales') ->where('tenant_id', $tenantId) ->where('sale_date', $dateStr) ->whereNull('deleted_at') ->selectRaw(' COUNT(*) as sales_count, COALESCE(SUM(supply_amount), 0) as sales_amount, COALESCE(SUM(tax_amount), 0) as sales_tax_amount ') ->first(); // 신규 고객 (당일 생성된 고객) $newClientCount = DB::connection('mysql') ->table('clients') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->count(); // 활성 고객 (당일 수주/매출에 연결된 고유 고객) $activeClientCount = DB::connection('mysql') ->table('orders') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->whereNull('deleted_at') ->whereNotNull('client_id') ->distinct('client_id') ->count('client_id'); // 출하 집계 (shipments) $shipmentStats = DB::connection('mysql') ->table('shipments') ->where('tenant_id', $tenantId) ->where('scheduled_date', $dateStr) ->where('status', 'completed') ->whereNull('deleted_at') ->selectRaw(' COUNT(*) as shipment_count, COALESCE(SUM(shipping_cost), 0) as shipment_amount ') ->first(); StatSalesDaily::updateOrCreate( ['tenant_id' => $tenantId, 'stat_date' => $dateStr], [ 'order_count' => $orderStats->order_count ?? 0, 'order_amount' => $orderStats->order_amount ?? 0, 'order_item_count' => $orderStats->order_item_count ?? 0, 'sales_count' => $salesStats->sales_count ?? 0, 'sales_amount' => $salesStats->sales_amount ?? 0, 'sales_tax_amount' => $salesStats->sales_tax_amount ?? 0, 'new_client_count' => $newClientCount, 'active_client_count' => $activeClientCount, 'order_draft_count' => $orderStats->order_draft_count ?? 0, 'order_confirmed_count' => $orderStats->order_confirmed_count ?? 0, 'order_in_progress_count' => $orderStats->order_in_progress_count ?? 0, 'order_completed_count' => $orderStats->order_completed_count ?? 0, 'order_cancelled_count' => $orderStats->order_cancelled_count ?? 0, 'shipment_count' => $shipmentStats->shipment_count ?? 0, 'shipment_amount' => $shipmentStats->shipment_amount ?? 0, ] ); return 1; } public function aggregateMonthly(int $tenantId, int $year, int $month): int { // 일간 데이터를 합산하여 월간 통계 생성 $dailySum = StatSalesDaily::where('tenant_id', $tenantId) ->whereYear('stat_date', $year) ->whereMonth('stat_date', $month) ->selectRaw(' SUM(order_count) as order_count, SUM(order_amount) as order_amount, SUM(sales_count) as sales_count, SUM(sales_amount) as sales_amount, SUM(shipment_count) as shipment_count, SUM(shipment_amount) as shipment_amount ') ->first(); // 월간 고유 거래 고객 수 $startDate = Carbon::create($year, $month, 1)->format('Y-m-d'); $endDate = Carbon::create($year, $month, 1)->endOfMonth()->format('Y-m-d'); $uniqueClientCount = DB::connection('mysql') ->table('orders') ->where('tenant_id', $tenantId) ->whereBetween(DB::raw('DATE(created_at)'), [$startDate, $endDate]) ->whereNull('deleted_at') ->whereNotNull('client_id') ->distinct('client_id') ->count('client_id'); // 평균 수주 금액 $orderCount = $dailySum->order_count ?? 0; $orderAmount = $dailySum->order_amount ?? 0; $avgOrderAmount = $orderCount > 0 ? $orderAmount / $orderCount : 0; // 최다 거래 고객 $topClient = DB::connection('mysql') ->table('orders') ->where('tenant_id', $tenantId) ->whereBetween(DB::raw('DATE(created_at)'), [$startDate, $endDate]) ->whereNull('deleted_at') ->whereNotNull('client_id') ->groupBy('client_id') ->orderByRaw('SUM(total_amount) DESC') ->selectRaw('client_id, SUM(total_amount) as total') ->first(); // 전월 대비 성장률 $prevMonth = StatSalesMonthly::where('tenant_id', $tenantId) ->where(function ($q) use ($year, $month) { $prev = Carbon::create($year, $month, 1)->subMonth(); $q->where('stat_year', $prev->year)->where('stat_month', $prev->month); }) ->first(); $salesAmount = $dailySum->sales_amount ?? 0; $momGrowthRate = null; if ($prevMonth && $prevMonth->sales_amount > 0) { $momGrowthRate = (($salesAmount - $prevMonth->sales_amount) / $prevMonth->sales_amount) * 100; } // 전년동월 대비 $prevYear = StatSalesMonthly::where('tenant_id', $tenantId) ->where('stat_year', $year - 1) ->where('stat_month', $month) ->first(); $yoyGrowthRate = null; if ($prevYear && $prevYear->sales_amount > 0) { $yoyGrowthRate = (($salesAmount - $prevYear->sales_amount) / $prevYear->sales_amount) * 100; } StatSalesMonthly::updateOrCreate( ['tenant_id' => $tenantId, 'stat_year' => $year, 'stat_month' => $month], [ 'order_count' => $dailySum->order_count ?? 0, 'order_amount' => $orderAmount, 'sales_count' => $dailySum->sales_count ?? 0, 'sales_amount' => $salesAmount, 'shipment_count' => $dailySum->shipment_count ?? 0, 'shipment_amount' => $dailySum->shipment_amount ?? 0, 'unique_client_count' => $uniqueClientCount, 'avg_order_amount' => $avgOrderAmount, 'top_client_id' => $topClient->client_id ?? null, 'top_client_amount' => $topClient->total ?? 0, 'mom_growth_rate' => $momGrowthRate, 'yoy_growth_rate' => $yoyGrowthRate, ] ); return 1; } }