Quellcode durchsuchen

feat: expand TBR calculation tests to include scenarios for larger municipalities and enhance documentation

alvesantos vor 1 Tag
Ursprung
Commit
f9196f0bc0
1 geänderte Dateien mit 117 neuen und 12 gelöschten Zeilen
  1. 117 12
      tests/Unit/Financeiro/TbrCalculationClientScenariosTest.php

+ 117 - 12
tests/Unit/Financeiro/TbrCalculationClientScenariosTest.php

@@ -18,14 +18,20 @@
 use Tests\TestCase;
 
 /**
- * Reproduz, mês a mês, os dois cenários que o cliente mandou em planilha Excel
- * ("Cenário 50K" e "Cenário 50K a 100K", unidade Foz do Iguaçu, TBR R$ 1.621,00).
+ * Reproduz, mês a mês, os quatro cenários que o cliente mandou em planilha Excel
+ * — um por porte de município (até 50k, 50k-100k, 100k-200k, acima de 200k),
+ * unidade Foz do Iguaçu, TBR R$ 1.621,00.
  *
  * Esses cenários foram o que confirmou a regra correta de Royalties: o percentual
- * da faixa de porte (40%/60% para até 50k habitantes; 50%/75% para 50k-100k) JÁ é
- * o percentual final aplicado direto sobre a TBR — sem nenhuma taxa-base
- * intermediária vinda de outra configuração. Ex.: mês 4, faixa 40%, TBR 1.621,00
- * -> Royalties fixo = R$ 648,40 (0,40 x 1.621,00), batendo com a planilha.
+ * da faixa de porte (40%/60%, 50%/75%, 75%/100%, 100%/150%) JÁ é o percentual
+ * final aplicado direto sobre a TBR — sem nenhuma taxa-base intermediária vinda
+ * de outra configuração. Ex.: mês 4, faixa 40%, TBR 1.621,00 -> Royalties fixo =
+ * R$ 648,40 (0,40 x 1.621,00), batendo com a planilha.
+ *
+ * Os cenários de porte maior (100k-200k e acima de 200k) também confirmam que
+ * Royalties e FNM são comparados de forma independente: nos meses 16-19 (GP) e
+ * 16-29 (MGP) o FNM já vira "percentual do faturamento" enquanto os Royalties
+ * continuam no fixo, exatamente como a planilha mostra.
  *
  * Cada linha testada é [mês de contrato, faturamento do mês, total esperado da
  * cobrança], exatamente como está na planilha do cliente.
@@ -42,6 +48,10 @@ class TbrCalculationClientScenariosTest extends TestCase
 
     private Unit $unitMedioPorte;
 
+    private Unit $unitGrandePorte;
+
+    private Unit $unitMuitoGrandePorte;
+
     protected function setUp(): void
     {
         parent::setUp();
@@ -54,24 +64,33 @@ protected function setUp(): void
         $state   = State::create(['name' => 'Paraná', 'code' => 'PR', 'country_id' => $country->id]);
         $city    = City::create(['name' => 'Foz do Iguaçu', 'country_id' => $country->id, 'state_id' => $state->id]);
 
-        $sizePP = MunicipalitySize::create(['acronym' => 'PP', 'description' => 'Até 50 mil habitantes']);
-        $sizeMP = MunicipalitySize::create(['acronym' => 'MP', 'description' => 'De 50 mil a 100 mil habitantes']);
+        $sizePP  = MunicipalitySize::create(['acronym' => 'PP', 'description' => 'Até 50 mil habitantes']);
+        $sizeMP  = MunicipalitySize::create(['acronym' => 'MP', 'description' => 'De 50 mil a 100 mil habitantes']);
+        $sizeGP  = MunicipalitySize::create(['acronym' => 'GP', 'description' => 'De 100 mil a 200 mil habitantes']);
+        $sizeMGP = MunicipalitySize::create(['acronym' => 'MGP', 'description' => 'Acima de 200 mil habitantes']);
 
-        // Faixas exatamente como no cenário do cliente: 40%/60% (PP) e 50%/75% (MP).
+        // Faixas exatamente como nos cenários do cliente: 40%/60% (PP), 50%/75% (MP),
+        // 75%/100% (GP), 100%/150% (MGP).
         $this->makeBrackets($sizePP->id, [[1, 3, 0.0], [4, 12, 0.40], [13, 60, 0.60]]);
         $this->makeBrackets($sizeMP->id, [[1, 3, 0.0], [4, 12, 0.50], [13, 60, 0.75]]);
+        $this->makeBrackets($sizeGP->id, [[1, 3, 0.0], [4, 12, 0.75], [13, 60, 1.00]]);
+        $this->makeBrackets($sizeMGP->id, [[1, 3, 0.0], [4, 12, 1.00], [13, 60, 1.50]]);
 
-        // TBR e percentuais fixos de FNM/Manutenção iguais em ambos os cenários da planilha.
+        // TBR e percentuais fixos de FNM/Manutenção iguais em todos os cenários da planilha.
         Tbr::create([
             'year' => 2026, 'tbr_value' => self::TBR_VALUE,
             'royalties_percentage' => 0.08, 'fnm_percentage' => 0.20, 'maintenance_percentage' => 0.30,
         ]);
 
-        $this->unitPequenoPorte = $this->makeUnit('Foz do Iguaçu (até 50k)', $city, $state, '00000000000191');
-        $this->unitMedioPorte   = $this->makeUnit('Foz do Iguaçu (50k a 100k)', $city, $state, '00000000000272');
+        $this->unitPequenoPorte     = $this->makeUnit('Foz do Iguaçu (até 50k)', $city, $state, '00000000000191');
+        $this->unitMedioPorte       = $this->makeUnit('Foz do Iguaçu (50k a 100k)', $city, $state, '00000000000272');
+        $this->unitGrandePorte      = $this->makeUnit('Foz do Iguaçu (100k a 200k)', $city, $state, '00000000010316');
+        $this->unitMuitoGrandePorte = $this->makeUnit('Foz do Iguaçu (acima 200k)', $city, $state, '00000000020460');
 
         $this->makeContract($this->unitPequenoPorte->id, $sizePP->id);
         $this->makeContract($this->unitMedioPorte->id, $sizeMP->id);
+        $this->makeContract($this->unitGrandePorte->id, $sizeGP->id);
+        $this->makeContract($this->unitMuitoGrandePorte->id, $sizeMGP->id);
     }
 
     private function makeUnit(string $name, City $city, State $state, string $cnpj): Unit
@@ -187,6 +206,92 @@ public static function cenario50kA100kProvider(): array
         return self::keyed($rows);
     }
 
+    #[DataProvider('cenario100kA200kProvider')]
+    public function test_cenario_100k_a_200k_grande_porte(int $contractMonth, float $revenue, float $expectedTotal): void
+    {
+        [$year, $month] = $this->referenceFor($contractMonth);
+
+        $result = $this->service->preview([
+            'unit_id' => $this->unitGrandePorte->id,
+            'reference_year' => $year,
+            'reference_month' => $month,
+            'revenue_value' => $revenue,
+        ]);
+
+        $this->assertSame($contractMonth, $result['contract_month_reference']);
+        $this->assertEqualsWithDelta($expectedTotal, $result['final_value'], 0.01, "mês {$contractMonth}");
+    }
+
+    #[DataProvider('cenarioAcima200kProvider')]
+    public function test_cenario_acima_200k_porte_muito_grande(int $contractMonth, float $revenue, float $expectedTotal): void
+    {
+        [$year, $month] = $this->referenceFor($contractMonth);
+
+        $result = $this->service->preview([
+            'unit_id' => $this->unitMuitoGrandePorte->id,
+            'reference_year' => $year,
+            'reference_month' => $month,
+            'revenue_value' => $revenue,
+        ]);
+
+        $this->assertSame($contractMonth, $result['contract_month_reference']);
+        $this->assertEqualsWithDelta($expectedTotal, $result['final_value'], 0.01, "mês {$contractMonth}");
+    }
+
+    /** [mês de contrato, faturamento, total esperado] — planilha "100 a 200k". */
+    public static function cenario100kA200kProvider(): array
+    {
+        $rows = [
+            [1, 0, 486.30], [2, 0, 486.30], [3, 0, 486.30],
+            // Royalties fixo (75% x 1.621 = 1.215,75) + FNM fixo (324,20) + TM (486,30).
+            [4, 5000, 2026.25], [5, 6000, 2026.25], [6, 7000, 2026.25], [7, 8000, 2026.25],
+            [8, 9000, 2026.25], [9, 10000, 2026.25], [10, 11000, 2026.25], [11, 12000, 2026.25], [12, 13000, 2026.25],
+            // Mês 13: faixa muda pra 100% (1.621,00 fixo). Royalties e FNM ainda fixos.
+            [13, 14000, 2431.50], [14, 15000, 2431.50], [15, 16000, 2431.50],
+            // Meses 16-19: FNM já virou percentual do faturamento (2%), Royalties continua fixo em 1.621,00.
+            [16, 17000, 2447.30], [17, 18000, 2467.30], [18, 19000, 2487.30], [19, 20000, 2507.30],
+        ];
+
+        // A partir do mês 20, Royalties também vira percentual do faturamento (8%): total = 10% x faturamento + 486,30.
+        for ($month = 20; $month <= 60; $month++) {
+            $revenue = 1000 * $month + 1000;
+            $rows[] = [$month, $revenue, round($revenue * 0.10 + 486.30, 2)];
+        }
+
+        return self::keyed($rows);
+    }
+
+    /** [mês de contrato, faturamento, total esperado] — planilha "Acima de 200k". */
+    public static function cenarioAcima200kProvider(): array
+    {
+        $rows = [
+            [1, 0, 486.30], [2, 0, 486.30], [3, 0, 486.30],
+            // Royalties fixo (100% x 1.621 = 1.621,00) + FNM fixo (324,20) + TM (486,30).
+            [4, 5000, 2431.50], [5, 6000, 2431.50], [6, 7000, 2431.50], [7, 8000, 2431.50],
+            [8, 9000, 2431.50], [9, 10000, 2431.50], [10, 11000, 2431.50], [11, 12000, 2431.50], [12, 13000, 2431.50],
+        ];
+
+        // Mês 13: faixa muda pra 150% (2.431,50 fixo). Royalties e FNM ainda fixos até o mês 15.
+        foreach ([13, 14, 15] as $month) {
+            $revenue = 1000 * $month + 1000;
+            $rows[] = [$month, $revenue, 3242.00];
+        }
+
+        // Meses 16-29: FNM já virou percentual do faturamento (2%), Royalties continua fixo em 2.431,50.
+        for ($month = 16; $month <= 29; $month++) {
+            $revenue = 1000 * $month + 1000;
+            $rows[] = [$month, $revenue, round(2431.50 + 486.30 + $revenue * 0.02, 2)];
+        }
+
+        // A partir do mês 30, Royalties também vira percentual do faturamento (8%): total = 10% x faturamento + 486,30.
+        for ($month = 30; $month <= 60; $month++) {
+            $revenue = 1000 * $month + 1000;
+            $rows[] = [$month, $revenue, round($revenue * 0.10 + 486.30, 2)];
+        }
+
+        return self::keyed($rows);
+    }
+
     /** @param array<int, array{0:int,1:float,2:float}> $rows */
     private static function keyed(array $rows): array
     {