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 = Account::with('accountable'); if ($branchId) { $query->where('branch_id', $branchId); } if ($startDate) { $query->where('date', '>=', $startDate); } if ($endDate) { $query->where('date', '<=', $endDate); } $totalCredits = (clone $query)->sum('credit'); $totalDebits = (clone $query)->sum('debit'); // Fetch All Ledger Transactions for the breakdown $accounts = Account::where(function($q) { $q->where('credit', '>', 0)->orWhere('debit', '>', 0); }); if ($branchId) { $accounts->where('branch_id', $branchId); } if ($startDate) { $accounts->where('date', '>=', $startDate); } if ($endDate) { $accounts->where('date', '<=', $endDate); } $accounts = $accounts->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 [ 'date' => $a->date, 'type' => $a->credit > 0 ? 'Income' : 'Expense', 'category' => $a->type, 'description' => $a->description, 'amount' => $a->credit > 0 ? $a->credit : $a->debit, 'branch' => 'N/A', 'is_adjusted' => $isAdjusted, 'original_amount' => $originalAmount, 'remarks' => $remarks ]; }); $expensesQuery = Expense::with('category', 'branch'); if ($branchId) { $expensesQuery->where('branch_id', $branchId); } if ($startDate) { $expensesQuery->where('date', '>=', $startDate); } if ($endDate) { $expensesQuery->where('date', '<=', $endDate); } $expenses = $expensesQuery->get()->map(function($e) { return [ 'date' => $e->date, 'type' => 'Expense', 'category' => $e->category->name ?? 'Other', 'description' => $e->remarks, 'amount' => $e->amount, 'branch' => $e->branch->name ?? 'Global' ]; }); $transactions = $accounts->concat($expenses)->sortByDesc('date')->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'); $monthExpense = Expense::when($branchId, fn($q) => $q->where('branch_id', $branchId)) ->whereBetween('date', [$monthStart->toDateString(), $monthEnd->toDateString()]) ->sum('amount'); $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' => $transactions, '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'); $staffDocsQuery = \App\Models\StaffDocument::with('staff.branch') ->whereNotNull('expiry_date'); if ($branchId) { $staffDocsQuery->whereHas('staff', function($q) use ($branchId) { $q->where('branch_id', $branchId); }); } $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); } $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)->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' => $reportData ]); } }