add refund and event logic
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Refunds;
|
||||
|
||||
use App\Models\Configuration;
|
||||
use App\Models\Enrollment;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Refund;
|
||||
use App\Services\Fees\FeeRefundDetailService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RefundPolicyService
|
||||
{
|
||||
public function __construct(
|
||||
private FeeRefundDetailService $refundDetails,
|
||||
private RefundInvoiceAdjustmentService $invoiceAdjustments
|
||||
) {
|
||||
}
|
||||
|
||||
public function processWithdrawalRefund(
|
||||
int $parentId,
|
||||
?int $invoiceId = null,
|
||||
?string $schoolYear = null,
|
||||
?string $semester = null,
|
||||
?int $actorId = null
|
||||
): array {
|
||||
$schoolYear = $schoolYear ?? (string) (Configuration::getConfig('school_year') ?? '');
|
||||
if ($schoolYear === '') {
|
||||
return ['ok' => false, 'error' => 'Missing school year for refund processing.'];
|
||||
}
|
||||
|
||||
$semester = $semester ?? (string) (Configuration::getConfig('semester') ?? '');
|
||||
|
||||
$invoice = $invoiceId
|
||||
? Invoice::query()->find($invoiceId)
|
||||
: Invoice::query()
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->orderByDesc('created_at')
|
||||
->first();
|
||||
|
||||
if (!$invoice) {
|
||||
return ['ok' => false, 'error' => 'No invoice found for refund processing.'];
|
||||
}
|
||||
|
||||
$students = Enrollment::query()
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->map(fn ($row) => $row->toArray())
|
||||
->all();
|
||||
|
||||
if (empty($students)) {
|
||||
return ['ok' => false, 'error' => 'No enrollments found for refund processing.'];
|
||||
}
|
||||
|
||||
$details = $this->refundDetails->calculateRefundDetails($students, $parentId);
|
||||
|
||||
return DB::transaction(function () use (
|
||||
$details,
|
||||
$invoice,
|
||||
$parentId,
|
||||
$schoolYear,
|
||||
$semester,
|
||||
$actorId,
|
||||
$students
|
||||
) {
|
||||
if (empty($details['ok'])) {
|
||||
$reason = $details['error'] ?? 'Refund calculation failed.';
|
||||
$note = json_encode([
|
||||
'parent_id' => $parentId,
|
||||
'invoice_id' => (int) ($invoice->id ?? 0),
|
||||
'error' => $reason,
|
||||
]);
|
||||
|
||||
$existingRefund = Refund::query()->where('invoice_id', (int) $invoice->id)->first();
|
||||
if ($existingRefund) {
|
||||
$existingRefund->update([
|
||||
'refund_amount' => 0.0,
|
||||
'status' => Refund::STATUS_REJECTED,
|
||||
'reason' => $reason,
|
||||
'request' => 'tuition',
|
||||
'note' => $note,
|
||||
'updated_at' => utc_now(),
|
||||
'updated_by' => $actorId,
|
||||
]);
|
||||
} else {
|
||||
Refund::query()->create([
|
||||
'parent_id' => $parentId,
|
||||
'school_year' => $schoolYear,
|
||||
'semester' => $semester,
|
||||
'invoice_id' => (int) $invoice->id,
|
||||
'refund_amount' => 0.0,
|
||||
'refund_paid_amount' => 0.00,
|
||||
'status' => Refund::STATUS_REJECTED,
|
||||
'reason' => $reason,
|
||||
'request' => 'tuition',
|
||||
'note' => $note,
|
||||
'requested_at' => utc_now(),
|
||||
'created_at' => utc_now(),
|
||||
'updated_at' => utc_now(),
|
||||
'updated_by' => $actorId,
|
||||
]);
|
||||
}
|
||||
|
||||
return ['ok' => false, 'error' => $reason];
|
||||
}
|
||||
|
||||
$invoiceUpdate = $this->invoiceAdjustments->applyBookChargeAndUpdateInvoice(
|
||||
$invoice,
|
||||
$parentId,
|
||||
$details,
|
||||
$semester,
|
||||
$actorId
|
||||
);
|
||||
|
||||
$refundAmount = (float) ($details['refund_amount'] ?? 0.0);
|
||||
$debitAmount = (float) ($details['debit_amount'] ?? 0.0);
|
||||
$eligible = !empty($details['eligible']);
|
||||
$status = $refundAmount > 0 ? Refund::STATUS_PENDING : Refund::STATUS_REJECTED;
|
||||
|
||||
$reason = null;
|
||||
if ($refundAmount <= 0) {
|
||||
if (!$eligible) {
|
||||
$reason = 'No refund due to late withdrawal.';
|
||||
} elseif (!empty($details['full_discount'])) {
|
||||
$reason = 'No refund due to full discount.';
|
||||
} elseif ($debitAmount > 0) {
|
||||
$reason = 'No refund due; book fee balance remains.';
|
||||
} else {
|
||||
$reason = 'No refund due after book fee deduction.';
|
||||
}
|
||||
}
|
||||
|
||||
$auditPayload = [
|
||||
'student_ids' => array_map('intval', array_column($students, 'student_id')),
|
||||
'parent_id' => $parentId,
|
||||
'withdraw_date' => $details['withdraw_date'] ?? null,
|
||||
'refund_deadline' => $details['refund_deadline'] ?? null,
|
||||
'eligible' => $details['eligible'] ?? null,
|
||||
'paid_amount' => $details['paid_amount'] ?? null,
|
||||
'book_price' => $details['book_price'] ?? null,
|
||||
'invoice_total' => $details['invoice_total'] ?? null,
|
||||
'discount_total' => $details['discount_total'] ?? null,
|
||||
'full_discount' => $details['full_discount'] ?? null,
|
||||
'refundable_tuition' => $details['refundable_tuition'] ?? null,
|
||||
'withdrawn_eligible_count' => $details['withdrawn_eligible_count'] ?? null,
|
||||
'refund_amount' => $refundAmount,
|
||||
'debit_amount' => $debitAmount,
|
||||
'invoice_id' => (int) ($invoice->id ?? 0),
|
||||
];
|
||||
|
||||
$auditPayload = array_merge($auditPayload, $invoiceUpdate);
|
||||
|
||||
$existingRefund = Refund::query()->where('invoice_id', (int) $invoice->id)->first();
|
||||
if ($existingRefund) {
|
||||
$existingRefund->update([
|
||||
'refund_amount' => $refundAmount,
|
||||
'status' => $status,
|
||||
'reason' => $reason,
|
||||
'request' => 'tuition',
|
||||
'note' => json_encode($auditPayload),
|
||||
'updated_at' => utc_now(),
|
||||
'updated_by' => $actorId,
|
||||
]);
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'refund_id' => (int) $existingRefund->id,
|
||||
'refund_amount' => $refundAmount,
|
||||
'status' => $status,
|
||||
];
|
||||
}
|
||||
|
||||
$created = Refund::query()->create([
|
||||
'parent_id' => $parentId,
|
||||
'school_year' => $schoolYear,
|
||||
'semester' => $semester,
|
||||
'invoice_id' => (int) $invoice->id,
|
||||
'refund_amount' => $refundAmount,
|
||||
'refund_paid_amount' => 0.00,
|
||||
'status' => $status,
|
||||
'reason' => $reason,
|
||||
'request' => 'tuition',
|
||||
'note' => json_encode($auditPayload),
|
||||
'requested_at' => utc_now(),
|
||||
'created_at' => utc_now(),
|
||||
'updated_at' => utc_now(),
|
||||
'updated_by' => $actorId,
|
||||
]);
|
||||
|
||||
if (!$created) {
|
||||
return ['ok' => false, 'error' => 'Failed to create refund record.'];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'refund_id' => (int) $created->id,
|
||||
'refund_amount' => $refundAmount,
|
||||
'status' => $status,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user