Files
usher-manage-stack/database/seeders/FinancialWorkflowTestDataSeeder.php
2025-11-20 23:21:05 +08:00

394 lines
14 KiB
PHP

<?php
namespace Database\Seeders;
use App\Models\BankReconciliation;
use App\Models\CashierLedgerEntry;
use App\Models\FinanceDocument;
use App\Models\PaymentOrder;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
/**
* Financial Workflow Test Data Seeder
*
* Generates comprehensive test data for the financial workflow system
*/
class FinancialWorkflowTestDataSeeder extends Seeder
{
protected User $requester;
protected User $cashier;
protected User $accountant;
protected User $chair;
protected User $boardMember;
/**
* Run the database seeds.
*/
public function run(): void
{
$this->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');
}
}