ソースを参照

feat(tbr): faturamento real por competência alimenta o cálculo (fixo x % faturamento)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ebagabee 4 週間 前
コミット
a833f354af
1 ファイル変更51 行追加4 行削除
  1. 51 4
      app/Services/TbrCalculationService.php

+ 51 - 4
app/Services/TbrCalculationService.php

@@ -6,6 +6,7 @@
 use App\Models\FranchiseeAccountReceiveDetail;
 use App\Models\FranchiseeContract;
 use App\Models\InhabitantClassification;
+use App\Models\StudentContractInstallment;
 use App\Models\Tbr;
 use App\Models\TbrCalculation;
 use App\Models\UnitFinancial;
@@ -23,6 +24,45 @@ class TbrCalculationService
     private const MAINTENANCE_RATE       = 0.30;
     private const EXEMPT_THRESHOLD_MONTH = 3;
 
+    // Faturamento (competência): por padrão usa o próprio mês de referência
+    // (Opção A — assume-se o mês já fechado ao gerar). Ligue para usar o mês
+    // anterior fechado (Opção B) quando a geração ocorrer dentro do mês corrente.
+    private const REVENUE_FROM_PREVIOUS_MONTH = false;
+
+    /**
+     * Faturamento da unidade no mês (competência): soma das parcelas de aluno com
+     * vencimento no mês, de contratos não cancelados. Alimenta a regra "maior entre
+     * valor fixo e % do faturamento".
+     */
+    public function resolveRevenue(int $unitId, int $referenceYear, int $referenceMonth): float
+    {
+        $base = Carbon::createFromDate($referenceYear, $referenceMonth, 1);
+
+        if (self::REVENUE_FROM_PREVIOUS_MONTH) {
+            $base = $base->subMonthNoOverflow();
+        }
+
+        return (float) StudentContractInstallment::whereNull('deleted_at')
+            ->where('unit_id', $unitId)
+            ->where('status', '!=', 'cancelled')
+            ->whereBetween('due_date', [
+                $base->copy()->startOfMonth()->toDateString(),
+                $base->copy()->endOfMonth()->toDateString(),
+            ])
+            ->sum('value');
+    }
+
+    /**
+     * Faturamento do cálculo individual: usa o valor informado manualmente
+     * (override) quando > 0; senão calcula automaticamente pelas parcelas do mês.
+     */
+    private function pickRevenue(array $data, int $unitId, int $year, int $month): float
+    {
+        $manual = (float) ($data['revenue_value'] ?? 0);
+
+        return $manual > 0 ? $manual : $this->resolveRevenue($unitId, $year, $month);
+    }
+
     public function paginate(int $perPage = 15): LengthAwarePaginator
     {
         return TbrCalculation::with(['unit', 'user'])
@@ -50,7 +90,10 @@ public function findById(int $id): ?TbrCalculation
     public function preview(array $data): array
     {
         $contract = $this->resolveContract($data['unit_id']);
-        return $this->buildPreview($contract, (int) $data['reference_year'], (int) $data['reference_month'], (float) ($data['revenue_value'] ?? 0));
+        $year     = (int) $data['reference_year'];
+        $month    = (int) $data['reference_month'];
+
+        return $this->buildPreview($contract, $year, $month, $this->pickRevenue($data, $contract->unit_id, $year, $month));
     }
 
     public function previewBatch(int $referenceYear, int $referenceMonth): array
@@ -59,7 +102,8 @@ public function previewBatch(int $referenceYear, int $referenceMonth): array
 
         return $contracts->map(function (FranchiseeContract $contract) use ($referenceYear, $referenceMonth) {
             try {
-                return $this->buildPreview($contract, $referenceYear, $referenceMonth, 0);
+                $revenue = $this->resolveRevenue($contract->unit_id, $referenceYear, $referenceMonth);
+                return $this->buildPreview($contract, $referenceYear, $referenceMonth, $revenue);
             } catch (ValidationException $e) {
                 return [
                     'unit_id'   => $contract->unit_id,
@@ -74,7 +118,9 @@ public function calculate(array $data): TbrCalculation
     {
         return DB::transaction(function () use ($data) {
             $contract = $this->resolveContract($data['unit_id']);
-            $payload  = $this->buildPreview($contract, (int) $data['reference_year'], (int) $data['reference_month'], (float) ($data['revenue_value'] ?? 0));
+            $year     = (int) $data['reference_year'];
+            $month    = (int) $data['reference_month'];
+            $payload  = $this->buildPreview($contract, $year, $month, $this->pickRevenue($data, $contract->unit_id, $year, $month));
 
             return $this->persistCalculation($payload);
         });
@@ -127,7 +173,8 @@ public function generateBatch(int $referenceYear, int $referenceMonth, ?array $u
         foreach ($contracts as $contract) {
             try {
                 DB::transaction(function () use ($contract, $referenceYear, $referenceMonth, &$generated, &$skipped) {
-                    $payload = $this->buildPreview($contract, $referenceYear, $referenceMonth, 0);
+                    $revenue = $this->resolveRevenue($contract->unit_id, $referenceYear, $referenceMonth);
+                    $payload = $this->buildPreview($contract, $referenceYear, $referenceMonth, $revenue);
 
                     if ($payload['receivable_already_generated']) {
                         $skipped[] = [