tenantId(); $userId = $this->apiUserId(); $today = Carbon::today(); $startOfMonth = Carbon::now()->startOfMonth(); $endOfMonth = Carbon::now()->endOfMonth(); return [ 'today' => $this->getTodaySummary($tenantId, $today), 'finance' => $this->getFinanceSummary($tenantId, $startOfMonth, $endOfMonth), 'sales' => $this->getSalesSummary($tenantId, $startOfMonth, $endOfMonth), 'tasks' => $this->getTasksSummary($tenantId, $userId), ]; } /** * 대시보드 차트 데이터 조회 * * @param array $params [period: week|month|quarter] */ public function charts(array $params): array { $tenantId = $this->tenantId(); $period = $params['period'] ?? 'month'; [$startDate, $endDate] = $this->getPeriodRange($period); return [ 'period' => $period, 'start_date' => $startDate->toDateString(), 'end_date' => $endDate->toDateString(), 'deposit_trend' => $this->getDepositTrend($tenantId, $startDate, $endDate), 'withdrawal_trend' => $this->getWithdrawalTrend($tenantId, $startDate, $endDate), 'sales_by_client' => $this->getSalesByClient($tenantId, $startDate, $endDate), ]; } /** * 결재 현황 조회 * * @param array $params [limit: int] */ public function approvals(array $params): array { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); $limit = $params['limit'] ?? 10; // 내가 결재할 문서 (결재함) $pendingApprovals = $this->getPendingApprovals($tenantId, $userId, $limit); // 내가 기안한 문서 중 진행중인 것 $myDrafts = $this->getMyPendingDrafts($tenantId, $userId, $limit); return [ 'pending_approvals' => $pendingApprovals, 'my_drafts' => $myDrafts, ]; } /** * 오늘 요약 데이터 */ private function getTodaySummary(int $tenantId, Carbon $today): array { // 오늘 출근자 수 $attendancesCount = Attendance::query() ->where('tenant_id', $tenantId) ->whereDate('work_date', $today) ->whereNotNull('check_in') ->count(); // 오늘 휴가자 수 $leavesCount = Leave::query() ->where('tenant_id', $tenantId) ->where('status', 'approved') ->whereDate('start_date', '<=', $today) ->whereDate('end_date', '>=', $today) ->count(); // 결재 대기 문서 수 (전체) $approvalsPending = Approval::query() ->where('tenant_id', $tenantId) ->where('status', 'pending') ->count(); return [ 'date' => $today->toDateString(), 'attendances_count' => $attendancesCount, 'leaves_count' => $leavesCount, 'approvals_pending' => $approvalsPending, ]; } /** * 재무 요약 데이터 (sam_stat 우선, 폴백: 원본 DB) */ private function getFinanceSummary(int $tenantId, Carbon $startOfMonth, Carbon $endOfMonth): array { // sam_stat 월간 데이터 시도 $monthly = StatFinanceMonthly::where('tenant_id', $tenantId) ->where('stat_year', $startOfMonth->year) ->where('stat_month', $startOfMonth->month) ->first(); if ($monthly) { return [ 'monthly_deposit' => (float) $monthly->deposit_amount, 'monthly_withdrawal' => (float) $monthly->withdrawal_amount, 'balance' => (float) ($monthly->deposit_amount - $monthly->withdrawal_amount), 'source' => 'sam_stat', ]; } // 폴백: 원본 DB 실시간 집계 $monthlyDeposit = Deposit::query() ->where('tenant_id', $tenantId) ->whereBetween('deposit_date', [$startOfMonth, $endOfMonth]) ->sum('amount'); $monthlyWithdrawal = Withdrawal::query() ->where('tenant_id', $tenantId) ->whereBetween('withdrawal_date', [$startOfMonth, $endOfMonth]) ->sum('amount'); $totalDeposits = Deposit::query() ->where('tenant_id', $tenantId) ->sum('amount'); $totalWithdrawals = Withdrawal::query() ->where('tenant_id', $tenantId) ->sum('amount'); $balance = $totalDeposits - $totalWithdrawals; return [ 'monthly_deposit' => (float) $monthlyDeposit, 'monthly_withdrawal' => (float) $monthlyWithdrawal, 'balance' => (float) $balance, 'source' => 'samdb', ]; } /** * 매출/매입 요약 데이터 (sam_stat 우선, 폴백: 원본 DB) */ private function getSalesSummary(int $tenantId, Carbon $startOfMonth, Carbon $endOfMonth): array { // sam_stat 월간 데이터 시도 $monthly = StatSalesMonthly::where('tenant_id', $tenantId) ->where('stat_year', $startOfMonth->year) ->where('stat_month', $startOfMonth->month) ->first(); if ($monthly) { return [ 'monthly_sales' => (float) $monthly->sales_amount, 'monthly_purchases' => 0, 'source' => 'sam_stat', ]; } // 폴백: 원본 DB 실시간 집계 $monthlySales = Sale::query() ->where('tenant_id', $tenantId) ->whereBetween('sale_date', [$startOfMonth, $endOfMonth]) ->sum('total_amount'); $monthlyPurchases = Purchase::query() ->where('tenant_id', $tenantId) ->whereBetween('purchase_date', [$startOfMonth, $endOfMonth]) ->sum('total_amount'); return [ 'monthly_sales' => (float) $monthlySales, 'monthly_purchases' => (float) $monthlyPurchases, 'source' => 'samdb', ]; } /** * 할 일 요약 데이터 */ private function getTasksSummary(int $tenantId, int $userId): array { // 내가 결재해야 할 문서 수 $pendingApprovals = ApprovalStep::query() ->whereHas('approval', function ($query) use ($tenantId) { $query->where('tenant_id', $tenantId) ->where('status', 'pending'); }) ->where('approver_id', $userId) ->where('status', 'pending') ->count(); // 승인 대기 휴가 신청 수 (관리자용) $pendingLeaves = Leave::query() ->where('tenant_id', $tenantId) ->where('status', 'pending') ->count(); return [ 'pending_approvals' => $pendingApprovals, 'pending_leaves' => $pendingLeaves, ]; } /** * 기간 범위 계산 * * @return array [Carbon $startDate, Carbon $endDate] */ private function getPeriodRange(string $period): array { $endDate = Carbon::today(); switch ($period) { case 'week': $startDate = $endDate->copy()->subDays(6); break; case 'quarter': $startDate = $endDate->copy()->subMonths(3)->startOfMonth(); break; case 'month': default: $startDate = $endDate->copy()->subDays(29); break; } return [$startDate, $endDate]; } /** * 입금 추이 데이터 */ private function getDepositTrend(int $tenantId, Carbon $startDate, Carbon $endDate): array { $deposits = Deposit::query() ->where('tenant_id', $tenantId) ->whereBetween('deposit_date', [$startDate, $endDate]) ->select( DB::raw('DATE(deposit_date) as date'), DB::raw('SUM(amount) as amount') ) ->groupBy(DB::raw('DATE(deposit_date)')) ->orderBy('date') ->get(); return $deposits->map(function ($item) { return [ 'date' => $item->date, 'amount' => (float) $item->amount, ]; })->toArray(); } /** * 출금 추이 데이터 */ private function getWithdrawalTrend(int $tenantId, Carbon $startDate, Carbon $endDate): array { $withdrawals = Withdrawal::query() ->where('tenant_id', $tenantId) ->whereBetween('withdrawal_date', [$startDate, $endDate]) ->select( DB::raw('DATE(withdrawal_date) as date'), DB::raw('SUM(amount) as amount') ) ->groupBy(DB::raw('DATE(withdrawal_date)')) ->orderBy('date') ->get(); return $withdrawals->map(function ($item) { return [ 'date' => $item->date, 'amount' => (float) $item->amount, ]; })->toArray(); } /** * 거래처별 매출 데이터 */ private function getSalesByClient(int $tenantId, Carbon $startDate, Carbon $endDate): array { $sales = Sale::query() ->where('tenant_id', $tenantId) ->whereBetween('sale_date', [$startDate, $endDate]) ->with('client:id,name') ->select( 'client_id', DB::raw('SUM(total_amount) as amount') ) ->groupBy('client_id') ->orderByDesc('amount') ->limit(10) ->get(); return $sales->map(function ($item) { return [ 'client_id' => $item->client_id, 'client_name' => $item->client?->name ?? __('message.dashboard.unknown_client'), 'amount' => (float) $item->amount, ]; })->toArray(); } /** * 내가 결재해야 할 문서 목록 */ private function getPendingApprovals(int $tenantId, int $userId, int $limit): array { $steps = ApprovalStep::query() ->whereHas('approval', function ($query) use ($tenantId) { $query->where('tenant_id', $tenantId) ->where('status', 'pending'); }) ->where('approver_id', $userId) ->where('status', 'pending') ->with(['approval' => function ($query) { $query->with('drafter:id,name'); }]) ->orderBy('created_at', 'desc') ->limit($limit) ->get(); return $steps->map(function ($step) { return [ 'id' => $step->approval->id, 'title' => $step->approval->title, 'drafter_name' => $step->approval->drafter?->name ?? '', 'status' => $step->approval->status, 'created_at' => $step->approval->created_at?->toDateTimeString(), ]; })->toArray(); } /** * 내가 기안한 진행중인 문서 목록 */ private function getMyPendingDrafts(int $tenantId, int $userId, int $limit): array { $approvals = Approval::query() ->where('tenant_id', $tenantId) ->where('drafter_id', $userId) ->where('status', 'pending') ->orderBy('created_at', 'desc') ->limit($limit) ->get(); return $approvals->map(function ($approval) { return [ 'id' => $approval->id, 'title' => $approval->title, 'status' => $approval->status, 'current_step' => $approval->current_step, 'created_at' => $approval->created_at?->toDateTimeString(), ]; })->toArray(); } }