florida_gym/app/Http/Controllers/ReportController.php
2026-03-11 11:03:12 +05:30

295 lines
12 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');
$query = Account::with('accountable');
if ($branchId) {
$query->where('branch_id', $branchId);
}
if ($startDate) {
$query->where('date', '>=', $startDate);
}
if ($endDate) {
$query->where('date', '<=', $endDate);
}
$totalCredits = $query->sum('credit');
$totalDebits = (clone $query)->sum('debit');
// Note: We use Account table for both to ensure consistency with the "Total Received" and "Total Debited" requirement.
// If Expenses are also tracked in Accounts as debits (which they should be), this is correct.
// Fetch Transactions for the breakdown
$accounts = Account::select('date', 'credit as amount', 'type', 'description')
->where('credit', '>', 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->amount;
$remarks = '';
if ($a->accountable_type === \App\Models\ProductSale::class && $a->accountable) {
$originalAmount = $a->accountable->subtotal_amount + $a->accountable->vat_amount;
$isAdjusted = abs($a->amount - $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->amount - $originalAmount) > 0.01;
$remarks = $a->accountable->remarks;
}
return [
'date' => $a->date,
'type' => 'Income',
'category' => $a->type,
'description' => $a->description,
'amount' => $a->amount,
'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();
return response()->json([
'total_income' => $totalCredits,
'total_expense' => $totalDebits,
'net_profit' => $totalCredits - $totalDebits,
'low_stock_count' => $lowStockCount,
'transactions' => $transactions
]);
}
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
]);
}
}