artisan('db:seed', ['--class' => 'FinancialWorkflowPermissionsSeeder', '--force' => true]); $this->artisan('db:seed', ['--class' => 'ChartOfAccountSeeder', '--force' => true]); } public function test_budget_belongs_to_created_by_user(): void { $user = User::factory()->create(); $budget = Budget::factory()->create(['created_by_user_id' => $user->id]); $this->assertInstanceOf(User::class, $budget->createdBy); $this->assertEquals($user->id, $budget->createdBy->id); } public function test_budget_belongs_to_approved_by_user(): void { $user = User::factory()->create(); $budget = Budget::factory()->create(['approved_by_user_id' => $user->id]); $this->assertInstanceOf(User::class, $budget->approvedBy); $this->assertEquals($user->id, $budget->approvedBy->id); } public function test_budget_has_many_budget_items(): void { $budget = Budget::factory()->create(); $account1 = ChartOfAccount::first(); $account2 = ChartOfAccount::skip(1)->first(); $item1 = BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $account1->id, ]); $item2 = BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $account2->id, ]); $this->assertCount(2, $budget->budgetItems); } public function test_is_draft_returns_true_when_status_is_draft(): void { $budget = Budget::factory()->create(['status' => Budget::STATUS_DRAFT]); $this->assertTrue($budget->isDraft()); } public function test_is_approved_returns_true_when_status_is_approved(): void { $budget = Budget::factory()->create(['status' => Budget::STATUS_APPROVED]); $this->assertTrue($budget->isApproved()); } public function test_is_active_returns_true_when_status_is_active(): void { $budget = Budget::factory()->create(['status' => Budget::STATUS_ACTIVE]); $this->assertTrue($budget->isActive()); } public function test_is_closed_returns_true_when_status_is_closed(): void { $budget = Budget::factory()->create(['status' => Budget::STATUS_CLOSED]); $this->assertTrue($budget->isClosed()); } public function test_can_be_edited_validates_correctly(): void { $draftBudget = Budget::factory()->create(['status' => Budget::STATUS_DRAFT]); $this->assertTrue($draftBudget->canBeEdited()); $submittedBudget = Budget::factory()->create(['status' => Budget::STATUS_SUBMITTED]); $this->assertTrue($submittedBudget->canBeEdited()); $activeBudget = Budget::factory()->create(['status' => Budget::STATUS_ACTIVE]); $this->assertFalse($activeBudget->canBeEdited()); $closedBudget = Budget::factory()->create(['status' => Budget::STATUS_CLOSED]); $this->assertFalse($closedBudget->canBeEdited()); } public function test_can_be_approved_validates_correctly(): void { $submittedBudget = Budget::factory()->create(['status' => Budget::STATUS_SUBMITTED]); $this->assertTrue($submittedBudget->canBeApproved()); $draftBudget = Budget::factory()->create(['status' => Budget::STATUS_DRAFT]); $this->assertFalse($draftBudget->canBeApproved()); $approvedBudget = Budget::factory()->create(['status' => Budget::STATUS_APPROVED]); $this->assertFalse($approvedBudget->canBeApproved()); } public function test_total_budgeted_income_calculation(): void { $budget = Budget::factory()->create(); $incomeAccount = ChartOfAccount::where('account_type', ChartOfAccount::TYPE_INCOME)->first(); if ($incomeAccount) { BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $incomeAccount->id, 'budgeted_amount' => 10000, ]); BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $incomeAccount->id, 'budgeted_amount' => 5000, ]); $this->assertEquals(15000, $budget->total_budgeted_income); } else { $this->assertTrue(true, 'No income account available for test'); } } public function test_total_budgeted_expense_calculation(): void { $budget = Budget::factory()->create(); $expenseAccount = ChartOfAccount::where('account_type', ChartOfAccount::TYPE_EXPENSE)->first(); if ($expenseAccount) { BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $expenseAccount->id, 'budgeted_amount' => 8000, ]); BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $expenseAccount->id, 'budgeted_amount' => 3000, ]); $this->assertEquals(11000, $budget->total_budgeted_expense); } else { $this->assertTrue(true, 'No expense account available for test'); } } public function test_total_actual_income_calculation(): void { $budget = Budget::factory()->create(); $incomeAccount = ChartOfAccount::where('account_type', ChartOfAccount::TYPE_INCOME)->first(); if ($incomeAccount) { BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $incomeAccount->id, 'budgeted_amount' => 10000, 'actual_amount' => 12000, ]); BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $incomeAccount->id, 'budgeted_amount' => 5000, 'actual_amount' => 4500, ]); $this->assertEquals(16500, $budget->total_actual_income); } else { $this->assertTrue(true, 'No income account available for test'); } } public function test_total_actual_expense_calculation(): void { $budget = Budget::factory()->create(); $expenseAccount = ChartOfAccount::where('account_type', ChartOfAccount::TYPE_EXPENSE)->first(); if ($expenseAccount) { BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $expenseAccount->id, 'budgeted_amount' => 8000, 'actual_amount' => 7500, ]); BudgetItem::factory()->create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $expenseAccount->id, 'budgeted_amount' => 3000, 'actual_amount' => 3200, ]); $this->assertEquals(10700, $budget->total_actual_expense); } else { $this->assertTrue(true, 'No expense account available for test'); } } public function test_budget_item_variance_calculation(): void { $account = ChartOfAccount::first(); $budgetItem = BudgetItem::factory()->create([ 'chart_of_account_id' => $account->id, 'budgeted_amount' => 10000, 'actual_amount' => 8500, ]); $this->assertEquals(-1500, $budgetItem->variance); } public function test_budget_item_variance_percentage_calculation(): void { $account = ChartOfAccount::first(); $budgetItem = BudgetItem::factory()->create([ 'chart_of_account_id' => $account->id, 'budgeted_amount' => 10000, 'actual_amount' => 8500, ]); $this->assertEquals(-15.0, $budgetItem->variance_percentage); } public function test_budget_item_remaining_budget_calculation(): void { $account = ChartOfAccount::first(); $budgetItem = BudgetItem::factory()->create([ 'chart_of_account_id' => $account->id, 'budgeted_amount' => 10000, 'actual_amount' => 6000, ]); $this->assertEquals(4000, $budgetItem->remaining_budget); } public function test_budget_item_is_over_budget_detection(): void { $account = ChartOfAccount::first(); $overBudgetItem = BudgetItem::factory()->create([ 'chart_of_account_id' => $account->id, 'budgeted_amount' => 10000, 'actual_amount' => 12000, ]); $this->assertTrue($overBudgetItem->isOverBudget()); $underBudgetItem = BudgetItem::factory()->create([ 'chart_of_account_id' => $account->id, 'budgeted_amount' => 10000, 'actual_amount' => 8000, ]); $this->assertFalse($underBudgetItem->isOverBudget()); } public function test_budget_item_utilization_percentage_calculation(): void { $account = ChartOfAccount::first(); $budgetItem = BudgetItem::factory()->create([ 'chart_of_account_id' => $account->id, 'budgeted_amount' => 10000, 'actual_amount' => 7500, ]); $this->assertEquals(75.0, $budgetItem->utilization_percentage); } public function test_budget_workflow_sequence(): void { $budget = Budget::factory()->create(['status' => Budget::STATUS_DRAFT]); // Draft can be edited $this->assertTrue($budget->canBeEdited()); $this->assertFalse($budget->canBeApproved()); // Submitted can be edited and approved $budget->status = Budget::STATUS_SUBMITTED; $this->assertTrue($budget->canBeEdited()); $this->assertTrue($budget->canBeApproved()); // Approved cannot be edited $budget->status = Budget::STATUS_APPROVED; $this->assertFalse($budget->canBeEdited()); $this->assertFalse($budget->canBeApproved()); // Active cannot be edited $budget->status = Budget::STATUS_ACTIVE; $this->assertFalse($budget->canBeEdited()); // Closed cannot be edited $budget->status = Budget::STATUS_CLOSED; $this->assertFalse($budget->canBeEdited()); } }