|
|
@@ -0,0 +1,225 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace Tests\Feature\Financeiro;
|
|
|
+
|
|
|
+use App\Models\City;
|
|
|
+use App\Models\Country;
|
|
|
+use App\Models\FranchiseeContract;
|
|
|
+use App\Models\InhabitantClassification;
|
|
|
+use App\Models\MunicipalitySize;
|
|
|
+use App\Models\State;
|
|
|
+use App\Models\Tbr;
|
|
|
+use App\Models\Unit;
|
|
|
+use App\Models\User;
|
|
|
+use App\Models\UserType;
|
|
|
+use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
+use Illuminate\Support\Facades\Bus;
|
|
|
+use Laravel\Sanctum\Sanctum;
|
|
|
+use Tests\TestCase;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Camada HTTP das rotas de cálculo de contrato (routes/authRoutes/franchisor_tbr.php).
|
|
|
+ *
|
|
|
+ * Só GET /tbr-calculation, POST /tbr-calculation/preview-batch e
|
|
|
+ * POST /tbr-calculation/generate-batch estão registradas hoje — os métodos
|
|
|
+ * preview/store/show/generateReceivable existem no TbrCalculationController mas
|
|
|
+ * não têm rota, então ficam fora daqui.
|
|
|
+ */
|
|
|
+class TbrCalculationRoutesTest extends TestCase
|
|
|
+{
|
|
|
+ use RefreshDatabase;
|
|
|
+
|
|
|
+ private Unit $unit;
|
|
|
+
|
|
|
+ private MunicipalitySize $size;
|
|
|
+
|
|
|
+ protected function setUp(): void
|
|
|
+ {
|
|
|
+ parent::setUp();
|
|
|
+
|
|
|
+ Bus::fake();
|
|
|
+
|
|
|
+ $country = Country::create(['name' => 'Brasil', 'code' => 'BR']);
|
|
|
+ $state = State::create(['name' => 'Paraná', 'code' => 'PR', 'country_id' => $country->id]);
|
|
|
+ $city = City::create(['name' => 'Maringá', 'country_id' => $country->id, 'state_id' => $state->id]);
|
|
|
+
|
|
|
+ $this->unit = Unit::create([
|
|
|
+ 'fantasy_name' => 'Unidade Teste',
|
|
|
+ 'social_reason' => 'Unidade Teste LTDA',
|
|
|
+ 'cnpj' => '00000000000191',
|
|
|
+ 'street' => 'Rua Teste',
|
|
|
+ 'neighborhood' => 'Centro',
|
|
|
+ 'postal_code' => '87000000',
|
|
|
+ 'city_id' => $city->id,
|
|
|
+ 'state_id' => $state->id,
|
|
|
+ 'email' => 'unidade@teste.com',
|
|
|
+ 'name_responsible' => 'Responsável Teste',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $this->size = MunicipalitySize::create(['acronym' => 'GP', 'description' => 'De 100 mil a 200 mil habitantes']);
|
|
|
+
|
|
|
+ foreach ([[1, 3, 0.0], [4, 12, 0.75], [13, 60, 1.00]] as [$start, $end, $percentage]) {
|
|
|
+ InhabitantClassification::create([
|
|
|
+ 'municipality_size_id' => $this->size->id,
|
|
|
+ 'description' => "Faixa {$start}-{$end}",
|
|
|
+ 'start' => $start,
|
|
|
+ 'end' => $end,
|
|
|
+ 'tbr_percentage' => $percentage,
|
|
|
+ 'is_renewal' => false,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ Tbr::create([
|
|
|
+ 'year' => 2026, 'tbr_value' => 1000.00,
|
|
|
+ 'royalties_percentage' => 0.08, 'fnm_percentage' => 0.20, 'maintenance_percentage' => 0.30,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ FranchiseeContract::create([
|
|
|
+ 'unit_id' => $this->unit->id,
|
|
|
+ 'protocol' => 1,
|
|
|
+ 'name' => 'Contrato de Franquia',
|
|
|
+ 'description' => 'Contrato de teste',
|
|
|
+ 'start_date' => '2026-01-01',
|
|
|
+ 'end_date' => '2031-01-01',
|
|
|
+ 'signature_date' => '2026-01-01',
|
|
|
+ 'validity_months' => 60,
|
|
|
+ 'invoice_due_date' => 10,
|
|
|
+ 'municipality_size_id' => $this->size->id,
|
|
|
+ 'tbr_fixed_value' => 1000.00,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Usuário ADMIN autenticado via Sanctum: hasPermission() libera qualquer scope. */
|
|
|
+ private function actingAsAdmin(): User
|
|
|
+ {
|
|
|
+ $userType = UserType::create([
|
|
|
+ 'slug' => 'ADMIN_TEST',
|
|
|
+ 'label' => 'Admin Teste',
|
|
|
+ 'is_system' => true,
|
|
|
+ 'access_scope' => 'franchisor',
|
|
|
+ 'system_key' => 'ADMIN',
|
|
|
+ 'scope_key' => 'franchisor',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $user = User::create([
|
|
|
+ 'name' => 'Admin Teste',
|
|
|
+ 'email' => 'admin.teste@example.com',
|
|
|
+ 'password' => bcrypt('password'),
|
|
|
+ 'franchisor_user_type_id' => $userType->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ Sanctum::actingAs($user, ['access']);
|
|
|
+
|
|
|
+ return $user;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_rotas_de_calculo_exigem_autenticacao(): void
|
|
|
+ {
|
|
|
+ $this->getJson('/api/tbr-calculation')->assertStatus(401);
|
|
|
+ $this->postJson('/api/tbr-calculation/preview-batch', [])->assertStatus(401);
|
|
|
+ $this->postJson('/api/tbr-calculation/generate-batch', [])->assertStatus(401);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_index_lista_calculos_ja_gravados(): void
|
|
|
+ {
|
|
|
+ $this->actingAsAdmin();
|
|
|
+
|
|
|
+ $this->postJson('/api/tbr-calculation/generate-batch', [
|
|
|
+ 'reference_year' => 2026, 'reference_month' => 4,
|
|
|
+ ])->assertStatus(201);
|
|
|
+
|
|
|
+ $response = $this->getJson('/api/tbr-calculation');
|
|
|
+
|
|
|
+ $response->assertOk();
|
|
|
+ $this->assertCount(1, $response->json('payload'));
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_preview_batch_retorna_calculo_por_unidade_sem_gravar_nada(): void
|
|
|
+ {
|
|
|
+ $this->actingAsAdmin();
|
|
|
+
|
|
|
+ $response = $this->postJson('/api/tbr-calculation/preview-batch', [
|
|
|
+ 'reference_year' => 2026, 'reference_month' => 4,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertOk();
|
|
|
+ $payload = $response->json('payload');
|
|
|
+
|
|
|
+ $this->assertCount(1, $payload);
|
|
|
+ $this->assertSame($this->unit->id, $payload[0]['unit_id']);
|
|
|
+ $this->assertEquals(750.0, $payload[0]['royalties_effective_value']);
|
|
|
+ $this->assertEquals(300.0, $payload[0]['maintenance_effective_value']);
|
|
|
+
|
|
|
+ $this->assertDatabaseCount('tbr_calculations', 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_preview_batch_valida_ano_e_mes_obrigatorios(): void
|
|
|
+ {
|
|
|
+ $this->actingAsAdmin();
|
|
|
+
|
|
|
+ $this->postJson('/api/tbr-calculation/preview-batch', [])
|
|
|
+ ->assertStatus(422)
|
|
|
+ ->assertJsonValidationErrors(['reference_year', 'reference_month']);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_generate_batch_gera_titulo_e_marca_calculo_como_gerado(): void
|
|
|
+ {
|
|
|
+ $this->actingAsAdmin();
|
|
|
+
|
|
|
+ $response = $this->postJson('/api/tbr-calculation/generate-batch', [
|
|
|
+ 'reference_year' => 2026, 'reference_month' => 4,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertStatus(201);
|
|
|
+ $this->assertSame(1, $response->json('payload.generated_count'));
|
|
|
+
|
|
|
+ $this->assertDatabaseHas('tbr_calculations', [
|
|
|
+ 'unit_id' => $this->unit->id,
|
|
|
+ 'contract_month_reference' => 4,
|
|
|
+ 'receivable_generated' => true,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $this->assertDatabaseHas('franchisee_account_receives', [
|
|
|
+ 'unit_id' => $this->unit->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ Bus::assertDispatched(\App\Jobs\SyncFranchiseeChargeJob::class);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_generate_batch_pula_unidade_que_ja_tem_titulo_no_mes(): void
|
|
|
+ {
|
|
|
+ $this->actingAsAdmin();
|
|
|
+
|
|
|
+ $this->postJson('/api/tbr-calculation/generate-batch', [
|
|
|
+ 'reference_year' => 2026, 'reference_month' => 4,
|
|
|
+ ])->assertStatus(201);
|
|
|
+
|
|
|
+ $response = $this->postJson('/api/tbr-calculation/generate-batch', [
|
|
|
+ 'reference_year' => 2026, 'reference_month' => 4,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertStatus(201);
|
|
|
+ $this->assertSame(0, $response->json('payload.generated_count'));
|
|
|
+ $this->assertSame(1, $response->json('payload.skipped_count'));
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_generate_batch_pode_ser_filtrado_por_unit_ids(): void
|
|
|
+ {
|
|
|
+ $this->actingAsAdmin();
|
|
|
+
|
|
|
+ $outraUnidade = Unit::create([
|
|
|
+ 'fantasy_name' => 'Outra Unidade', 'social_reason' => 'Outra Unidade LTDA',
|
|
|
+ 'cnpj' => '00000000000272', 'street' => 'Rua Teste', 'neighborhood' => 'Centro',
|
|
|
+ 'postal_code' => '87000000', 'city_id' => $this->unit->city_id, 'state_id' => $this->unit->state_id,
|
|
|
+ 'email' => 'outra@teste.com', 'name_responsible' => 'Responsável Teste',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response = $this->postJson('/api/tbr-calculation/generate-batch', [
|
|
|
+ 'reference_year' => 2026, 'reference_month' => 4, 'unit_ids' => [$outraUnidade->id],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // Outra Unidade não tem contrato cadastrado: filtro por ela não gera nada.
|
|
|
+ $response->assertStatus(201);
|
|
|
+ $this->assertSame(0, $response->json('payload.generated_count'));
|
|
|
+ }
|
|
|
+}
|