florida_gym/app/Http/Controllers/ReportController.php
2026-03-16 17:31:32 +05:30

335 lines
14 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Account;
use App\Models\Expense;
use App\Models\Staff;
use App\Models\Branch;
use App\Models\BranchDocument;
use Illuminate\Http\Request;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class ReportController extends Controller
{
public function getProfitReport(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');
$page = $request->query('page', 1);
$perPage = $request->query('per_page', 10);
// Base Query from Account table (Ledger)
$query = Account::query()->with(['accountable', 'branch']);
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');
// Today's Stats (always based on today's date, but respecting branch filter)
$todayIncome = Account::where('date', Carbon::today()->toDateString())
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
->sum('credit');
// Monthly Stats (always based on current month, but respecting branch filter)
$monthlyExpense = Account::whereBetween('date', [
Carbon::now()->startOfMonth()->toDateString(),
Carbon::now()->endOfMonth()->toDateString()
])
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
->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' => $a->branch->name ?? 'N/A',
'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::query()
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
->whereBetween('date', [$monthStart->toDateString(), $monthEnd->toDateString()])
->sum('credit');
$monthExpense = Account::query()
->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,
'today_income' => $todayIncome,
'monthly_expense' => $monthlyExpense,
'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()
]);
}
}