command->info('🌱 Seeding financial workflow test data...'); // Create or get test users $this->createTestUsers(); // Seed finance documents at various stages $this->seedFinanceDocuments(); // Seed payment orders $this->seedPaymentOrders(); // Seed cashier ledger entries $this->seedCashierLedgerEntries(); // Seed bank reconciliations $this->seedBankReconciliations(); $this->command->info('✅ Financial workflow test data seeded successfully!'); } /** * Create test users with appropriate roles */ protected function createTestUsers(): void { $this->command->info('Creating test users...'); $this->requester = User::firstOrCreate( ['email' => 'requester@test.com'], [ 'name' => 'Test Requester', 'password' => Hash::make('password'), ] ); $this->requester->assignRole('finance_requester'); $this->cashier = User::firstOrCreate( ['email' => 'cashier@test.com'], [ 'name' => 'Test Cashier', 'password' => Hash::make('password'), ] ); $this->cashier->assignRole('finance_cashier'); $this->accountant = User::firstOrCreate( ['email' => 'accountant@test.com'], [ 'name' => 'Test Accountant', 'password' => Hash::make('password'), ] ); $this->accountant->assignRole('finance_accountant'); $this->chair = User::firstOrCreate( ['email' => 'chair@test.com'], [ 'name' => 'Test Chair', 'password' => Hash::make('password'), ] ); $this->chair->assignRole('finance_chair'); $this->boardMember = User::firstOrCreate( ['email' => 'board@test.com'], [ 'name' => 'Test Board Member', 'password' => Hash::make('password'), ] ); $this->boardMember->assignRole('finance_board_member'); $this->command->info('✓ Test users created'); } /** * Seed finance documents at various stages of the workflow */ protected function seedFinanceDocuments(): void { $this->command->info('Seeding finance documents...'); // Pending documents (Stage 1) FinanceDocument::factory() ->count(3) ->smallAmount() ->pending() ->create(['submitted_by_id' => $this->requester->id]); FinanceDocument::factory() ->count(2) ->mediumAmount() ->pending() ->create(['submitted_by_id' => $this->requester->id]); // Approved by cashier (Stage 1) FinanceDocument::factory() ->count(2) ->smallAmount() ->approvedByCashier() ->create([ 'submitted_by_id' => $this->requester->id, 'cashier_approved_by_id' => $this->cashier->id, ]); // Approved by accountant - small amounts (Ready for payment) FinanceDocument::factory() ->count(3) ->smallAmount() ->approvedByAccountant() ->create([ 'submitted_by_id' => $this->requester->id, 'cashier_approved_by_id' => $this->cashier->id, 'accountant_approved_by_id' => $this->accountant->id, ]); // Approved by chair - medium amounts (Ready for payment) FinanceDocument::factory() ->count(2) ->mediumAmount() ->approvedByChair() ->create([ 'submitted_by_id' => $this->requester->id, 'cashier_approved_by_id' => $this->cashier->id, 'accountant_approved_by_id' => $this->accountant->id, 'chair_approved_by_id' => $this->chair->id, ]); // Large amount with board approval (Ready for payment) FinanceDocument::factory() ->count(1) ->largeAmount() ->approvedByChair() ->create([ 'submitted_by_id' => $this->requester->id, 'cashier_approved_by_id' => $this->cashier->id, 'accountant_approved_by_id' => $this->accountant->id, 'chair_approved_by_id' => $this->chair->id, 'board_meeting_approved_at' => now(), 'board_meeting_approved_by_id' => $this->boardMember->id, ]); // Completed workflow FinanceDocument::factory() ->count(5) ->smallAmount() ->approvedByAccountant() ->paymentExecuted() ->create([ 'submitted_by_id' => $this->requester->id, 'cashier_approved_by_id' => $this->cashier->id, 'accountant_approved_by_id' => $this->accountant->id, 'cashier_recorded_at' => now(), ]); // Rejected documents FinanceDocument::factory() ->count(2) ->rejected() ->create([ 'submitted_by_id' => $this->requester->id, ]); $this->command->info('✓ Finance documents seeded'); } /** * Seed payment orders */ protected function seedPaymentOrders(): void { $this->command->info('Seeding payment orders...'); // Get approved documents without payment orders $approvedDocs = FinanceDocument::whereIn('status', [ FinanceDocument::STATUS_APPROVED_ACCOUNTANT, FinanceDocument::STATUS_APPROVED_CHAIR, ]) ->whereNull('payment_order_created_at') ->limit(5) ->get(); foreach ($approvedDocs as $doc) { // Pending verification PaymentOrder::factory() ->pendingVerification() ->create([ 'finance_document_id' => $doc->id, 'payment_amount' => $doc->amount, 'created_by_accountant_id' => $this->accountant->id, ]); $doc->update([ 'payment_order_created_at' => now(), 'payment_order_created_by_id' => $this->accountant->id, ]); } // Verified payment orders PaymentOrder::factory() ->count(3) ->verified() ->create([ 'created_by_accountant_id' => $this->accountant->id, 'verified_by_cashier_id' => $this->cashier->id, ]); // Executed payment orders PaymentOrder::factory() ->count(5) ->executed() ->create([ 'created_by_accountant_id' => $this->accountant->id, 'verified_by_cashier_id' => $this->cashier->id, 'executed_by_cashier_id' => $this->cashier->id, ]); $this->command->info('✓ Payment orders seeded'); } /** * Seed cashier ledger entries with running balances */ protected function seedCashierLedgerEntries(): void { $this->command->info('Seeding cashier ledger entries...'); $bankAccounts = [ 'First Bank - 1234567890', 'Second Bank - 0987654321', 'Petty Cash', ]; foreach ($bankAccounts as $account) { $currentBalance = 100000; // Starting balance // Create 10 entries for each account for ($i = 0; $i < 10; $i++) { $isReceipt = $i % 3 !== 0; // 2/3 receipts, 1/3 payments $amount = rand(1000, 10000); $entry = CashierLedgerEntry::create([ 'entry_type' => $isReceipt ? 'receipt' : 'payment', 'entry_date' => now()->subDays(rand(1, 30)), 'amount' => $amount, 'payment_method' => $account === 'Petty Cash' ? 'cash' : 'bank_transfer', 'bank_account' => $account, 'balance_before' => $currentBalance, 'balance_after' => $isReceipt ? $currentBalance + $amount : $currentBalance - $amount, 'receipt_number' => $isReceipt ? 'RCP' . str_pad($i + 1, 6, '0', STR_PAD_LEFT) : null, 'notes' => $isReceipt ? 'Test receipt entry' : 'Test payment entry', 'recorded_by_cashier_id' => $this->cashier->id, 'recorded_at' => now()->subDays(rand(1, 30)), ]); $currentBalance = $entry->balance_after; } } $this->command->info('✓ Cashier ledger entries seeded'); } /** * Seed bank reconciliations */ protected function seedBankReconciliations(): void { $this->command->info('Seeding bank reconciliations...'); // Pending reconciliation BankReconciliation::create([ 'reconciliation_month' => now()->startOfMonth(), 'bank_statement_date' => now(), 'bank_statement_balance' => 100000, 'system_book_balance' => 95000, 'outstanding_checks' => [ ['check_number' => 'CHK001', 'amount' => 3000, 'description' => 'Vendor A payment'], ['check_number' => 'CHK002', 'amount' => 2000, 'description' => 'Service fee'], ], 'deposits_in_transit' => [ ['date' => now()->format('Y-m-d'), 'amount' => 5000, 'description' => 'Member dues'], ], 'bank_charges' => [ ['amount' => 500, 'description' => 'Monthly service charge'], ], 'discrepancy_amount' => 4500, 'notes' => 'Pending review', 'prepared_by_cashier_id' => $this->cashier->id, 'prepared_at' => now(), 'reconciliation_status' => 'pending', ]); // Reviewed reconciliation BankReconciliation::create([ 'reconciliation_month' => now()->subMonth()->startOfMonth(), 'bank_statement_date' => now()->subMonth(), 'bank_statement_balance' => 95000, 'system_book_balance' => 93000, 'outstanding_checks' => [ ['check_number' => 'CHK003', 'amount' => 1500, 'description' => 'Supplies'], ], 'deposits_in_transit' => [ ['date' => now()->subMonth()->format('Y-m-d'), 'amount' => 3000, 'description' => 'Donation'], ], 'bank_charges' => [ ['amount' => 500, 'description' => 'Service charge'], ], 'discrepancy_amount' => 0, 'notes' => 'All items reconciled', 'prepared_by_cashier_id' => $this->cashier->id, 'prepared_at' => now()->subMonth(), 'reviewed_by_accountant_id' => $this->accountant->id, 'reviewed_at' => now()->subMonth()->addDays(2), 'reconciliation_status' => 'pending', ]); // Completed reconciliation BankReconciliation::create([ 'reconciliation_month' => now()->subMonths(2)->startOfMonth(), 'bank_statement_date' => now()->subMonths(2), 'bank_statement_balance' => 90000, 'system_book_balance' => 90000, 'outstanding_checks' => [], 'deposits_in_transit' => [], 'bank_charges' => [ ['amount' => 500, 'description' => 'Service charge'], ], 'discrepancy_amount' => 0, 'notes' => 'Perfect match', 'prepared_by_cashier_id' => $this->cashier->id, 'prepared_at' => now()->subMonths(2), 'reviewed_by_accountant_id' => $this->accountant->id, 'reviewed_at' => now()->subMonths(2)->addDays(2), 'approved_by_manager_id' => $this->chair->id, 'approved_at' => now()->subMonths(2)->addDays(3), 'reconciliation_status' => 'completed', ]); // Reconciliation with discrepancy BankReconciliation::create([ 'reconciliation_month' => now()->subMonths(3)->startOfMonth(), 'bank_statement_date' => now()->subMonths(3), 'bank_statement_balance' => 85000, 'system_book_balance' => 75000, 'outstanding_checks' => [ ['check_number' => 'CHK004', 'amount' => 2000, 'description' => 'Payment'], ], 'deposits_in_transit' => [], 'bank_charges' => [ ['amount' => 500, 'description' => 'Service charge'], ], 'discrepancy_amount' => 7500, 'notes' => 'Large discrepancy - needs investigation', 'prepared_by_cashier_id' => $this->cashier->id, 'prepared_at' => now()->subMonths(3), 'reconciliation_status' => 'discrepancy', ]); $this->command->info('✓ Bank reconciliations seeded'); } }