unit = $this->makeUnit(); $this->service = new UnitReceivableService(); $this->contractService = new StudentContractService(); } private function makeUnit(string $name = 'Unidade Teste', string $cnpj = '00000000000191'): 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' => $name, 'social_reason' => $name.' LTDA', 'cnpj' => $cnpj, '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', ]); } private function makeInstallmentViaContract(int $unitId, array $overrides = []): StudentContractInstallment { $student = Student::create(['name' => 'Aluno Teste', 'unit_id' => $unitId, 'status' => 'active']); $contract = $this->contractService->create(array_merge([ 'student_id' => $student->id, 'unit_id' => $unitId, 'package_value' => 300.00, 'package_installments' => 3, 'package_due_date' => '2026-08-10', ], $overrides)); return StudentContractInstallment::where('student_contract_id', $contract->id) ->orderBy('installment_number')->first(); } /** * Regra: A listagem de "contas a receber" da unidade é exatamente a tabela * de parcelas de aluno, filtrada por unidade, ordenada por vencimento. */ public function test_list_retorna_as_parcelas_da_unidade_ordenadas_por_vencimento(): void { $this->makeInstallmentViaContract($this->unit->id); $outraUnidade = $this->makeUnit('Outra Unidade', '00000000000272'); $this->makeInstallmentViaContract($outraUnidade->id); $result = $this->service->list($this->unit->id); $this->assertCount(3, $result); // as 3 parcelas do pacote da unidade de teste $this->assertTrue($result->every(fn ($i) => $i->unit_id === $this->unit->id)); } public function test_list_filtra_por_status_e_por_tipo(): void { $installment = $this->makeInstallmentViaContract($this->unit->id); $installment->update(['status' => 'paid', 'paid_value' => 100, 'payment_date' => '2026-08-10']); $pending = $this->service->list($this->unit->id, ['status' => 'pending']); $paid = $this->service->list($this->unit->id, ['status' => 'paid']); $this->assertCount(2, $pending); $this->assertCount(1, $paid); $byType = $this->service->list($this->unit->id, ['type' => 'package']); $this->assertCount(3, $byType); } /** * Regra: A baixa manual, sem informar paid_value, cobra o valor da parcela * menos desconto mais multa. */ public function test_settle_calcula_paid_value_padrao_a_partir_de_desconto_e_multa(): void { $installment = $this->makeInstallmentViaContract($this->unit->id); $settled = $this->service->settle($installment, ['discount' => 10, 'fine' => 5]); $this->assertSame('paid', $settled->status); $this->assertEquals(95.00, $settled->paid_value); // 100 - 10 + 5 } public function test_settle_bloqueia_parcela_ja_paga(): void { $installment = $this->makeInstallmentViaContract($this->unit->id); $installment->update(['status' => 'paid', 'paid_value' => 100, 'payment_date' => '2026-08-10']); $this->expectException(ValidationException::class); $this->expectExceptionMessage('já está paga'); $this->service->settle($installment, []); } public function test_settle_bloqueia_parcela_cancelada(): void { $installment = $this->makeInstallmentViaContract($this->unit->id); $installment->update(['status' => 'cancelled']); $this->expectException(ValidationException::class); $this->expectExceptionMessage('cancelada'); $this->service->settle($installment, []); } /** * Regra: Uma parcela paga nunca reabre — a baixa manual é definitiva * (estorno de verdade não é suportado por este fluxo). */ public function test_reopen_sempre_bloqueia(): void { $installment = $this->makeInstallmentViaContract($this->unit->id); $installment->update(['status' => 'paid', 'paid_value' => 100, 'payment_date' => '2026-08-10']); $this->expectException(ValidationException::class); $this->service->reopen($installment); } public function test_find_for_unit_nao_retorna_parcela_de_outra_unidade(): void { $installment = $this->makeInstallmentViaContract($this->unit->id); $outraUnidade = $this->makeUnit('Outra Unidade', '00000000000272'); $this->assertNull($this->service->findForUnit($installment->id, $outraUnidade->id)); $this->assertNotNull($this->service->findForUnit($installment->id, $this->unit->id)); } }