| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- <?php
- namespace Tests\Unit\Financeiro;
- use App\Models\City;
- use App\Models\Country;
- use App\Models\FranchiseeAccountReceive;
- use App\Models\State;
- use App\Models\Student;
- use App\Models\Unit;
- use App\Services\StudentContractService;
- use App\Services\TbrCalculationService;
- use App\Services\UnitFinancialDashboardService;
- use Illuminate\Foundation\Testing\RefreshDatabase;
- use Tests\TestCase;
- /**
- * Ponta a ponta: contrato de aluno assinado -> parcelas geradas -> faturamento
- * da unidade. Cobre o pedido de "ver se o faturamento da unidade está sendo
- * cobrado corretamente" usando o fluxo real (StudentContractService), não
- * inserindo parcela direto no banco como os outros testes de TBR fazem.
- */
- class StudentBillingRevenueTest extends TestCase
- {
- use RefreshDatabase;
- private Unit $unit;
- private StudentContractService $contractService;
- private TbrCalculationService $tbrService;
- protected function setUp(): void
- {
- parent::setUp();
- $this->unit = $this->makeUnit();
- $this->contractService = new StudentContractService();
- $this->tbrService = new TbrCalculationService();
- }
- private function makeUnit(): Unit
- {
- $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]);
- return 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',
- ]);
- }
- /**
- * Regra: O faturamento de um mês soma as parcelas de aluno com vencimento
- * naquele mês — incluindo a entrada, quando cai no mesmo mês, e mesmo entre
- * alunos/contratos diferentes.
- *
- * Cenário: Aluno A com pacote em 3x de R$ 100 (venc. dia 10) e entrada de
- * R$ 500 vencendo no mesmo mês da 1ª parcela; Aluno B com matrícula de R$ 150
- * vencendo no mesmo mês.
- *
- * Espera: Faturamento de 08/2026 = 100 (1ª parcela do pacote) + 500 (entrada) + 150 (matrícula) = 750.
- * Faturamento de 09/2026 = 100 (2ª parcela do pacote, sozinha).
- */
- public function test_faturamento_do_mes_soma_parcelas_de_contratos_diferentes_incluindo_entrada(): void
- {
- $alunoA = Student::create(['name' => 'Aluno A', 'unit_id' => $this->unit->id, 'status' => 'active']);
- $this->contractService->create([
- 'student_id' => $alunoA->id,
- 'unit_id' => $this->unit->id,
- 'recurring_day' => 10,
- 'down_payment_value' => 500.00,
- 'down_payment_date' => '2026-08-10',
- 'package_value' => 300.00,
- 'package_installments' => 3,
- 'package_due_date' => '2026-08-10',
- ]);
- $alunoB = Student::create(['name' => 'Aluno B', 'unit_id' => $this->unit->id, 'status' => 'active']);
- $this->contractService->create([
- 'student_id' => $alunoB->id,
- 'unit_id' => $this->unit->id,
- 'tax_register' => 150.00,
- 'installments' => 1,
- 'enrollment_due_date' => '2026-08-20',
- ]);
- $revenueAgosto = $this->tbrService->resolveRevenue($this->unit->id, 2026, 8);
- $revenueSetembro = $this->tbrService->resolveRevenue($this->unit->id, 2026, 9);
- $this->assertSame(750.0, $revenueAgosto);
- $this->assertSame(100.0, $revenueSetembro);
- }
- /**
- * Regra: A entrada com vencimento num mês diferente das parcelas normais
- * entra no faturamento do MÊS EM QUE ELA VENCE, não no mês da 1ª parcela.
- */
- public function test_entrada_com_vencimento_em_mes_diferente_entra_no_faturamento_do_seu_proprio_mes(): void
- {
- $aluno = Student::create(['name' => 'Aluno Teste', 'unit_id' => $this->unit->id, 'status' => 'active']);
- $this->contractService->create([
- 'student_id' => $aluno->id,
- 'unit_id' => $this->unit->id,
- 'recurring_day' => 10,
- 'down_payment_value' => 500.00,
- 'down_payment_date' => '2026-07-25', // entrada assinada antes, vencendo em julho
- 'package_value' => 300.00,
- 'package_installments' => 3,
- 'package_due_date' => '2026-08-10', // parcelas normais começam em agosto
- ]);
- $this->assertSame(500.0, $this->tbrService->resolveRevenue($this->unit->id, 2026, 7));
- $this->assertSame(100.0, $this->tbrService->resolveRevenue($this->unit->id, 2026, 8));
- }
- /**
- * Regra: O dashboard financeiro da unidade (UnitFinancialDashboardService)
- * e o faturamento usado pelo cálculo de royalties (TbrCalculationService)
- * precisam bater — inclusive contas que a franqueadora vinculou à unidade
- * (origin=manual_unit) sem cobrança Asaas nenhuma. Antes dessa correção o
- * dashboard ignorava essa fonte; o cliente reclamava do número não bater.
- */
- public function test_dashboard_da_unidade_soma_contas_vinculadas_pela_franqueadora_mesmo_sem_asaas(): void
- {
- FranchiseeAccountReceive::create([
- 'unit_id' => $this->unit->id,
- 'origin' => 'manual_unit',
- 'history' => 'Conta vinculada pela franqueadora',
- 'value' => 1000,
- 'due_date' => now()->toDateString(),
- 'status' => 'pending',
- ]);
- $tbrRevenueBetween = $this->tbrService->resolveRevenueBetween(
- [$this->unit->id],
- now()->startOfMonth(),
- now()->endOfMonth(),
- );
- $dashboard = (new UnitFinancialDashboardService())->overview($this->unit->id);
- $this->assertSame(1000.0, $tbrRevenueBetween);
- $this->assertSame(1000.0, $dashboard['receivable_pending']);
- $this->assertSame(1, $dashboard['receivable_pending_count']);
- }
- /**
- * Regra: Conta vinculada pela franqueadora paga dentro do mês entra em
- * "recebido no mês"; vencida (não paga) entra em "vencido".
- */
- public function test_dashboard_conta_recebido_e_vencido_das_contas_vinculadas_pela_franqueadora(): void
- {
- FranchiseeAccountReceive::create([
- 'unit_id' => $this->unit->id,
- 'origin' => 'manual_unit',
- 'history' => 'Paga no mês',
- 'value' => 200,
- 'paid_value' => 200,
- 'due_date' => now()->startOfMonth()->toDateString(),
- 'payment_date' => now()->toDateString(),
- 'status' => 'paid',
- ]);
- FranchiseeAccountReceive::create([
- 'unit_id' => $this->unit->id,
- 'origin' => 'manual_unit',
- 'history' => 'Vencida',
- 'value' => 300,
- 'due_date' => now()->subDays(5)->toDateString(),
- 'status' => 'pending',
- ]);
- $dashboard = (new UnitFinancialDashboardService())->overview($this->unit->id);
- $this->assertSame(200.0, $dashboard['received_month']);
- $this->assertSame(300.0, $dashboard['overdue_receivable']);
- }
- }
|