|
|
@@ -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
|
|
|
{
|