Procházet zdrojové kódy

feat(unit-payable): service com baixa manual, reabertura e lançamento avulso

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ebagabee před 4 týdny
rodič
revize
0b86aa11b9
1 změnil soubory, kde provedl 111 přidání a 0 odebrání
  1. 111 0
      app/Services/UnitAccountPayableService.php

+ 111 - 0
app/Services/UnitAccountPayableService.php

@@ -0,0 +1,111 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\UnitAccountPayable;
+use Carbon\Carbon;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Validation\ValidationException;
+
+/**
+ * Contas a Pagar da unidade.
+ *
+ * Registros com origin='tbr' são criados automaticamente quando a matriz gera a
+ * cobrança do TBR (espelho da dívida). Registros avulsos usam origin='manual'.
+ * A baixa é manual (Asaas não cobra a unidade de si mesma).
+ */
+class UnitAccountPayableService
+{
+    public function list(int $unitId, array $filters = []): Collection
+    {
+        return UnitAccountPayable::where('unit_id', $unitId)
+            ->when(
+                isset($filters['status']),
+                fn ($q) => $q->where('status', $filters['status']),
+            )
+            ->when(
+                isset($filters['origin']),
+                fn ($q) => $q->where('origin', $filters['origin']),
+            )
+            ->orderBy('due_date')
+            ->get();
+    }
+
+    public function findForUnit(int $id, int $unitId): ?UnitAccountPayable
+    {
+        return UnitAccountPayable::where('id', $id)
+            ->where('unit_id', $unitId)
+            ->first();
+    }
+
+    public function createManual(int $unitId, array $data): UnitAccountPayable
+    {
+        return UnitAccountPayable::create([
+            'unit_id'   => $unitId,
+            'origin'    => UnitAccountPayable::ORIGIN_MANUAL,
+            'history'   => $data['history'],
+            'value'     => $data['value'],
+            'due_date'  => $data['due_date'],
+            'obs'       => $data['obs'] ?? null,
+            'paid_value' => 0,
+            'discount'  => 0,
+            'fine'      => 0,
+            'status'    => 'pending',
+        ]);
+    }
+
+    /**
+     * Baixa manual: marca a conta como paga.
+     */
+    public function settle(UnitAccountPayable $payable, array $data): UnitAccountPayable
+    {
+        if ($payable->status === 'paid') {
+            throw ValidationException::withMessages([
+                'status' => 'Esta conta já está paga.',
+            ]);
+        }
+
+        if ($payable->status === 'cancelled') {
+            throw ValidationException::withMessages([
+                'status' => 'Esta conta está cancelada e não pode receber baixa.',
+            ]);
+        }
+
+        $discount = isset($data['discount']) ? (float) $data['discount'] : (float) $payable->discount;
+        $fine     = isset($data['fine']) ? (float) $data['fine'] : (float) $payable->fine;
+
+        $paidValue = isset($data['paid_value'])
+            ? (float) $data['paid_value']
+            : round((float) $payable->value - $discount + $fine, 2);
+
+        $payable->update([
+            'status'       => 'paid',
+            'discount'     => $discount,
+            'fine'         => $fine,
+            'paid_value'   => $paidValue,
+            'payment_date' => $data['payment_date'] ?? Carbon::today()->format('Y-m-d'),
+        ]);
+
+        return $payable->fresh();
+    }
+
+    /**
+     * Reabre uma conta paga (estorno da baixa manual).
+     */
+    public function reopen(UnitAccountPayable $payable): UnitAccountPayable
+    {
+        if ($payable->status !== 'paid') {
+            throw ValidationException::withMessages([
+                'status' => 'Só é possível reabrir contas que estão pagas.',
+            ]);
+        }
+
+        $payable->update([
+            'status'       => 'pending',
+            'paid_value'   => 0,
+            'payment_date' => null,
+        ]);
+
+        return $payable->fresh();
+    }
+}