|
|
@@ -0,0 +1,102 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Services;
|
|
|
+
|
|
|
+use App\Models\StudentContractInstallment;
|
|
|
+use Carbon\Carbon;
|
|
|
+use Illuminate\Database\Eloquent\Collection;
|
|
|
+use Illuminate\Validation\ValidationException;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Contas a Receber da unidade.
|
|
|
+ *
|
|
|
+ * Decisão de modelagem (A): as parcelas de contrato de aluno
|
|
|
+ * (student_contract_installments) SÃO a Contas a Receber de alunos da unidade.
|
|
|
+ * A baixa é manual aqui; quando houver Asaas na unidade, a baixa também chega
|
|
|
+ * pelo webhook (ramo STU_).
|
|
|
+ */
|
|
|
+class UnitReceivableService
|
|
|
+{
|
|
|
+ public function list(int $unitId, array $filters = []): Collection
|
|
|
+ {
|
|
|
+ return StudentContractInstallment::with(['student'])
|
|
|
+ ->where('unit_id', $unitId)
|
|
|
+ ->when(
|
|
|
+ isset($filters['status']),
|
|
|
+ fn ($q) => $q->where('status', $filters['status']),
|
|
|
+ )
|
|
|
+ ->when(
|
|
|
+ isset($filters['student_id']),
|
|
|
+ fn ($q) => $q->where('student_id', $filters['student_id']),
|
|
|
+ )
|
|
|
+ ->when(
|
|
|
+ isset($filters['type']),
|
|
|
+ fn ($q) => $q->where('type', $filters['type']),
|
|
|
+ )
|
|
|
+ ->orderBy('due_date')
|
|
|
+ ->orderBy('installment_number')
|
|
|
+ ->get();
|
|
|
+ }
|
|
|
+
|
|
|
+ public function findForUnit(int $id, int $unitId): ?StudentContractInstallment
|
|
|
+ {
|
|
|
+ return StudentContractInstallment::where('id', $id)
|
|
|
+ ->where('unit_id', $unitId)
|
|
|
+ ->first();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Baixa manual: marca a parcela como paga.
|
|
|
+ */
|
|
|
+ public function settle(StudentContractInstallment $installment, array $data): StudentContractInstallment
|
|
|
+ {
|
|
|
+ if ($installment->status === 'paid') {
|
|
|
+ throw ValidationException::withMessages([
|
|
|
+ 'status' => 'Esta parcela já está paga.',
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($installment->status === 'cancelled') {
|
|
|
+ throw ValidationException::withMessages([
|
|
|
+ 'status' => 'Esta parcela está cancelada e não pode receber baixa.',
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ $discount = isset($data['discount']) ? (float) $data['discount'] : (float) $installment->discount;
|
|
|
+ $fine = isset($data['fine']) ? (float) $data['fine'] : (float) $installment->fine;
|
|
|
+
|
|
|
+ $paidValue = isset($data['paid_value'])
|
|
|
+ ? (float) $data['paid_value']
|
|
|
+ : round((float) $installment->value - $discount + $fine, 2);
|
|
|
+
|
|
|
+ $installment->update([
|
|
|
+ 'status' => 'paid',
|
|
|
+ 'discount' => $discount,
|
|
|
+ 'fine' => $fine,
|
|
|
+ 'paid_value' => $paidValue,
|
|
|
+ 'payment_date' => $data['payment_date'] ?? Carbon::today()->format('Y-m-d'),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return $installment->fresh(['student']);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Reabre uma parcela paga (estorno da baixa manual).
|
|
|
+ */
|
|
|
+ public function reopen(StudentContractInstallment $installment): StudentContractInstallment
|
|
|
+ {
|
|
|
+ if ($installment->status !== 'paid') {
|
|
|
+ throw ValidationException::withMessages([
|
|
|
+ 'status' => 'Só é possível reabrir parcelas que estão pagas.',
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ $installment->update([
|
|
|
+ 'status' => 'pending',
|
|
|
+ 'paid_value' => 0,
|
|
|
+ 'payment_date' => null,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return $installment->fresh(['student']);
|
|
|
+ }
|
|
|
+}
|