129 lines
4.8 KiB
PHP
129 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Refunds;
|
|
|
|
use App\Models\Configuration;
|
|
use App\Models\Refund;
|
|
use App\Models\User;
|
|
use App\Services\ApplicationUrlService;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class RefundRequestService
|
|
{
|
|
public const REQUEST_TYPES = ['tuition', 'overpayment', 'duplicate', 'extra'];
|
|
|
|
public function __construct(
|
|
private RefundPolicyService $policyService,
|
|
private RefundSummaryService $summaryService,
|
|
private RefundNotificationService $notifications,
|
|
private ApplicationUrlService $urls,
|
|
) {
|
|
}
|
|
|
|
public function requestRefund(array $payload, int $actorId): array
|
|
{
|
|
$parentId = (int) ($payload['parent_id'] ?? 0);
|
|
$requestType = strtolower((string) ($payload['request_type'] ?? ''));
|
|
$amount = (float) ($payload['amount'] ?? 0);
|
|
$invoiceId = isset($payload['invoice_id']) ? (int) $payload['invoice_id'] : null;
|
|
$paymentId = isset($payload['payment_id']) ? (int) $payload['payment_id'] : null;
|
|
$reason = $payload['reason'] ?? null;
|
|
|
|
if (!in_array($requestType, self::REQUEST_TYPES, true)) {
|
|
return ['ok' => false, 'message' => 'Invalid refund request type.'];
|
|
}
|
|
|
|
$parent = User::query()->find($parentId);
|
|
if (!$parent) {
|
|
return ['ok' => false, 'message' => 'Parent not found.'];
|
|
}
|
|
|
|
$termSchoolYear = (string) (Configuration::getConfig('school_year') ?? '');
|
|
$termSemester = (string) (Configuration::getConfig('semester') ?? '');
|
|
$schoolYear = (string) ($payload['school_year'] ?? $termSchoolYear);
|
|
$semester = (string) ($payload['semester'] ?? $termSemester);
|
|
|
|
if ($requestType === 'tuition') {
|
|
$result = $this->policyService->processWithdrawalRefund($parentId, $invoiceId, $schoolYear, $semester, $actorId);
|
|
if (empty($result['ok'])) {
|
|
return ['ok' => false, 'message' => $result['error'] ?? 'Refund calculation failed.'];
|
|
}
|
|
|
|
if (($result['refund_amount'] ?? 0) > 0) {
|
|
$this->notifications->notifyPending($parentId, (float) $result['refund_amount'], $this->urls->webLoginUrl());
|
|
}
|
|
|
|
return [
|
|
'ok' => true,
|
|
'refund_id' => $result['refund_id'] ?? null,
|
|
'refund_amount' => $result['refund_amount'] ?? 0.0,
|
|
'status' => $result['status'] ?? null,
|
|
];
|
|
}
|
|
|
|
if (in_array($requestType, ['overpayment', 'extra'], true)) {
|
|
$summary = $this->summaryService->getParentFinancialSummary($parentId, $schoolYear, $semester);
|
|
if (($summary['unapplied_balance'] ?? 0) <= 0) {
|
|
return ['ok' => false, 'message' => 'No unapplied balance available.'];
|
|
}
|
|
if ($amount > (float) ($summary['unapplied_balance'] ?? 0) + 0.0001) {
|
|
return [
|
|
'ok' => false,
|
|
'message' => 'Amount exceeds available unapplied balance.',
|
|
'available' => number_format((float) ($summary['unapplied_balance'] ?? 0), 2),
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($amount <= 0) {
|
|
return ['ok' => false, 'message' => 'Refund amount must be greater than zero.'];
|
|
}
|
|
|
|
return DB::transaction(function () use (
|
|
$parentId,
|
|
$schoolYear,
|
|
$semester,
|
|
$invoiceId,
|
|
$amount,
|
|
$requestType,
|
|
$reason,
|
|
$paymentId,
|
|
$actorId
|
|
) {
|
|
$refund = Refund::query()->create([
|
|
'parent_id' => $parentId,
|
|
'school_year' => $schoolYear,
|
|
'semester' => $semester,
|
|
'invoice_id' => $invoiceId,
|
|
'refund_amount' => $amount,
|
|
'refund_paid_amount' => 0.00,
|
|
'status' => Refund::STATUS_PENDING,
|
|
'reason' => $reason,
|
|
'request' => $requestType,
|
|
'note' => $paymentId ? ('duplicate-of-payment#' . $paymentId) : null,
|
|
'requested_at' => utc_now(),
|
|
'created_at' => utc_now(),
|
|
'updated_at' => utc_now(),
|
|
'updated_by' => $actorId,
|
|
]);
|
|
|
|
if (!$refund) {
|
|
Log::error('Failed to create refund request.', [
|
|
'parent_id' => $parentId,
|
|
'type' => $requestType,
|
|
]);
|
|
return ['ok' => false, 'message' => 'Failed to create refund request.'];
|
|
}
|
|
|
|
$this->notifications->notifyPending($parentId, $amount, $this->urls->webLoginUrl());
|
|
|
|
return [
|
|
'ok' => true,
|
|
'refund_id' => (int) $refund->id,
|
|
'refund_amount' => (float) $refund->refund_amount,
|
|
];
|
|
});
|
|
}
|
|
}
|