123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- <?php
-
- use App\Models\Accounting\Account;
- use App\Models\Accounting\Transaction;
- use App\Utilities\Currency\ConfigureCurrencies;
-
- it('creates correct journal entries for a deposit transaction', function () {
- $transaction = Transaction::factory()
- ->forDefaultBankAccount()
- ->forUncategorizedRevenue()
- ->asDeposit(1000)
- ->create();
-
- [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
-
- expect($transaction->journalEntries->count())->toBe(2)
- ->and($debitAccount->name)->toBe('Cash on Hand')
- ->and($creditAccount->name)->toBe('Uncategorized Income');
- });
-
- it('creates correct journal entries for a withdrawal transaction', function () {
- $transaction = Transaction::factory()
- ->forDefaultBankAccount()
- ->forUncategorizedExpense()
- ->asWithdrawal(500)
- ->create();
-
- [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
-
- expect($transaction->journalEntries->count())->toBe(2)
- ->and($debitAccount->name)->toBe('Uncategorized Expense')
- ->and($creditAccount->name)->toBe('Cash on Hand');
- });
-
- it('creates correct journal entries for a transfer transaction', function () {
- $transaction = Transaction::factory()
- ->forDefaultBankAccount()
- ->forDestinationBankAccount()
- ->asTransfer(1500)
- ->create();
-
- [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
-
- // Acts as a withdrawal transaction for the source account
- expect($transaction->journalEntries->count())->toBe(2)
- ->and($debitAccount->name)->toBe('Destination Bank Account')
- ->and($creditAccount->name)->toBe('Cash on Hand');
- });
-
- it('does not create journal entries for a journal transaction', function () {
- $transaction = Transaction::factory()
- ->forDefaultBankAccount()
- ->forUncategorizedRevenue()
- ->asJournal(1000)
- ->create();
-
- // Journal entries for a journal transaction are created manually
- expect($transaction->journalEntries->count())->toBe(0);
- });
-
- it('stores and sums correct debit and credit amounts for different transaction types', function ($method, $setupMethod, $amount) {
- $transaction = Transaction::factory()
- ->forDefaultBankAccount()
- ->{$setupMethod}()
- ->{$method}($amount)
- ->create();
-
- expect($transaction->journalEntries->sumDebits()->getValue())->toEqual($amount)
- ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual($amount);
- })->with([
- ['asDeposit', 'forUncategorizedRevenue', 2000],
- ['asWithdrawal', 'forUncategorizedExpense', 500],
- ['asTransfer', 'forDestinationBankAccount', 1500],
- ]);
-
- it('deletes associated journal entries when transaction is deleted', function () {
- $transaction = Transaction::factory()
- ->forDefaultBankAccount()
- ->forUncategorizedRevenue()
- ->asDeposit(1000)
- ->create();
-
- expect($transaction->journalEntries()->count())->toBe(2);
-
- $transaction->delete();
-
- $this->assertModelMissing($transaction);
-
- $this->assertDatabaseCount('journal_entries', 0);
- });
-
- it('handles multi-currency transfers without conversion when the source bank account is in the default currency', function () {
- $foreignBankAccount = Account::factory()
- ->withForeignBankAccount('Foreign Bank Account', 'EUR', 0.92)
- ->create();
-
- $transaction = Transaction::factory()
- ->forDefaultBankAccount()
- ->forDestinationBankAccount($foreignBankAccount)
- ->asTransfer(1500)
- ->create();
-
- [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
-
- expect($transaction->journalEntries->count())->toBe(2)
- ->and($debitAccount->name)->toBe('Foreign Bank Account')
- ->and($creditAccount->name)->toBe('Cash on Hand')
- ->and($transaction->journalEntries->sumDebits()->getValue())->toEqual(1500)
- ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual(1500)
- ->and($transaction->amount)->toEqual('1,500.00');
- });
-
- it('handles multi-currency transfers correctly', function () {
- $foreignBankAccount = Account::factory()
- ->withForeignBankAccount('CAD Bank Account', 'CAD', 1.36)
- ->create();
-
- ConfigureCurrencies::syncCurrencies();
-
- // Create a transfer of 1500 CAD from the foreign bank account to USD bank account
- $transaction = Transaction::factory()
- ->forBankAccount($foreignBankAccount->bankAccount)
- ->forDestinationBankAccount()
- ->asTransfer(1500)
- ->create();
-
- [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
-
- expect($transaction->journalEntries->count())->toBe(2)
- ->and($debitAccount->name)->toBe('Destination Bank Account') // Debit: Destination (USD) account
- ->and($creditAccount->name)->toBe('CAD Bank Account'); // Credit: Foreign (CAD) account
-
- // The 1500 CAD is worth 1102.94 USD (1500 CAD / 1.36)
- $expectedUSDValue = round(1500 / 1.36, 2);
-
- // Verify that the debit is 1102.94 USD and the credit is 1500 CAD converted to 1102.94 USD
- // Transaction amount stays in source bank account currency (cast is applied)
- expect($transaction->journalEntries->sumDebits()->getValue())->toEqual($expectedUSDValue)
- ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual($expectedUSDValue)
- ->and($transaction->amount)->toEqual('1,500.00');
- });
-
- it('handles multi-currency deposits correctly', function () {
- $foreignBankAccount = Account::factory()
- ->withForeignBankAccount('BHD Bank Account', 'BHD', 0.38)
- ->create();
-
- ConfigureCurrencies::syncCurrencies();
-
- // Create a deposit of 1500 BHD to the foreign bank account
- $transaction = Transaction::factory()
- ->forBankAccount($foreignBankAccount->bankAccount)
- ->forUncategorizedRevenue()
- ->asDeposit(1500)
- ->create();
-
- [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
-
- expect($transaction->journalEntries->count())->toBe(2)
- ->and($debitAccount->name)->toBe('BHD Bank Account') // Debit: Foreign (BHD) account
- ->and($creditAccount->name)->toBe('Uncategorized Income'); // Credit: Uncategorized Income (USD) account
-
- // Convert to USD using the rate 0.38 BHD per USD
- $expectedUSDValue = round(1500 / 0.38, 2);
-
- // Verify that the debit is 39473.68 USD and the credit is 1500 BHD converted to 39473.68 USD
- expect($transaction->journalEntries->sumDebits()->getValue())->toEqual($expectedUSDValue)
- ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual($expectedUSDValue)
- ->and($transaction->amount)->toEqual('1,500.000'); // Original amount in BHD (3 decimal precision)
- });
-
- it('handles multi-currency withdrawals correctly', function () {
- $foreignBankAccount = Account::factory()
- ->withForeignBankAccount('Foreign Bank Account', 'GBP', 0.76) // GBP account
- ->create();
-
- ConfigureCurrencies::syncCurrencies();
-
- $transaction = Transaction::factory()
- ->forBankAccount($foreignBankAccount->bankAccount)
- ->forUncategorizedExpense()
- ->asWithdrawal(1500)
- ->create();
-
- [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
-
- expect($transaction->journalEntries->count())->toBe(2)
- ->and($debitAccount->name)->toBe('Uncategorized Expense')
- ->and($creditAccount->name)->toBe('Foreign Bank Account');
-
- $expectedUSDValue = round(1500 / 0.76, 2);
-
- expect($transaction->journalEntries->sumDebits()->getValue())->toEqual($expectedUSDValue)
- ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual($expectedUSDValue)
- ->and($transaction->amount)->toEqual('1,500.00');
- });
|