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.

HasJournalEntryActions.php 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <?php
  2. namespace App\Concerns;
  3. use App\Enums\Accounting\JournalEntryType;
  4. use App\Utilities\Currency\CurrencyAccessor;
  5. use App\Utilities\Currency\CurrencyConverter;
  6. use Filament\Tables\Actions\Action;
  7. trait HasJournalEntryActions
  8. {
  9. public int $debitAmount = 0;
  10. public int $creditAmount = 0;
  11. private function formatMoney(int $amount): string
  12. {
  13. return CurrencyConverter::formatCentsToMoney($amount, CurrencyAccessor::getDefaultCurrency());
  14. }
  15. /**
  16. * Expects formatted simple amount: e.g. 1,000.00 or 1.000,00
  17. *
  18. * Sets debit amount in cents as integer: e.g. 100000
  19. */
  20. public function setDebitAmount(int $amount): void
  21. {
  22. $this->debitAmount = $amount;
  23. }
  24. /**
  25. * Expects formatted simple amount: e.g. 1,000.00 or 1.000,00
  26. *
  27. * Sets credit amount in cents as integer: e.g. 100000
  28. */
  29. public function setCreditAmount(int $amount): void
  30. {
  31. $this->creditAmount = $amount;
  32. }
  33. /**
  34. * Returns debit amount in cents as integer: e.g. 100000
  35. */
  36. public function getDebitAmount(): int
  37. {
  38. return $this->debitAmount;
  39. }
  40. /**
  41. * Returns credit amount in cents as integer: e.g. 100000
  42. */
  43. public function getCreditAmount(): int
  44. {
  45. return $this->creditAmount;
  46. }
  47. /**
  48. * Expects debit amount in cents as string integer: e.g. 100000
  49. *
  50. * Returns formatted amount: e.g. $1,000.00 or €1.000,00
  51. */
  52. public function getFormattedDebitAmount(): string
  53. {
  54. return $this->formatMoney($this->getDebitAmount());
  55. }
  56. /**
  57. * Expects credit amount in cents as integer: e.g. 100000
  58. *
  59. * Returns formatted amount: e.g. $1,000.00 or €1.000,00
  60. */
  61. public function getFormattedCreditAmount(): string
  62. {
  63. return $this->formatMoney($this->getCreditAmount());
  64. }
  65. /**
  66. * Returns balance difference in cents as integer: e.g. 100000
  67. */
  68. public function getBalanceDifference(): int
  69. {
  70. return $this->getDebitAmount() - $this->getCreditAmount();
  71. }
  72. /**
  73. * Returns formatted balance difference: e.g. $1,000.00 or €1.000,00
  74. */
  75. public function getFormattedBalanceDifference(): string
  76. {
  77. $absoluteDifference = abs($this->getBalanceDifference());
  78. return $this->formatMoney($absoluteDifference);
  79. }
  80. /**
  81. * Returns boolean indicating whether the journal entry is balanced
  82. * using the debit and credit integer amounts
  83. */
  84. public function isJournalEntryBalanced(): bool
  85. {
  86. return $this->getDebitAmount() === $this->getCreditAmount();
  87. }
  88. /**
  89. * Resets debit and credit amounts to '0.00'
  90. */
  91. public function resetJournalEntryAmounts(): void
  92. {
  93. $this->debitAmount = 0;
  94. $this->creditAmount = 0;
  95. }
  96. public function adjustJournalEntryAmountsForTypeChange(JournalEntryType $newType, JournalEntryType $oldType, ?string $amount): void
  97. {
  98. if ($newType === $oldType) {
  99. return;
  100. }
  101. $entries = $this instanceof Action
  102. ? ($this->getLivewire()->mountedTableActionsData[0]['journalEntries'] ?? [])
  103. : ($this->getLivewire()->mountedActionsData[0]['journalEntries'] ?? []);
  104. // Reset the totals
  105. $this->debitAmount = 0;
  106. $this->creditAmount = 0;
  107. // Recalculate totals from all entries
  108. foreach ($entries as $entry) {
  109. if (empty($entry['type']) || empty($entry['amount'])) {
  110. continue;
  111. }
  112. $entryType = JournalEntryType::parse($entry['type']);
  113. $entryAmount = $this->ensureCompleteDecimal($entry['amount']);
  114. $formattedAmount = $this->convertAmountToCents($entryAmount);
  115. if ($entryType->isDebit()) {
  116. $this->debitAmount += $formattedAmount;
  117. } else {
  118. $this->creditAmount += $formattedAmount;
  119. }
  120. }
  121. }
  122. /**
  123. * Expects the journal entry type,
  124. * the new amount and the old amount as formatted simple amounts: e.g. 1,000.00 or 1.000,00
  125. * It can expect the amounts as partial amounts: e.g. 1,000. or 1.000, (this needs to be handled by this method)
  126. */
  127. public function updateJournalEntryAmount(JournalEntryType $journalEntryType, ?string $newAmount, ?string $oldAmount): void
  128. {
  129. if ($newAmount === $oldAmount) {
  130. return;
  131. }
  132. $entries = $this instanceof Action
  133. ? ($this->getLivewire()->mountedTableActionsData[0]['journalEntries'] ?? [])
  134. : ($this->getLivewire()->mountedActionsData[0]['journalEntries'] ?? []);
  135. // Reset the totals
  136. $this->debitAmount = 0;
  137. $this->creditAmount = 0;
  138. // Recalculate totals from all entries
  139. foreach ($entries as $entry) {
  140. if (empty($entry['type']) || empty($entry['amount'])) {
  141. continue;
  142. }
  143. $entryType = JournalEntryType::parse($entry['type']);
  144. $entryAmount = $this->ensureCompleteDecimal($entry['amount']);
  145. $formattedAmount = $this->convertAmountToCents($entryAmount);
  146. if ($entryType->isDebit()) {
  147. $this->debitAmount += $formattedAmount;
  148. } else {
  149. $this->creditAmount += $formattedAmount;
  150. }
  151. }
  152. }
  153. private function ensureCompleteDecimal(?string $amount): string
  154. {
  155. if ($amount === null) {
  156. return '0';
  157. }
  158. $currency = currency('MYR');
  159. $decimal = $currency->getDecimalMark();
  160. if (substr($amount, -1) === $decimal) {
  161. return '0';
  162. }
  163. return $amount;
  164. }
  165. /**
  166. * Expects formatted simple amount: e.g. 1,000.00 or 1.000,00
  167. *
  168. * Returns sanitized amount in cents as integer: e.g. 100000
  169. */
  170. protected function convertAmountToCents(string $amount): int
  171. {
  172. return CurrencyConverter::convertToCents($amount, 'USD');
  173. }
  174. }