Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

TransactionTest.php 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. use App\Models\Accounting\Account;
  3. use App\Models\Accounting\Transaction;
  4. use App\Utilities\Currency\ConfigureCurrencies;
  5. it('creates correct journal entries for a deposit transaction', function () {
  6. $transaction = Transaction::factory()
  7. ->forDefaultBankAccount()
  8. ->forUncategorizedRevenue()
  9. ->asDeposit(1000)
  10. ->create();
  11. [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
  12. expect($transaction->journalEntries->count())->toBe(2)
  13. ->and($debitAccount->name)->toBe('Cash on Hand')
  14. ->and($creditAccount->name)->toBe('Uncategorized Income');
  15. });
  16. it('creates correct journal entries for a withdrawal transaction', function () {
  17. $transaction = Transaction::factory()
  18. ->forDefaultBankAccount()
  19. ->forUncategorizedExpense()
  20. ->asWithdrawal(500)
  21. ->create();
  22. [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
  23. expect($transaction->journalEntries->count())->toBe(2)
  24. ->and($debitAccount->name)->toBe('Uncategorized Expense')
  25. ->and($creditAccount->name)->toBe('Cash on Hand');
  26. });
  27. it('creates correct journal entries for a transfer transaction', function () {
  28. $transaction = Transaction::factory()
  29. ->forDefaultBankAccount()
  30. ->forDestinationBankAccount()
  31. ->asTransfer(1500)
  32. ->create();
  33. [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
  34. // Acts as a withdrawal transaction for the source account
  35. expect($transaction->journalEntries->count())->toBe(2)
  36. ->and($debitAccount->name)->toBe('Destination Bank Account')
  37. ->and($creditAccount->name)->toBe('Cash on Hand');
  38. });
  39. it('does not create journal entries for a journal transaction', function () {
  40. $transaction = Transaction::factory()
  41. ->forDefaultBankAccount()
  42. ->forUncategorizedRevenue()
  43. ->asJournal(1000)
  44. ->create();
  45. // Journal entries for a journal transaction are created manually
  46. expect($transaction->journalEntries->count())->toBe(0);
  47. });
  48. it('stores and sums correct debit and credit amounts for different transaction types', function ($method, $setupMethod, $amount) {
  49. $transaction = Transaction::factory()
  50. ->forDefaultBankAccount()
  51. ->{$setupMethod}()
  52. ->{$method}($amount)
  53. ->create();
  54. expect($transaction->journalEntries->sumDebits()->getValue())->toEqual($amount)
  55. ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual($amount);
  56. })->with([
  57. ['asDeposit', 'forUncategorizedRevenue', 2000],
  58. ['asWithdrawal', 'forUncategorizedExpense', 500],
  59. ['asTransfer', 'forDestinationBankAccount', 1500],
  60. ]);
  61. it('deletes associated journal entries when transaction is deleted', function () {
  62. $transaction = Transaction::factory()
  63. ->forDefaultBankAccount()
  64. ->forUncategorizedRevenue()
  65. ->asDeposit(1000)
  66. ->create();
  67. expect($transaction->journalEntries()->count())->toBe(2);
  68. $transaction->delete();
  69. $this->assertModelMissing($transaction);
  70. $this->assertDatabaseCount('journal_entries', 0);
  71. });
  72. it('handles multi-currency transfers without conversion when the source bank account is in the default currency', function () {
  73. $foreignBankAccount = Account::factory()
  74. ->withForeignBankAccount('Foreign Bank Account', 'EUR', 0.92)
  75. ->create();
  76. $transaction = Transaction::factory()
  77. ->forDefaultBankAccount()
  78. ->forDestinationBankAccount($foreignBankAccount)
  79. ->asTransfer(1500)
  80. ->create();
  81. [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
  82. expect($transaction->journalEntries->count())->toBe(2)
  83. ->and($debitAccount->name)->toBe('Foreign Bank Account')
  84. ->and($creditAccount->name)->toBe('Cash on Hand')
  85. ->and($transaction->journalEntries->sumDebits()->getValue())->toEqual(1500)
  86. ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual(1500)
  87. ->and($transaction->amount)->toEqual('1,500.00');
  88. });
  89. it('handles multi-currency transfers correctly', function () {
  90. $foreignBankAccount = Account::factory()
  91. ->withForeignBankAccount('CAD Bank Account', 'CAD', 1.36)
  92. ->create();
  93. ConfigureCurrencies::syncCurrencies();
  94. // Create a transfer of 1500 CAD from the foreign bank account to USD bank account
  95. $transaction = Transaction::factory()
  96. ->forBankAccount($foreignBankAccount->bankAccount)
  97. ->forDestinationBankAccount()
  98. ->asTransfer(1500)
  99. ->create();
  100. [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
  101. expect($transaction->journalEntries->count())->toBe(2)
  102. ->and($debitAccount->name)->toBe('Destination Bank Account') // Debit: Destination (USD) account
  103. ->and($creditAccount->name)->toBe('CAD Bank Account'); // Credit: Foreign (CAD) account
  104. // The 1500 CAD is worth 1102.94 USD (1500 CAD / 1.36)
  105. $expectedUSDValue = round(1500 / 1.36, 2);
  106. // Verify that the debit is 1102.94 USD and the credit is 1500 CAD converted to 1102.94 USD
  107. // Transaction amount stays in source bank account currency (cast is applied)
  108. expect($transaction->journalEntries->sumDebits()->getValue())->toEqual($expectedUSDValue)
  109. ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual($expectedUSDValue)
  110. ->and($transaction->amount)->toEqual('1,500.00');
  111. });
  112. it('handles multi-currency deposits correctly', function () {
  113. $foreignBankAccount = Account::factory()
  114. ->withForeignBankAccount('BHD Bank Account', 'BHD', 0.38)
  115. ->create();
  116. ConfigureCurrencies::syncCurrencies();
  117. // Create a deposit of 1500 BHD to the foreign bank account
  118. $transaction = Transaction::factory()
  119. ->forBankAccount($foreignBankAccount->bankAccount)
  120. ->forUncategorizedRevenue()
  121. ->asDeposit(1500)
  122. ->create();
  123. [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
  124. expect($transaction->journalEntries->count())->toBe(2)
  125. ->and($debitAccount->name)->toBe('BHD Bank Account') // Debit: Foreign (BHD) account
  126. ->and($creditAccount->name)->toBe('Uncategorized Income'); // Credit: Uncategorized Income (USD) account
  127. // Convert to USD using the rate 0.38 BHD per USD
  128. $expectedUSDValue = round(1500 / 0.38, 2);
  129. // Verify that the debit is 39473.68 USD and the credit is 1500 BHD converted to 39473.68 USD
  130. expect($transaction->journalEntries->sumDebits()->getValue())->toEqual($expectedUSDValue)
  131. ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual($expectedUSDValue)
  132. ->and($transaction->amount)->toEqual('1,500.000'); // Original amount in BHD (3 decimal precision)
  133. });
  134. it('handles multi-currency withdrawals correctly', function () {
  135. $foreignBankAccount = Account::factory()
  136. ->withForeignBankAccount('Foreign Bank Account', 'GBP', 0.76) // GBP account
  137. ->create();
  138. ConfigureCurrencies::syncCurrencies();
  139. $transaction = Transaction::factory()
  140. ->forBankAccount($foreignBankAccount->bankAccount)
  141. ->forUncategorizedExpense()
  142. ->asWithdrawal(1500)
  143. ->create();
  144. [$debitAccount, $creditAccount] = getTransactionDebitAndCreditAccounts($transaction);
  145. expect($transaction->journalEntries->count())->toBe(2)
  146. ->and($debitAccount->name)->toBe('Uncategorized Expense')
  147. ->and($creditAccount->name)->toBe('Foreign Bank Account');
  148. $expectedUSDValue = round(1500 / 0.76, 2);
  149. expect($transaction->journalEntries->sumDebits()->getValue())->toEqual($expectedUSDValue)
  150. ->and($transaction->journalEntries->sumCredits()->getValue())->toEqual($expectedUSDValue)
  151. ->and($transaction->amount)->toEqual('1,500.00');
  152. });