TbrCalculationRoutesTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <?php
  2. namespace Tests\Feature\Financeiro;
  3. use App\Models\City;
  4. use App\Models\Country;
  5. use App\Models\FranchiseeContract;
  6. use App\Models\InhabitantClassification;
  7. use App\Models\MunicipalitySize;
  8. use App\Models\State;
  9. use App\Models\Tbr;
  10. use App\Models\Unit;
  11. use App\Models\User;
  12. use App\Models\UserType;
  13. use Illuminate\Foundation\Testing\RefreshDatabase;
  14. use Illuminate\Support\Facades\Bus;
  15. use Laravel\Sanctum\Sanctum;
  16. use Tests\TestCase;
  17. /**
  18. * Camada HTTP das rotas de cálculo de contrato (routes/authRoutes/franchisor_tbr.php).
  19. *
  20. * O frontend (src/api/tbr_calculation.js) já tem previewTbrCalculation(),
  21. * createTbrCalculation() e generateReceivable(id) prontos, mas as rotas
  22. * preview/store/{id}/generate-receivable não existiam — foram registradas
  23. * agora e são cobertas aqui junto com index/preview-batch/generate-batch.
  24. */
  25. class TbrCalculationRoutesTest extends TestCase
  26. {
  27. use RefreshDatabase;
  28. private Unit $unit;
  29. private MunicipalitySize $size;
  30. protected function setUp(): void
  31. {
  32. parent::setUp();
  33. Bus::fake();
  34. $country = Country::create(['name' => 'Brasil', 'code' => 'BR']);
  35. $state = State::create(['name' => 'Paraná', 'code' => 'PR', 'country_id' => $country->id]);
  36. $city = City::create(['name' => 'Maringá', 'country_id' => $country->id, 'state_id' => $state->id]);
  37. $this->unit = Unit::create([
  38. 'fantasy_name' => 'Unidade Teste',
  39. 'social_reason' => 'Unidade Teste LTDA',
  40. 'cnpj' => '00000000000191',
  41. 'street' => 'Rua Teste',
  42. 'neighborhood' => 'Centro',
  43. 'postal_code' => '87000000',
  44. 'city_id' => $city->id,
  45. 'state_id' => $state->id,
  46. 'email' => 'unidade@teste.com',
  47. 'name_responsible' => 'Responsável Teste',
  48. ]);
  49. $this->size = MunicipalitySize::create(['acronym' => 'GP', 'description' => 'De 100 mil a 200 mil habitantes']);
  50. foreach ([[1, 3, 0.0], [4, 12, 0.75], [13, 60, 1.00]] as [$start, $end, $percentage]) {
  51. InhabitantClassification::create([
  52. 'municipality_size_id' => $this->size->id,
  53. 'description' => "Faixa {$start}-{$end}",
  54. 'start' => $start,
  55. 'end' => $end,
  56. 'tbr_percentage' => $percentage,
  57. 'is_renewal' => false,
  58. ]);
  59. }
  60. Tbr::create([
  61. 'year' => 2026, 'tbr_value' => 1000.00,
  62. 'royalties_percentage' => 0.08, 'fnm_percentage' => 0.20, 'maintenance_percentage' => 0.30,
  63. ]);
  64. FranchiseeContract::create([
  65. 'unit_id' => $this->unit->id,
  66. 'protocol' => 1,
  67. 'name' => 'Contrato de Franquia',
  68. 'description' => 'Contrato de teste',
  69. 'start_date' => '2026-01-01',
  70. 'end_date' => '2031-01-01',
  71. 'signature_date' => '2026-01-01',
  72. 'validity_months' => 60,
  73. 'invoice_due_date' => 10,
  74. 'municipality_size_id' => $this->size->id,
  75. 'tbr_fixed_value' => 1000.00,
  76. ]);
  77. }
  78. /** Usuário ADMIN autenticado via Sanctum: hasPermission() libera qualquer scope. */
  79. private function actingAsAdmin(): User
  80. {
  81. $userType = UserType::create([
  82. 'slug' => 'ADMIN_TEST',
  83. 'label' => 'Admin Teste',
  84. 'is_system' => true,
  85. 'access_scope' => 'franchisor',
  86. 'system_key' => 'ADMIN',
  87. 'scope_key' => 'franchisor',
  88. ]);
  89. $user = User::create([
  90. 'name' => 'Admin Teste',
  91. 'email' => 'admin.teste@example.com',
  92. 'password' => bcrypt('password'),
  93. 'franchisor_user_type_id' => $userType->id,
  94. ]);
  95. Sanctum::actingAs($user, ['access']);
  96. return $user;
  97. }
  98. public function test_rotas_de_calculo_exigem_autenticacao(): void
  99. {
  100. $this->getJson('/api/tbr-calculation')->assertStatus(401);
  101. $this->postJson('/api/tbr-calculation/preview-batch', [])->assertStatus(401);
  102. $this->postJson('/api/tbr-calculation/generate-batch', [])->assertStatus(401);
  103. }
  104. public function test_index_lista_calculos_ja_gravados(): void
  105. {
  106. $this->actingAsAdmin();
  107. $this->postJson('/api/tbr-calculation/generate-batch', [
  108. 'reference_year' => 2026, 'reference_month' => 4,
  109. ])->assertStatus(201);
  110. $response = $this->getJson('/api/tbr-calculation');
  111. $response->assertOk();
  112. $this->assertCount(1, $response->json('payload'));
  113. }
  114. public function test_preview_batch_retorna_calculo_por_unidade_sem_gravar_nada(): void
  115. {
  116. $this->actingAsAdmin();
  117. $response = $this->postJson('/api/tbr-calculation/preview-batch', [
  118. 'reference_year' => 2026, 'reference_month' => 4,
  119. ]);
  120. $response->assertOk();
  121. $payload = $response->json('payload');
  122. $this->assertCount(1, $payload);
  123. $this->assertSame($this->unit->id, $payload[0]['unit_id']);
  124. $this->assertEquals(750.0, $payload[0]['royalties_effective_value']);
  125. $this->assertEquals(300.0, $payload[0]['maintenance_effective_value']);
  126. $this->assertDatabaseCount('tbr_calculations', 0);
  127. }
  128. public function test_preview_batch_valida_ano_e_mes_obrigatorios(): void
  129. {
  130. $this->actingAsAdmin();
  131. $this->postJson('/api/tbr-calculation/preview-batch', [])
  132. ->assertStatus(422)
  133. ->assertJsonValidationErrors(['reference_year', 'reference_month']);
  134. }
  135. public function test_generate_batch_gera_titulo_e_marca_calculo_como_gerado(): void
  136. {
  137. $this->actingAsAdmin();
  138. $response = $this->postJson('/api/tbr-calculation/generate-batch', [
  139. 'reference_year' => 2026, 'reference_month' => 4,
  140. ]);
  141. $response->assertStatus(201);
  142. $this->assertSame(1, $response->json('payload.generated_count'));
  143. $this->assertDatabaseHas('tbr_calculations', [
  144. 'unit_id' => $this->unit->id,
  145. 'contract_month_reference' => 4,
  146. 'receivable_generated' => true,
  147. ]);
  148. $this->assertDatabaseHas('franchisee_account_receives', [
  149. 'unit_id' => $this->unit->id,
  150. ]);
  151. Bus::assertDispatched(\App\Jobs\SyncFranchiseeChargeJob::class);
  152. }
  153. public function test_generate_batch_pula_unidade_que_ja_tem_titulo_no_mes(): void
  154. {
  155. $this->actingAsAdmin();
  156. $this->postJson('/api/tbr-calculation/generate-batch', [
  157. 'reference_year' => 2026, 'reference_month' => 4,
  158. ])->assertStatus(201);
  159. $response = $this->postJson('/api/tbr-calculation/generate-batch', [
  160. 'reference_year' => 2026, 'reference_month' => 4,
  161. ]);
  162. $response->assertStatus(201);
  163. $this->assertSame(0, $response->json('payload.generated_count'));
  164. $this->assertSame(1, $response->json('payload.skipped_count'));
  165. }
  166. public function test_preview_calcula_uma_unidade_sem_gravar_nada(): void
  167. {
  168. $this->actingAsAdmin();
  169. $response = $this->postJson('/api/tbr-calculation/preview', [
  170. 'unit_id' => $this->unit->id, 'reference_year' => 2026, 'reference_month' => 4,
  171. ]);
  172. $response->assertOk();
  173. $this->assertEquals(750.0, $response->json('payload.royalties_effective_value'));
  174. $this->assertDatabaseCount('tbr_calculations', 0);
  175. }
  176. public function test_store_grava_o_calculo_de_uma_unidade(): void
  177. {
  178. $this->actingAsAdmin();
  179. $response = $this->postJson('/api/tbr-calculation', [
  180. 'unit_id' => $this->unit->id, 'reference_year' => 2026, 'reference_month' => 4,
  181. ]);
  182. $response->assertStatus(201);
  183. $this->assertDatabaseHas('tbr_calculations', [
  184. 'unit_id' => $this->unit->id,
  185. 'contract_month_reference' => 4,
  186. 'receivable_generated' => false,
  187. ]);
  188. }
  189. public function test_show_retorna_um_calculo_gravado(): void
  190. {
  191. $this->actingAsAdmin();
  192. $id = $this->postJson('/api/tbr-calculation', [
  193. 'unit_id' => $this->unit->id, 'reference_year' => 2026, 'reference_month' => 4,
  194. ])->json('payload.id');
  195. $response = $this->getJson("/api/tbr-calculation/{$id}");
  196. $response->assertOk();
  197. $this->assertSame($id, $response->json('payload.id'));
  198. }
  199. public function test_generate_receivable_gera_titulo_para_um_calculo_especifico(): void
  200. {
  201. $this->actingAsAdmin();
  202. $id = $this->postJson('/api/tbr-calculation', [
  203. 'unit_id' => $this->unit->id, 'reference_year' => 2026, 'reference_month' => 4,
  204. ])->json('payload.id');
  205. $response = $this->postJson("/api/tbr-calculation/{$id}/generate-receivable");
  206. $response->assertStatus(201);
  207. $this->assertDatabaseHas('tbr_calculations', ['id' => $id, 'receivable_generated' => true]);
  208. $this->assertDatabaseHas('franchisee_account_receives', ['unit_id' => $this->unit->id]);
  209. Bus::assertDispatched(\App\Jobs\SyncFranchiseeChargeJob::class);
  210. }
  211. public function test_generate_receivable_bloqueia_segunda_geracao_para_o_mesmo_calculo(): void
  212. {
  213. $this->actingAsAdmin();
  214. $id = $this->postJson('/api/tbr-calculation', [
  215. 'unit_id' => $this->unit->id, 'reference_year' => 2026, 'reference_month' => 4,
  216. ])->json('payload.id');
  217. $this->postJson("/api/tbr-calculation/{$id}/generate-receivable")->assertStatus(201);
  218. $this->postJson("/api/tbr-calculation/{$id}/generate-receivable")
  219. ->assertStatus(422)
  220. ->assertJsonValidationErrors(['tbr_calculation_id']);
  221. }
  222. public function test_generate_batch_pode_ser_filtrado_por_unit_ids(): void
  223. {
  224. $this->actingAsAdmin();
  225. $outraUnidade = Unit::create([
  226. 'fantasy_name' => 'Outra Unidade', 'social_reason' => 'Outra Unidade LTDA',
  227. 'cnpj' => '00000000000272', 'street' => 'Rua Teste', 'neighborhood' => 'Centro',
  228. 'postal_code' => '87000000', 'city_id' => $this->unit->city_id, 'state_id' => $this->unit->state_id,
  229. 'email' => 'outra@teste.com', 'name_responsible' => 'Responsável Teste',
  230. ]);
  231. $response = $this->postJson('/api/tbr-calculation/generate-batch', [
  232. 'reference_year' => 2026, 'reference_month' => 4, 'unit_ids' => [$outraUnidade->id],
  233. ]);
  234. // Outra Unidade não tem contrato cadastrado: filtro por ela não gera nada.
  235. $response->assertStatus(201);
  236. $this->assertSame(0, $response->json('payload.generated_count'));
  237. }
  238. }