You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AccountService.php 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. <?php
  2. namespace App\Services;
  3. use App\Contracts\AccountHandler;
  4. use App\Enums\Accounting\AccountCategory;
  5. use App\Models\Accounting\Account;
  6. use App\Models\Accounting\Transaction;
  7. use App\Models\Banking\BankAccount;
  8. use App\Repositories\Accounting\JournalEntryRepository;
  9. use App\Utilities\Currency\CurrencyAccessor;
  10. use App\ValueObjects\Money;
  11. class AccountService implements AccountHandler
  12. {
  13. public function __construct(
  14. protected JournalEntryRepository $journalEntryRepository
  15. ) {
  16. }
  17. public function getDebitBalance(Account $account, string $startDate, string $endDate): Money
  18. {
  19. $amount = $this->journalEntryRepository->sumDebitAmounts($account, $startDate, $endDate);
  20. return new Money($amount, $account->currency_code);
  21. }
  22. public function getCreditBalance(Account $account, string $startDate, string $endDate): Money
  23. {
  24. $amount = $this->journalEntryRepository->sumCreditAmounts($account, $startDate, $endDate);
  25. return new Money($amount, $account->currency_code);
  26. }
  27. public function getNetMovement(Account $account, string $startDate, string $endDate): Money
  28. {
  29. $debitBalance = $this->journalEntryRepository->sumDebitAmounts($account, $startDate, $endDate);
  30. $creditBalance = $this->journalEntryRepository->sumCreditAmounts($account, $startDate, $endDate);
  31. $netMovement = $this->calculateNetMovementByCategory($account->category, $debitBalance, $creditBalance);
  32. return new Money($netMovement, $account->currency_code);
  33. }
  34. public function getStartingBalance(Account $account, string $startDate): ?Money
  35. {
  36. if (in_array($account->category, [AccountCategory::Expense, AccountCategory::Revenue], true)) {
  37. return null;
  38. }
  39. $debitBalanceBefore = $this->journalEntryRepository->sumDebitAmounts($account, $startDate);
  40. $creditBalanceBefore = $this->journalEntryRepository->sumCreditAmounts($account, $startDate);
  41. $startingBalance = $this->calculateNetMovementByCategory($account->category, $debitBalanceBefore, $creditBalanceBefore);
  42. return new Money($startingBalance, $account->currency_code);
  43. }
  44. public function getEndingBalance(Account $account, string $startDate, string $endDate): ?Money
  45. {
  46. $netMovement = $this->getNetMovement($account, $startDate, $endDate)->getAmount();
  47. if (in_array($account->category, [AccountCategory::Expense, AccountCategory::Revenue], true)) {
  48. return new Money($netMovement, $account->currency_code);
  49. }
  50. $startingBalance = $this->getStartingBalance($account, $startDate)?->getAmount();
  51. $endingBalance = $startingBalance + $netMovement;
  52. return new Money($endingBalance, $account->currency_code);
  53. }
  54. private function calculateNetMovementByCategory(AccountCategory $category, int $debitBalance, int $creditBalance): int
  55. {
  56. return match ($category) {
  57. AccountCategory::Asset, AccountCategory::Expense => $debitBalance - $creditBalance,
  58. AccountCategory::Liability, AccountCategory::Equity, AccountCategory::Revenue => $creditBalance - $debitBalance,
  59. };
  60. }
  61. public function getBalances(Account $account, string $startDate, string $endDate): array
  62. {
  63. $debitBalance = $this->getDebitBalance($account, $startDate, $endDate)->getAmount();
  64. $creditBalance = $this->getCreditBalance($account, $startDate, $endDate)->getAmount();
  65. $netMovement = $this->getNetMovement($account, $startDate, $endDate)->getAmount();
  66. $balances = [
  67. 'debit_balance' => $debitBalance,
  68. 'credit_balance' => $creditBalance,
  69. 'net_movement' => $netMovement,
  70. ];
  71. if (! in_array($account->category, [AccountCategory::Expense, AccountCategory::Revenue], true)) {
  72. $balances['starting_balance'] = $this->getStartingBalance($account, $startDate)?->getAmount();
  73. $balances['ending_balance'] = $this->getEndingBalance($account, $startDate, $endDate)?->getAmount();
  74. }
  75. return $balances;
  76. }
  77. public function getTotalBalanceForAllBankAccounts(string $startDate, string $endDate): Money
  78. {
  79. $bankAccounts = BankAccount::with('account')
  80. ->get();
  81. $totalBalance = 0;
  82. foreach ($bankAccounts as $bankAccount) {
  83. $account = $bankAccount->account;
  84. if ($account) {
  85. $endingBalance = $this->getEndingBalance($account, $startDate, $endDate)?->getAmount() ?? 0;
  86. $totalBalance += $endingBalance;
  87. }
  88. }
  89. return new Money($totalBalance, CurrencyAccessor::getDefaultCurrency());
  90. }
  91. public function getAccountCategoryOrder(): array
  92. {
  93. return [
  94. AccountCategory::Asset->getPluralLabel(),
  95. AccountCategory::Liability->getPluralLabel(),
  96. AccountCategory::Equity->getPluralLabel(),
  97. AccountCategory::Revenue->getPluralLabel(),
  98. AccountCategory::Expense->getPluralLabel(),
  99. ];
  100. }
  101. public function getEarliestTransactionDate(): string
  102. {
  103. $earliestDate = Transaction::oldest('posted_at')
  104. ->value('posted_at');
  105. return $earliestDate ?? now()->format('Y-m-d');
  106. }
  107. }