Files
alrahma_sunday_school_api/app/Http/Controllers/Api/Finance/FinancialController.php
T
2026-06-09 01:03:53 -04:00

315 lines
12 KiB
PHP

<?php
namespace App\Http\Controllers\Api\Finance;
use App\Http\Controllers\Api\Core\BaseApiController;
use App\Http\Requests\Finance\FinancialReportRequest;
use App\Http\Requests\Finance\FinancialSummaryRequest;
use App\Http\Requests\Finance\StakeholderFinancialAnalysisRequest;
use App\Http\Requests\Finance\FinancialUnpaidParentsRequest;
use App\Http\Requests\Finance\ParentPaymentFollowUpRequest;
use App\Http\Requests\Finance\FollowUpNoteRequest;
use App\Http\Resources\Finance\FinancialReportResource;
use App\Http\Resources\Finance\FinancialSummaryResource;
use App\Http\Resources\Finance\FinancialUnpaidParentResource;
use App\Services\Finance\FinancialPdfReportService;
use App\Services\Finance\FinancialReportService;
use App\Services\Finance\FinancialSchoolYearService;
use App\Services\Finance\FinancialSummaryService;
use App\Services\Finance\StakeholderFinancialAnalysisService;
use App\Services\Finance\FinancialUnpaidParentsService;
use App\Services\Finance\ParentPaymentFollowUpService;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
class FinancialController extends BaseApiController
{
public function __construct(
private FinancialReportService $reportService,
private FinancialSummaryService $summaryService,
private FinancialUnpaidParentsService $unpaidParentsService,
private FinancialSchoolYearService $schoolYears,
private FinancialPdfReportService $pdfService,
private StakeholderFinancialAnalysisService $stakeholderAnalysisService,
private ParentPaymentFollowUpService $parentPaymentFollowUpService
) {
parent::__construct();
}
public function report(FinancialReportRequest $request): JsonResponse
{
$payload = $request->validated();
$report = $this->reportService->getReport(
$payload['date_from'] ?? null,
$payload['date_to'] ?? null,
$payload['school_year'] ?? null
);
return response()->json([
'ok' => true,
'report' => new FinancialReportResource($report),
]);
}
public function summary(FinancialSummaryRequest $request): JsonResponse
{
$payload = $request->validated();
$summary = $this->summaryService->getSummary(
$payload['date_from'] ?? null,
$payload['date_to'] ?? null,
$payload['school_year'] ?? null
);
$schoolYears = $this->schoolYears->listYears($summary['schoolYear'] ?? null);
return response()->json([
'ok' => true,
'summary' => new FinancialSummaryResource($summary),
'schoolYears' => $schoolYears,
]);
}
public function unpaidParents(FinancialUnpaidParentsRequest $request): JsonResponse
{
$payload = $request->validated();
$data = $this->unpaidParentsService->getUnpaidParents($payload['school_year'] ?? null);
return response()->json([
'ok' => true,
'schoolYear' => $data['schoolYear'] ?? null,
'schoolYears' => $data['schoolYears'] ?? [],
'results' => FinancialUnpaidParentResource::collection($data['results'] ?? []),
]);
}
public function parentPaymentFollowups(ParentPaymentFollowUpRequest $request): JsonResponse
{
return response()->json([
'ok' => true,
'followups' => $this->parentPaymentFollowUpService->report($request->validated()),
]);
}
public function parentPaymentFollowupsCsv(ParentPaymentFollowUpRequest $request): StreamedResponse
{
$report = $this->parentPaymentFollowUpService->report($request->validated());
$rows = $this->parentPaymentFollowUpService->csvRows($report);
return response()->streamDownload(function () use ($rows) {
$out = fopen('php://output', 'w');
foreach ($rows as $row) {
fputcsv($out, $row);
}
fclose($out);
}, 'parent_payment_followups_' . ($report['schoolYear'] ?? 'report') . '_' . date('Ymd_His') . '.csv', ['Content-Type' => 'text/csv']);
}
public function storeParentFollowUpNote(FollowUpNoteRequest $request, int $parent): JsonResponse
{
return response()->json([
'ok' => true,
'note' => $this->parentPaymentFollowUpService->storeNote($parent, $request->validated(), optional($request->user())->id),
], 201);
}
public function markParentContacted(FollowUpNoteRequest $request, int $parent): JsonResponse
{
$payload = array_merge($request->validated(), ['status' => $request->input('status', 'contacted')]);
return response()->json(['ok' => true, 'note' => $this->parentPaymentFollowUpService->storeNote($parent, $payload, optional($request->user())->id)], 201);
}
public function storePromiseToPay(FollowUpNoteRequest $request, int $parent): JsonResponse
{
$payload = array_merge($request->validated(), ['status' => 'promise_to_pay']);
return response()->json(['ok' => true, 'note' => $this->parentPaymentFollowUpService->storeNote($parent, $payload, optional($request->user())->id)], 201);
}
public function resolveParentFollowUp(FollowUpNoteRequest $request, int $parent): JsonResponse
{
$payload = array_merge($request->validated(), ['status' => 'resolved']);
return response()->json(['ok' => true, 'note' => $this->parentPaymentFollowUpService->storeNote($parent, $payload, optional($request->user())->id)], 201);
}
public function stakeholderAnalysis(StakeholderFinancialAnalysisRequest $request): JsonResponse
{
$payload = $request->validated();
$analysis = $this->stakeholderAnalysisService->analyze(
$payload['date_from'] ?? null,
$payload['date_to'] ?? null,
$payload['school_year'] ?? null,
$payload['compare_school_year'] ?? null
);
return response()->json([
'ok' => true,
'analysis' => $analysis,
]);
}
public function stakeholderAnalysisCsv(StakeholderFinancialAnalysisRequest $request): StreamedResponse
{
$payload = $request->validated();
$analysis = $this->stakeholderAnalysisService->analyze(
$payload['date_from'] ?? null,
$payload['date_to'] ?? null,
$payload['school_year'] ?? null,
$payload['compare_school_year'] ?? null
);
$rows = $this->stakeholderAnalysisService->csvRows($analysis);
$schoolYear = $analysis['schoolYear'] ?? 'report';
return response()->streamDownload(function () use ($rows) {
$out = fopen('php://output', 'w');
foreach ($rows as $row) {
fputcsv($out, $row);
}
fclose($out);
}, 'stakeholder_financial_analysis_' . $schoolYear . '_' . date('Ymd_His') . '.csv', ['Content-Type' => 'text/csv']);
}
public function downloadCsv(FinancialReportRequest $request): StreamedResponse
{
$payload = $request->validated();
$report = $this->reportService->getReport(
$payload['date_from'] ?? null,
$payload['date_to'] ?? null,
$payload['school_year'] ?? null
);
$paymentsMap = [];
foreach ($report['payments'] as $payment) {
$invoiceId = (int) ($payment['invoice_id'] ?? 0);
$paymentsMap[$invoiceId] = (float) ($payment['paid_amount'] ?? 0);
}
$refundsMap = [];
foreach ($report['refunds'] as $refund) {
$invoiceId = (int) ($refund['invoice_id'] ?? 0);
$refundsMap[$invoiceId] = (float) ($refund['total_refunded'] ?? 0);
}
$discountsMap = [];
foreach ($report['discounts'] as $discount) {
$invoiceId = (int) ($discount['invoice_id'] ?? 0);
$discountsMap[$invoiceId] = (float) ($discount['discount_amount'] ?? 0);
}
$breakdown = $report['paymentBreakdown'] ?? [];
$filename = 'financial_report_' . date('Ymd_His') . '.csv';
return response()->streamDownload(function () use ($report, $paymentsMap, $refundsMap, $discountsMap, $breakdown) {
$out = fopen('php://output', 'w');
fputcsv($out, ['Invoice #', 'Parent', 'School Year', 'Total', 'Paid', 'Cash', 'Credit', 'Check', 'Balance', 'Refund', 'Discount', 'Status']);
$sumTotal = 0.0;
$sumPaid = 0.0;
$sumCash = 0.0;
$sumCredit = 0.0;
$sumCheck = 0.0;
$sumBalance = 0.0;
$sumRefund = 0.0;
$sumDiscount = 0.0;
foreach ($report['invoices'] as $inv) {
$invoiceId = (int) ($inv['id'] ?? 0);
$paid = (float) ($paymentsMap[$invoiceId] ?? 0);
$bd = $breakdown[$invoiceId] ?? [];
$cash = (float) ($bd['cash'] ?? 0);
$credit = (float) ($bd['credit'] ?? 0);
$check = (float) ($bd['check'] ?? 0);
$refunded = (float) ($refundsMap[$invoiceId] ?? 0);
$discount = (float) ($discountsMap[$invoiceId] ?? 0);
$total = (float) ($inv['total_amount'] ?? 0);
$balance = $total - $paid - $discount - $refunded;
if ($balance < 0) {
$balance = 0.0;
}
$status = $balance === 0.0 ? 'Paid' : 'Unpaid';
fputcsv($out, [
$inv['invoice_number'] ?? '',
$inv['parent_name'] ?? '',
$inv['school_year'] ?? '',
$total,
$paid,
$cash,
$credit,
$check,
$balance,
$refunded,
$discount,
$status,
]);
$sumTotal += $total;
$sumPaid += $paid;
$sumCash += $cash;
$sumCredit += $credit;
$sumCheck += $check;
$sumBalance += $balance;
$sumRefund += $refunded;
$sumDiscount += $discount;
}
fputcsv($out, [
'TOTALS',
'',
'',
$sumTotal,
$sumPaid,
$sumCash,
$sumCredit,
$sumCheck,
$sumBalance,
$sumRefund,
$sumDiscount,
'',
]);
fputcsv($out, []);
fputcsv($out, ['Expenses Summary']);
fputcsv($out, ['Category', 'Total Amount']);
foreach ($report['expenses'] as $exp) {
fputcsv($out, [
$exp['category'] ?? '',
$exp['total_amount'] ?? 0,
]);
}
fputcsv($out, []);
fputcsv($out, ['Reimbursements Summary']);
fputcsv($out, ['Status', 'Total Amount']);
foreach ($report['reimbursements'] as $row) {
fputcsv($out, [
$row['status'] ?? '',
$row['total_amount'] ?? 0,
]);
}
fclose($out);
}, $filename, ['Content-Type' => 'text/csv']);
}
public function downloadPdf(FinancialReportRequest $request): StreamedResponse
{
$payload = $request->validated();
$summary = $this->summaryService->getSummary(
$payload['date_from'] ?? null,
$payload['date_to'] ?? null,
$payload['school_year'] ?? null
);
$pdfBytes = $this->pdfService->buildPdf($summary);
$schoolYear = $summary['schoolYear'] ?? 'report';
return response()->streamDownload(function () use ($pdfBytes) {
echo $pdfBytes;
}, 'Financial_Report_' . $schoolYear . '.pdf', ['Content-Type' => 'application/pdf']);
}
}