payments() ->with('recorder.profile') ->latest('paid_at') ->get(); } public function create(TuitionInvoice $invoice, array $data, ?UploadedFile $file): TuitionPayment { $payment = $invoice->payments()->create([ 'amount_paid' => $data['amount_paid'], 'paid_at' => $data['paid_at'], 'payment_method' => $data['payment_method'] ?? null, 'status' => $this->resolveStatus($invoice, $data['amount_paid']), 'recorded_by' => auth()->id(), 'notes' => $data['notes'] ?? null, ]); if ($file) { $payment->addMedia($file)->toMediaCollection('payment_proof'); } return $payment; } public function update(TuitionPayment $payment, array $data, ?UploadedFile $file): TuitionPayment { $payment->amount_paid = $data['amount_paid']; $payment->paid_at = $data['paid_at']; $payment->payment_method = $data['payment_method'] ?? null; $payment->status = $this->resolveStatus($payment->invoice, $data['amount_paid'], excludePaymentId: $payment->id); $payment->notes = $data['notes'] ?? null; $payment->update(); if ($file) { $payment->addMedia($file)->toMediaCollection('payment_proof'); } return $payment; } public function delete(TuitionPayment $payment): bool { return $payment->delete(); } private function resolveStatus(TuitionInvoice $invoice, float|string $amountPaid, ?int $excludePaymentId = null): PaymentStatus { $otherPaidTotal = $invoice->payments() ->when($excludePaymentId, fn ($query) => $query->where('id', '!=', $excludePaymentId)) ->sum('amount_paid'); $totalPaid = round($otherPaidTotal + (float) $amountPaid, 2); $amountDue = round((float) $invoice->amount_due, 2); return $totalPaid >= $amountDue ? PaymentStatus::Paid : PaymentStatus::Partial; } }