user() ?? Auth::guard('receptionist')->user(); if (!$user) return response()->json(['message' => 'Unauthenticated'], 401); $branchId = $user->isReceptionist() ? $user->branch_id : $request->query('branch_id'); $startDate = $request->query('start_date'); $endDate = $request->query('end_date'); $page = $request->query('page', 1); $perPage = $request->query('per_page', 10); // Base Query from Account table (Ledger) $query = Account::query()->with('accountable'); if ($branchId) { $query->where('branch_id', $branchId); } if ($startDate) { $query->where('date', '>=', $startDate); } if ($endDate) { $query->where('date', '<=', $endDate); } // Stats from the same filtered query $totalCredits = (clone $query)->sum('credit'); $totalDebits = (clone $query)->sum('debit'); // Fetch Paginated Transactions from the same filtered query $allTransactions = $query->where(function($q) { $q->where('credit', '>', 0)->orWhere('debit', '>', 0); }) ->orderBy('date', 'desc') ->orderBy('time', 'desc') ->orderBy('id', 'desc') ->get() ->map(function($a) { $isAdjusted = false; $originalAmount = $a->credit > 0 ? $a->credit : $a->debit; $remarks = ''; if ($a->accountable_type === \App\Models\ProductSale::class && $a->accountable) { $originalAmount = $a->accountable->subtotal_amount + $a->accountable->vat_amount; $isAdjusted = abs($a->credit - $originalAmount) > 0.01; $remarks = $a->accountable->remarks; } elseif ($a->accountable_type === \App\Models\Collection::class && $a->accountable) { $originalAmount = $a->accountable->items()->sum('subtotal'); $isAdjusted = $originalAmount > 0 && abs($a->credit - $originalAmount) > 0.01; $remarks = $a->accountable->remarks; } return [ 'id' => $a->id, 'date' => $a->date, 'time' => $a->time || '00:00:00', 'type' => $a->credit > 0 ? 'Income' : 'Expense', 'category' => $a->type, 'description' => $a->description, 'amount' => $a->credit > 0 ? $a->credit : $a->debit, 'branch' => 'N/A', // Branch name if needed can be added via relation 'is_adjusted' => $isAdjusted, 'original_amount' => $originalAmount, 'remarks' => $remarks ]; }); $totalTransactions = $allTransactions->count(); $paginatedTransactions = $allTransactions->forPage($page, $perPage)->values(); $lowStockCount = \App\Models\Product::query(); if ($branchId) { $lowStockCount->where('branch_id', $branchId); } $lowStockCount = $lowStockCount->whereRaw('current_stock <= reorder_level')->count(); // Calculate 6-month trend for Dashboard table/chart $trend = []; for ($i = 5; $i >= 0; $i--) { $monthStart = Carbon::now()->subMonths($i)->startOfMonth(); $monthEnd = Carbon::now()->subMonths($i)->endOfMonth(); $monthIncome = Account::where('branch_id', $branchId ?: '!=', 0) ->when($branchId, fn($q) => $q->where('branch_id', $branchId)) ->whereBetween('date', [$monthStart->toDateString(), $monthEnd->toDateString()]) ->sum('credit'); // For trend, we can also use Account table for expenses $monthExpense = Account::where('branch_id', $branchId ?: '!=', 0) ->when($branchId, fn($q) => $q->where('branch_id', $branchId)) ->whereBetween('date', [$monthStart->toDateString(), $monthEnd->toDateString()]) ->sum('debit'); $trend[] = [ 'month' => $monthStart->format('M'), 'income' => round($monthIncome, 2), 'expense' => round($monthExpense, 2), 'profit' => round($monthIncome - $monthExpense, 2), 'status' => ($monthIncome - $monthExpense) >= 0 ? 'Profit' : 'Loss' ]; } return response()->json([ 'total_income' => $totalCredits, 'total_expense' => $totalDebits, 'net_profit' => $totalCredits - $totalDebits, 'low_stock_count' => $lowStockCount, 'transactions' => $paginatedTransactions, 'pagination' => [ 'total' => $totalTransactions, 'per_page' => (int)$perPage, 'current_page' => (int)$page, 'last_page' => ceil($totalTransactions / $perPage) ], 'trend' => $trend ]); } public function getExpiryReminders(Request $request) { $today = Carbon::today(); $user = Auth::guard('web')->user() ?? Auth::guard('receptionist')->user(); if (!$user) return response()->json(['message' => 'Unauthenticated'], 401); $branchId = $user->isReceptionist() ? $user->branch_id : $request->query('branch_id'); $startDate = $request->query('start_date'); $endDate = $request->query('end_date'); $staffDocsQuery = \App\Models\StaffDocument::with('staff.branch') ->whereNotNull('expiry_date'); if ($branchId) { $staffDocsQuery->whereHas('staff', function($q) use ($branchId) { $q->where('branch_id', $branchId); }); } if ($startDate) { $staffDocsQuery->where('expiry_date', '>=', $startDate); } if ($endDate) { $staffDocsQuery->where('expiry_date', '<=', $endDate); } $staffDocs = $staffDocsQuery->get() ->filter(function($doc) use ($today) { $expiryDate = Carbon::parse($doc->expiry_date)->startOfDay(); $reminderDays = (int)($doc->reminder_days ?? 30); return $today->startOfDay()->greaterThanOrEqualTo($expiryDate->copy()->subDays($reminderDays)); }) ->map(function($doc) use ($today) { $expiryDate = Carbon::parse($doc->expiry_date); $reminderDays = (int)($doc->reminder_days ?? 30); $reminderStartedOn = $expiryDate->copy()->subDays($reminderDays); return [ 'type' => 'Staff', 'entity_name' => $doc->staff->full_name ?? 'N/A', 'branch_name' => $doc->staff->branch->name ?? 'Global', 'document_name' => $doc->name, 'document_number' => $doc->document_number, 'reminder_started_on' => $reminderStartedOn->format('d/m/Y'), 'expiry_date' => $doc->expiry_date, 'days_left' => (int)$today->diffInDays($expiryDate, false) ]; }); // Branch Document Reminders $branchDocsQuery = BranchDocument::with('branch') ->whereNotNull('expiry_date'); if ($branchId) { $branchDocsQuery->where('branch_id', $branchId); } if ($startDate) { $branchDocsQuery->where('expiry_date', '>=', $startDate); } if ($endDate) { $branchDocsQuery->where('expiry_date', '<=', $endDate); } $branchReminders = $branchDocsQuery->get() ->filter(function($doc) use ($today) { $expiryDate = Carbon::parse($doc->expiry_date); $reminderDays = (int)($doc->reminder_days ?? 30); return $today->greaterThanOrEqualTo($expiryDate->copy()->subDays($reminderDays)); }) ->map(function($doc) use ($today) { $expiryDate = Carbon::parse($doc->expiry_date); $reminderDays = (int)($doc->reminder_days ?? 30); $reminderStartedOn = $expiryDate->copy()->subDays($reminderDays); return [ 'type' => 'Branch', 'entity_name' => $doc->branch->name ?? 'N/A', 'branch_name' => $doc->branch->name ?? 'Global', 'document_name' => $doc->name, 'document_number' => $doc->document_number, 'reminder_started_on' => $reminderStartedOn->format('d/m/Y'), 'expiry_date' => $doc->expiry_date, 'days_left' => (int)$today->diffInDays($expiryDate, false) ]; }); return response()->json([ 'reminders' => $staffDocs->concat($branchReminders)->sortBy('expiry_date')->values() ]); } public function getInvestmentReport(Request $request) { $user = Auth::guard('web')->user() ?? Auth::guard('receptionist')->user(); if (!$user) return response()->json(['message' => 'Unauthenticated'], 401); $branchId = $user->isReceptionist() ? $user->branch_id : $request->query('branch_id'); $startDate = $request->query('start_date'); $endDate = $request->query('end_date'); $query = \App\Models\Investor::with('branches'); if ($branchId) { $query->where(function($q) use ($branchId) { $q->where('applicable_to_all_branches', true) ->orWhereHas('branches', function($bq) use ($branchId) { $bq->where('branches.id', $branchId); }); }); } if ($startDate) { $query->where('investment_date', '>=', $startDate); } if ($endDate) { $query->where('investment_date', '<=', $endDate); } $investors = $query->get(); $reportData = []; $currentMonth = Carbon::now()->startOfMonth(); $totalInvested = 0; $totalROIReturned = 0; foreach ($investors as $investor) { $investmentDate = Carbon::parse($investor->investment_date)->startOfMonth(); // Get all actual payouts made to this investor $totalPaid = \App\Models\InvestorPayout::where('investor_id', $investor->id)->sum('amount'); $returnsEarned = $totalPaid; $tempMonth = $investmentDate->copy(); $periodMonths = 1; if ($investor->roi_period === 'Quarterly') $periodMonths = 3; if ($investor->roi_period === 'Yearly') $periodMonths = 12; $totalPending = 0; $carryOver = 0; // Calculate how much should have been paid up to now while ($tempMonth->lessThan($currentMonth)) { $monthKey = $tempMonth->format('F Y'); $baseAmount = round($investor->roi_type === 'Percentage' ? ($investor->investment_amount * ($investor->roi_value / 100)) : ($investor->roi_value ?? 0), 2); $expected = $baseAmount + $carryOver; if ($totalPaid >= $expected && $expected > 0) { $totalPaid -= $expected; $carryOver = 0; } else { $paidTowardsThis = $totalPaid; $remains = $expected - $paidTowardsThis; $totalPaid = 0; $carryOver = $remains; } $tempMonth->addMonths($periodMonths); } $totalPending = $carryOver; // The leftover carryover is the net pending amount $totalInvested += $investor->investment_amount; $totalROIReturned += $returnsEarned; $reportData[] = [ 'investor_id' => $investor->id, 'investor_name' => $investor->name, 'investment_date' => $investor->investment_date, 'investment_amount' => $investor->investment_amount, 'roi_type' => $investor->roi_type, 'roi_value' => $investor->roi_value, 'roi_period' => $investor->roi_period, 'returns_earned' => $returnsEarned, 'total_pending' => $totalPending ]; } return response()->json([ 'total_invested' => $totalInvested, 'total_roi_returned' => $totalROIReturned, 'investors' => collect($reportData)->sortByDesc('investment_date')->values() ]); } }