394 lines
14 KiB
PHP
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');
|
|
}
|
|
}
|