|
@@ -126,12 +126,12 @@ private function makeBrackets(int $sizeId, array $brackets, bool $isRenewal = fa
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private function makeTbrYear(int $year, float $value, float $fnm = 0.20, float $maintenance = 0.30): Tbr
|
|
|
|
|
|
|
+ private function makeTbrYear(int $year, float $value, float $fnm = 0.20, float $maintenance = 0.30, float $royalties = 0.08): Tbr
|
|
|
{
|
|
{
|
|
|
return Tbr::create([
|
|
return Tbr::create([
|
|
|
'year' => $year,
|
|
'year' => $year,
|
|
|
'tbr_value' => $value,
|
|
'tbr_value' => $value,
|
|
|
- 'royalties_percentage' => 0.08,
|
|
|
|
|
|
|
+ 'royalties_percentage' => $royalties,
|
|
|
'fnm_percentage' => $fnm,
|
|
'fnm_percentage' => $fnm,
|
|
|
'maintenance_percentage' => $maintenance,
|
|
'maintenance_percentage' => $maintenance,
|
|
|
]);
|
|
]);
|
|
@@ -308,25 +308,24 @@ public function test_isencao_acaba_no_mes_4(): void
|
|
|
// ---------------------------------------------- Royalties: regra do maior valor
|
|
// ---------------------------------------------- Royalties: regra do maior valor
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Regra (md "Royalties" meses 4-60): maior entre % fixo x TBR ou 8% do faturamento.
|
|
|
|
|
- * O % fixo é a taxa-base configurada em "Configurações do TBR"
|
|
|
|
|
- * (tbrs.royalties_percentage, 8% no cenário base) multiplicada pelo percentual
|
|
|
|
|
- * da faixa de porte (Classificação de Habitantes) — a faixa é um multiplicador,
|
|
|
|
|
- * não o percentual final.
|
|
|
|
|
|
|
+ * Regra (md "Royalties" meses 4-60): maior entre % fixo da faixa de porte x TBR
|
|
|
|
|
+ * ou 8% do faturamento. O percentual da faixa (Classificação de Habitantes) já é
|
|
|
|
|
+ * o percentual final sobre a TBR — confirmado pelo cenário do cliente (Excel):
|
|
|
|
|
+ * faixa 40% x TBR R$ 1.621,00 = R$ 648,40 direto, sem taxa-base intermediária.
|
|
|
*
|
|
*
|
|
|
- * Cenário: Mês 4, faixa GP = 75% (multiplicador), taxa-base = 8% -> fixo efetivo
|
|
|
|
|
- * = 6% da TBR = R$ 60,00 (TBR R$ 1.000,00). Faturamento baixo (R$ 500,00, 8% = R$ 40,00).
|
|
|
|
|
|
|
+ * Cenário: Mês 4, TBR R$ 1.000,00 (75% da faixa = R$ 750,00), faturamento
|
|
|
|
|
+ * R$ 1.000,00 (8% = R$ 80,00).
|
|
|
*
|
|
*
|
|
|
- * Espera: Royalties = R$ 60,00, critério = 'tbr_fixo'.
|
|
|
|
|
|
|
+ * Espera: Royalties = R$ 750,00, critério = 'tbr_fixo'.
|
|
|
*/
|
|
*/
|
|
|
public function test_royalties_usa_valor_fixo_quando_faturamento_e_baixo(): void
|
|
public function test_royalties_usa_valor_fixo_quando_faturamento_e_baixo(): void
|
|
|
{
|
|
{
|
|
|
$this->makeContract();
|
|
$this->makeContract();
|
|
|
|
|
|
|
|
- $result = $this->preview(2026, 4, 500.00);
|
|
|
|
|
|
|
+ $result = $this->preview(2026, 4, 1000.00);
|
|
|
|
|
|
|
|
- $this->assertSame(60.0, $result['royalties_effective_value']);
|
|
|
|
|
- $this->assertSame(0.06, $result['royalties_effective_percentage']);
|
|
|
|
|
|
|
+ $this->assertSame(750.0, $result['royalties_effective_value']);
|
|
|
|
|
+ $this->assertSame(0.75, $result['royalties_effective_percentage']);
|
|
|
$this->assertSame('tbr_fixo', $result['royalties_applied_criteria']);
|
|
$this->assertSame('tbr_fixo', $result['royalties_applied_criteria']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -351,20 +350,20 @@ public function test_royalties_usa_percentual_do_faturamento_quando_faturamento_
|
|
|
/**
|
|
/**
|
|
|
* Regra: Critério de desempate — valor igual mantém o fixo da TBR (comparação é >=).
|
|
* Regra: Critério de desempate — valor igual mantém o fixo da TBR (comparação é >=).
|
|
|
*
|
|
*
|
|
|
- * Cenário: Fixo do mês 4 = 8% (base) x 75% (faixa GP) = 6% x TBR R$ 1.000 = R$ 60,00.
|
|
|
|
|
- * Faturamento R$ 750,00, em que 8% = R$ 60,00 = exatamente o valor fixo.
|
|
|
|
|
|
|
+ * Cenário: Faturamento R$ 9.375,00, em que 8% = R$ 750,00 = exatamente o valor fixo.
|
|
|
*
|
|
*
|
|
|
- * Espera: Royalties = R$ 60,00, critério = 'tbr_fixo', percentual gravado = 0,06.
|
|
|
|
|
|
|
+ * Espera: Royalties = R$ 750,00, critério = 'tbr_fixo', percentual gravado = 0,75 (o da faixa).
|
|
|
*/
|
|
*/
|
|
|
public function test_empate_entre_fixo_e_faturamento_mantem_o_fixo_da_tbr(): void
|
|
public function test_empate_entre_fixo_e_faturamento_mantem_o_fixo_da_tbr(): void
|
|
|
{
|
|
{
|
|
|
$this->makeContract();
|
|
$this->makeContract();
|
|
|
|
|
|
|
|
- $result = $this->preview(2026, 4, 750.00);
|
|
|
|
|
|
|
+ // 8% x 9.375 = 750,00 = valor fixo dos royalties
|
|
|
|
|
+ $result = $this->preview(2026, 4, 9375.00);
|
|
|
|
|
|
|
|
- $this->assertSame(60.0, $result['royalties_effective_value']);
|
|
|
|
|
|
|
+ $this->assertSame(750.0, $result['royalties_effective_value']);
|
|
|
$this->assertSame('tbr_fixo', $result['royalties_applied_criteria']);
|
|
$this->assertSame('tbr_fixo', $result['royalties_applied_criteria']);
|
|
|
- $this->assertSame(0.06, $result['royalties_effective_percentage']);
|
|
|
|
|
|
|
+ $this->assertSame(0.75, $result['royalties_effective_percentage']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------- FNM: regra do maior valor
|
|
// --------------------------------------------------------- FNM: regra do maior valor
|
|
@@ -466,23 +465,20 @@ public function test_faixa_de_royalties_muda_no_mes_13(): void
|
|
|
$mes12 = $this->preview(2026, 12, 1000);
|
|
$mes12 = $this->preview(2026, 12, 1000);
|
|
|
$mes13 = $this->preview(2027, 1, 1000);
|
|
$mes13 = $this->preview(2027, 1, 1000);
|
|
|
|
|
|
|
|
- // 8% (base) x 75% (faixa 4-12) = 6%
|
|
|
|
|
- $this->assertSame(0.06, $mes12['royalties_bracket_percentage']);
|
|
|
|
|
- $this->assertSame(60.0, $mes12['royalties_bracket_value']);
|
|
|
|
|
|
|
+ $this->assertSame(0.75, $mes12['royalties_bracket_percentage']);
|
|
|
|
|
+ $this->assertSame(750.0, $mes12['royalties_bracket_value']);
|
|
|
|
|
|
|
|
- // 8% (base) x 100% (faixa 13-60) = 8%
|
|
|
|
|
- $this->assertSame(0.08, $mes13['royalties_bracket_percentage']);
|
|
|
|
|
- $this->assertSame(80.0, $mes13['royalties_bracket_value']);
|
|
|
|
|
|
|
+ $this->assertSame(1.0, $mes13['royalties_bracket_percentage']);
|
|
|
|
|
+ $this->assertSame(1000.0, $mes13['royalties_bracket_value']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Regra (md "Classificação de Habitantes"): o porte do município do contrato
|
|
* Regra (md "Classificação de Habitantes"): o porte do município do contrato
|
|
|
* define qual faixa de royalties se aplica.
|
|
* define qual faixa de royalties se aplica.
|
|
|
*
|
|
*
|
|
|
- * Cenário: Contrato em porte MGP (multiplicador 100% no mês 4) no lugar de GP (75%).
|
|
|
|
|
|
|
+ * Cenário: Contrato em porte MGP (100% no mês 4) no lugar de GP (75%).
|
|
|
*
|
|
*
|
|
|
- * Espera: percentual efetivo da faixa = 8% (base) x 100% = 0,08; Royalties = R$ 80,00
|
|
|
|
|
- * em vez dos R$ 60,00 do porte GP.
|
|
|
|
|
|
|
+ * Espera: percentual da faixa = 1,00; Royalties = R$ 1.000,00 em vez de R$ 750,00.
|
|
|
*/
|
|
*/
|
|
|
public function test_porte_maior_do_municipio_cobra_percentual_maior(): void
|
|
public function test_porte_maior_do_municipio_cobra_percentual_maior(): void
|
|
|
{
|
|
{
|
|
@@ -493,8 +489,8 @@ public function test_porte_maior_do_municipio_cobra_percentual_maior(): void
|
|
|
|
|
|
|
|
$result = $this->preview(2026, 4, 1000);
|
|
$result = $this->preview(2026, 4, 1000);
|
|
|
|
|
|
|
|
- $this->assertSame(0.08, $result['royalties_bracket_percentage']);
|
|
|
|
|
- $this->assertSame(80.0, $result['royalties_effective_value']);
|
|
|
|
|
|
|
+ $this->assertSame(1.0, $result['royalties_bracket_percentage']);
|
|
|
|
|
+ $this->assertSame(1000.0, $result['royalties_effective_value']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -524,11 +520,10 @@ public function test_erro_quando_nao_existe_faixa_nem_de_renovacao_para_o_mes():
|
|
|
* royalties passa a vir de is_renewal=true — o contrato "renova" e continua sendo
|
|
* royalties passa a vir de is_renewal=true — o contrato "renova" e continua sendo
|
|
|
* cobrado, em vez de estourar erro.
|
|
* cobrado, em vez de estourar erro.
|
|
|
*
|
|
*
|
|
|
- * Cenário: Faixa de renovação do porte GP cobrindo 1-60, com multiplicador 100%
|
|
|
|
|
- * (igual à última faixa normal). Competência cai no mês 61 do contrato.
|
|
|
|
|
|
|
+ * Cenário: Faixa de renovação do porte GP cobrindo 1-60, com 100% (igual à última
|
|
|
|
|
+ * faixa normal). Competência cai no mês 61 do contrato.
|
|
|
*
|
|
*
|
|
|
- * Espera: encontra a faixa de renovação; percentual efetivo = 8% (base) x 100% = 0,08;
|
|
|
|
|
- * Royalties = R$ 80,00 (empata com os 8% de faturamento de R$ 1.000,00, fica no fixo).
|
|
|
|
|
|
|
+ * Espera: encontra a faixa de renovação, percentual = 1,00, Royalties = R$ 1.000,00.
|
|
|
*/
|
|
*/
|
|
|
public function test_mes_61_usa_a_faixa_de_renovacao(): void
|
|
public function test_mes_61_usa_a_faixa_de_renovacao(): void
|
|
|
{
|
|
{
|
|
@@ -538,8 +533,8 @@ public function test_mes_61_usa_a_faixa_de_renovacao(): void
|
|
|
|
|
|
|
|
$result = $this->preview(2031, 1, 1000); // mês de contrato 61
|
|
$result = $this->preview(2031, 1, 1000); // mês de contrato 61
|
|
|
|
|
|
|
|
- $this->assertSame(0.08, $result['royalties_bracket_percentage']);
|
|
|
|
|
- $this->assertSame(80.0, $result['royalties_effective_value']);
|
|
|
|
|
|
|
+ $this->assertSame(1.0, $result['royalties_bracket_percentage']);
|
|
|
|
|
+ $this->assertSame(1000.0, $result['royalties_effective_value']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -549,9 +544,9 @@ public function test_mes_61_usa_a_faixa_de_renovacao(): void
|
|
|
* Cenário: Faixa de renovação com duas fatias (1-12 = 60%, 13-60 = 100%), simulando
|
|
* Cenário: Faixa de renovação com duas fatias (1-12 = 60%, 13-60 = 100%), simulando
|
|
|
* uma nova isenção/escalonamento dentro do próprio ciclo de renovação.
|
|
* uma nova isenção/escalonamento dentro do próprio ciclo de renovação.
|
|
|
*
|
|
*
|
|
|
- * Espera (percentual efetivo = 8% base x multiplicador da faixa de renovação):
|
|
|
|
|
- * - mês 65 do contrato (5º mês da renovação, multiplicador 60%) -> 8% x 60% = 0,048
|
|
|
|
|
- * - mês 120 do contrato (60º mês da renovação, multiplicador 100%) -> 8% x 100% = 0,08
|
|
|
|
|
|
|
+ * Espera:
|
|
|
|
|
+ * - mês 65 do contrato (5º mês da renovação) -> 60%
|
|
|
|
|
+ * - mês 120 do contrato (60º mês da renovação) -> 100%
|
|
|
*/
|
|
*/
|
|
|
public function test_mes_de_renovacao_e_calculado_dentro_do_ciclo_de_60_meses(): void
|
|
public function test_mes_de_renovacao_e_calculado_dentro_do_ciclo_de_60_meses(): void
|
|
|
{
|
|
{
|
|
@@ -563,18 +558,17 @@ public function test_mes_de_renovacao_e_calculado_dentro_do_ciclo_de_60_meses():
|
|
|
$mes65 = $this->preview(2031, 5, 1000); // mês de contrato 65 -> renovação mês 5
|
|
$mes65 = $this->preview(2031, 5, 1000); // mês de contrato 65 -> renovação mês 5
|
|
|
$mes120 = $this->preview(2035, 12, 1000); // mês de contrato 120 -> renovação mês 60
|
|
$mes120 = $this->preview(2035, 12, 1000); // mês de contrato 120 -> renovação mês 60
|
|
|
|
|
|
|
|
- $this->assertSame(0.048, $mes65['royalties_bracket_percentage']);
|
|
|
|
|
- $this->assertSame(0.08, $mes120['royalties_bracket_percentage']);
|
|
|
|
|
|
|
+ $this->assertSame(0.60, $mes65['royalties_bracket_percentage']);
|
|
|
|
|
+ $this->assertSame(1.00, $mes120['royalties_bracket_percentage']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Regra: Passado um segundo ciclo completo de 60 meses (mês 121 em diante), o
|
|
* Regra: Passado um segundo ciclo completo de 60 meses (mês 121 em diante), o
|
|
|
* ciclo de renovação recomeça do zero.
|
|
* ciclo de renovação recomeça do zero.
|
|
|
*
|
|
*
|
|
|
- * Cenário: Faixa de renovação única (1-60, multiplicador 100%); competência cai no mês 121.
|
|
|
|
|
|
|
+ * Cenário: Faixa de renovação única (1-60 = 100%); competência cai no mês 121.
|
|
|
*
|
|
*
|
|
|
- * Espera: continua encontrando a faixa de renovação (não estoura erro); percentual
|
|
|
|
|
- * efetivo = 8% (base) x 100% = 0,08.
|
|
|
|
|
|
|
+ * Espera: continua encontrando a faixa de renovação (não estoura erro), 100%.
|
|
|
*/
|
|
*/
|
|
|
public function test_segundo_ciclo_de_renovacao_reinicia_a_contagem(): void
|
|
public function test_segundo_ciclo_de_renovacao_reinicia_a_contagem(): void
|
|
|
{
|
|
{
|
|
@@ -584,7 +578,7 @@ public function test_segundo_ciclo_de_renovacao_reinicia_a_contagem(): void
|
|
|
|
|
|
|
|
$result = $this->preview(2036, 1, 1000);
|
|
$result = $this->preview(2036, 1, 1000);
|
|
|
|
|
|
|
|
- $this->assertSame(0.08, $result['royalties_bracket_percentage']);
|
|
|
|
|
|
|
+ $this->assertSame(1.0, $result['royalties_bracket_percentage']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------- valor da TBR
|
|
// --------------------------------------------------------------- valor da TBR
|
|
@@ -595,18 +589,17 @@ public function test_segundo_ciclo_de_renovacao_reinicia_a_contagem(): void
|
|
|
* sobre o valor gravado no contrato.
|
|
* sobre o valor gravado no contrato.
|
|
|
*
|
|
*
|
|
|
* Cenário: tbrs/2026 = R$ 1.000,00 (setUp) e contrato com tbr_fixed_value = R$ 2.000,00.
|
|
* Cenário: tbrs/2026 = R$ 1.000,00 (setUp) e contrato com tbr_fixed_value = R$ 2.000,00.
|
|
|
- * Faturamento baixo (R$ 500,00) para o fixo (R$ 60,00) prevalecer sobre o percentual.
|
|
|
|
|
*
|
|
*
|
|
|
- * Espera: TBR usada = R$ 1.000,00; Royalties = R$ 60,00; Manutenção = R$ 300,00.
|
|
|
|
|
|
|
+ * Espera: TBR usada = R$ 1.000,00; Royalties = R$ 750,00; Manutenção = R$ 300,00.
|
|
|
*/
|
|
*/
|
|
|
public function test_tbr_do_ano_configurada_pelo_admin_tem_prioridade_sobre_o_contrato(): void
|
|
public function test_tbr_do_ano_configurada_pelo_admin_tem_prioridade_sobre_o_contrato(): void
|
|
|
{
|
|
{
|
|
|
$this->makeContract(['tbr_fixed_value' => 2000.00]);
|
|
$this->makeContract(['tbr_fixed_value' => 2000.00]);
|
|
|
|
|
|
|
|
- $result = $this->preview(2026, 4, 500);
|
|
|
|
|
|
|
+ $result = $this->preview(2026, 4, 1000);
|
|
|
|
|
|
|
|
$this->assertSame(1000.0, $result['tbr_value']);
|
|
$this->assertSame(1000.0, $result['tbr_value']);
|
|
|
- $this->assertSame(60.0, $result['royalties_effective_value']);
|
|
|
|
|
|
|
+ $this->assertSame(750.0, $result['royalties_effective_value']);
|
|
|
$this->assertSame(300.0, $result['maintenance_effective_value']);
|
|
$this->assertSame(300.0, $result['maintenance_effective_value']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -677,6 +670,33 @@ public function test_percentuais_de_fnm_e_manutencao_vem_da_tabela_do_ano(): voi
|
|
|
$this->assertSame(350.0, $result['maintenance_effective_value']);
|
|
$this->assertSame(350.0, $result['maintenance_effective_value']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Regra: O campo "Royalties %" da tela de Configurações do TBR controla o
|
|
|
|
|
+ * percentual do lado FATURAMENTO da comparação "maior valor" — não o lado fixo
|
|
|
|
|
+ * (que continua vindo direto da faixa de porte, como confirmado pelo cliente).
|
|
|
|
|
+ * Antes, esse percentual era fixo em 8% no código; agora é configurável por ano.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Cenário: tbrs/2027 com royalties_percentage = 10% (em vez dos 8% padrão).
|
|
|
|
|
+ * Faturamento R$ 8.000,00: 10% = R$ 800,00, contra o fixo da faixa GP mês 13-60
|
|
|
|
|
+ * (100% x TBR R$ 1.000,00 = R$ 1.000,00) — o fixo ainda vence aqui.
|
|
|
|
|
+ * Faturamento R$ 12.000,00: 10% = R$ 1.200,00 > R$ 1.000,00 fixo — agora vence o
|
|
|
|
|
+ * faturamento, usando 10% (e não os 8% hardcoded antigos, que dariam R$ 960,00).
|
|
|
|
|
+ */
|
|
|
|
|
+ public function test_royalties_percentual_de_faturamento_vem_da_configuracao_do_tbr_do_ano(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $this->makeContract();
|
|
|
|
|
+ $this->makeTbrYear(2027, 1000.00, royalties: 0.10); // mês 13 (100% da faixa GP)
|
|
|
|
|
+
|
|
|
|
|
+ $fixoVence = $this->preview(2027, 1, 8000.00);
|
|
|
|
|
+ $this->assertSame(1000.0, $fixoVence['royalties_effective_value']);
|
|
|
|
|
+ $this->assertSame('tbr_fixo', $fixoVence['royalties_applied_criteria']);
|
|
|
|
|
+
|
|
|
|
|
+ $faturamentoVence = $this->preview(2027, 1, 12000.00);
|
|
|
|
|
+ $this->assertSame(1200.0, $faturamentoVence['royalties_effective_value']);
|
|
|
|
|
+ $this->assertSame(0.10, $faturamentoVence['royalties_effective_percentage']);
|
|
|
|
|
+ $this->assertSame('percentual_faturamento', $faturamentoVence['royalties_applied_criteria']);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// ------------------------------------------------ flags charge_roi / charge_fnm
|
|
// ------------------------------------------------ flags charge_roi / charge_fnm
|
|
|
|
|
|
|
|
/**
|
|
/**
|