Browse Source

feat: exchange rate fluctuation adjustments pt.2

3.x
wallo 1 year ago
parent
commit
fac97c527f

+ 0
- 4
app/Casts/MoneyCast.php View File

28
     {
28
     {
29
         $currency_code = $model->getAttribute('currency_code') ?? CurrencyAccessor::getDefaultCurrency();
29
         $currency_code = $model->getAttribute('currency_code') ?? CurrencyAccessor::getDefaultCurrency();
30
 
30
 
31
-        if (! $currency_code) {
32
-            throw new UnexpectedValueException('Currency code is not set');
33
-        }
34
-
35
         if (is_numeric($value)) {
31
         if (is_numeric($value)) {
36
             $value = (string) $value;
32
             $value = (string) $value;
37
         } elseif (! is_string($value)) {
33
         } elseif (! is_string($value)) {

+ 0
- 4
app/Casts/TransactionAmountCast.php View File

29
     {
29
     {
30
         $currency_code = $model->bankAccount?->account?->currency_code ?? CurrencyAccessor::getDefaultCurrency();
30
         $currency_code = $model->bankAccount?->account?->currency_code ?? CurrencyAccessor::getDefaultCurrency();
31
 
31
 
32
-        if (! $currency_code) {
33
-            throw new UnexpectedValueException('Currency code is not set');
34
-        }
35
-
36
         if (is_numeric($value)) {
32
         if (is_numeric($value)) {
37
             $value = (string) $value;
33
             $value = (string) $value;
38
         } elseif (! is_string($value)) {
34
         } elseif (! is_string($value)) {

+ 15
- 0
app/Enums/Accounting/TransactionType.php View File

17
     {
17
     {
18
         return $this->name;
18
         return $this->name;
19
     }
19
     }
20
+
21
+    public function isDeposit(): bool
22
+    {
23
+        return $this === self::Deposit;
24
+    }
25
+
26
+    public function isWithdrawal(): bool
27
+    {
28
+        return $this === self::Withdrawal;
29
+    }
30
+
31
+    public function isJournal(): bool
32
+    {
33
+        return $this === self::Journal;
34
+    }
20
 }
35
 }

+ 17
- 4
app/Filament/Company/Pages/Accounting/Transactions.php View File

16
 use App\Models\Banking\BankAccount;
16
 use App\Models\Banking\BankAccount;
17
 use App\Models\Company;
17
 use App\Models\Company;
18
 use App\Models\Setting\Localization;
18
 use App\Models\Setting\Localization;
19
+use App\Utilities\Currency\CurrencyAccessor;
20
+use App\Utilities\Currency\CurrencyConverter;
19
 use Awcodes\TableRepeater\Header;
21
 use Awcodes\TableRepeater\Header;
20
 use Exception;
22
 use Exception;
21
 use Filament\Actions;
23
 use Filament\Actions;
153
                     ->options(fn () => $this->getBankAccountOptions())
155
                     ->options(fn () => $this->getBankAccountOptions())
154
                     ->live()
156
                     ->live()
155
                     ->searchable()
157
                     ->searchable()
158
+                    ->afterStateUpdated(function (Set $set, $state, $old, Get $get) {
159
+                        $amount = CurrencyConverter::convertAndSet(
160
+                            BankAccount::find($state)->account->currency_code,
161
+                            BankAccount::find($old)->account->currency_code ?? CurrencyAccessor::getDefaultCurrency(),
162
+                            $get('amount')
163
+                        );
164
+
165
+                        if ($amount !== null) {
166
+                            $set('amount', $amount);
167
+                        }
168
+                    })
156
                     ->required(),
169
                     ->required(),
157
                 Forms\Components\Select::make('type')
170
                 Forms\Components\Select::make('type')
158
                     ->label('Type')
171
                     ->label('Type')
165
                     ->afterStateUpdated(static fn (Forms\Set $set, $state) => $set('account_id', static::getUncategorizedAccountByType(TransactionType::parse($state))?->id)),
178
                     ->afterStateUpdated(static fn (Forms\Set $set, $state) => $set('account_id', static::getUncategorizedAccountByType(TransactionType::parse($state))?->id)),
166
                 Forms\Components\TextInput::make('amount')
179
                 Forms\Components\TextInput::make('amount')
167
                     ->label('Amount')
180
                     ->label('Amount')
168
-                    ->money(static fn (Forms\Get $get) => BankAccount::find($get('bank_account_id'))?->account?->currency_code ?? 'USD')
181
+                    ->money(static fn (Forms\Get $get) => BankAccount::find($get('bank_account_id'))?->account?->currency_code ?? CurrencyAccessor::getDefaultCurrency())
169
                     ->required(),
182
                     ->required(),
170
                 Forms\Components\Select::make('account_id')
183
                 Forms\Components\Select::make('account_id')
171
                     ->label('Category')
184
                     ->label('Category')
236
                         }
249
                         }
237
                     )
250
                     )
238
                     ->currency(static fn (Transaction $record) => $record->bankAccount->account->currency_code ?? 'USD', true)
251
                     ->currency(static fn (Transaction $record) => $record->bankAccount->account->currency_code ?? 'USD', true)
239
-                    ->state(fn (Transaction $record) => $record->type === TransactionType::Journal ? $record->journalEntries->first()->amount : $record->amount),
252
+                    ->state(fn (Transaction $record) => $record->type->isJournal() ? $record->journalEntries->first()->amount : $record->amount),
240
             ])
253
             ])
241
             ->recordClasses(static fn (Transaction $record) => $record->reviewed ? 'bg-primary-300/10' : null)
254
             ->recordClasses(static fn (Transaction $record) => $record->reviewed ? 'bg-primary-300/10' : null)
242
             ->defaultSort('posted_at', 'desc')
255
             ->defaultSort('posted_at', 'desc')
342
                         ->modalHeading('Edit Transaction')
355
                         ->modalHeading('Edit Transaction')
343
                         ->modalWidth(MaxWidth::ThreeExtraLarge)
356
                         ->modalWidth(MaxWidth::ThreeExtraLarge)
344
                         ->form(fn (Form $form) => $this->transactionForm($form))
357
                         ->form(fn (Form $form) => $this->transactionForm($form))
345
-                        ->hidden(static fn (Transaction $record) => $record->type === TransactionType::Journal),
358
+                        ->hidden(static fn (Transaction $record) => $record->type->isJournal()),
346
                     Tables\Actions\EditAction::make('updateJournalTransaction')
359
                     Tables\Actions\EditAction::make('updateJournalTransaction')
347
                         ->label('Edit Journal Transaction')
360
                         ->label('Edit Journal Transaction')
348
                         ->modalHeading('Journal Entry')
361
                         ->modalHeading('Journal Entry')
355
                             $this->setDebitAmount($debitAmounts);
368
                             $this->setDebitAmount($debitAmounts);
356
                             $this->setCreditAmount($creditAmounts);
369
                             $this->setCreditAmount($creditAmounts);
357
                         })
370
                         })
358
-                        ->hidden(static fn (Transaction $record) => $record->type !== TransactionType::Journal),
371
+                        ->visible(static fn (Transaction $record) => $record->type->isJournal()),
359
                     Tables\Actions\DeleteAction::make(),
372
                     Tables\Actions\DeleteAction::make(),
360
                     Tables\Actions\ReplicateAction::make()
373
                     Tables\Actions\ReplicateAction::make()
361
                         ->excludeAttributes(['created_by', 'updated_by', 'created_at', 'updated_at'])
374
                         ->excludeAttributes(['created_by', 'updated_by', 'created_at', 'updated_at'])

+ 1
- 1
app/Filament/Company/Resources/Banking/AccountResource.php View File

172
                     ->icon(static fn (BankAccount $record) => $record->isEnabled() ? 'heroicon-o-lock-closed' : null)
172
                     ->icon(static fn (BankAccount $record) => $record->isEnabled() ? 'heroicon-o-lock-closed' : null)
173
                     ->tooltip(static fn (BankAccount $record) => $record->isEnabled() ? 'Default Account' : null)
173
                     ->tooltip(static fn (BankAccount $record) => $record->isEnabled() ? 'Default Account' : null)
174
                     ->iconPosition('after')
174
                     ->iconPosition('after')
175
-                    ->description(static fn (BankAccount $record) => $record->mask ?: 'N/A')
175
+                    ->description(static fn (BankAccount $record) => $record->mask ?? null)
176
                     ->sortable(),
176
                     ->sortable(),
177
                 Tables\Columns\TextColumn::make('account.ending_balance')
177
                 Tables\Columns\TextColumn::make('account.ending_balance')
178
                     ->localizeLabel('Current Balance')
178
                     ->localizeLabel('Current Balance')

+ 32
- 4
app/Listeners/ConfigureChartOfAccounts.php View File

3
 namespace App\Listeners;
3
 namespace App\Listeners;
4
 
4
 
5
 use App\Enums\Accounting\AccountType;
5
 use App\Enums\Accounting\AccountType;
6
+use App\Enums\Banking\BankAccountType;
6
 use App\Events\CompanyGenerated;
7
 use App\Events\CompanyGenerated;
7
 use App\Models\Accounting\Account;
8
 use App\Models\Accounting\Account;
8
 use App\Models\Accounting\AccountSubtype;
9
 use App\Models\Accounting\AccountSubtype;
10
+use App\Models\Banking\BankAccount;
9
 use App\Models\Company;
11
 use App\Models\Company;
10
 use App\Utilities\Currency\CurrencyAccessor;
12
 use App\Utilities\Currency\CurrencyAccessor;
11
 
13
 
55
             $baseCode = $subtypeConfig['base_code'];
57
             $baseCode = $subtypeConfig['base_code'];
56
 
58
 
57
             foreach ($subtypeConfig['accounts'] as $accountName => $accountDetails) {
59
             foreach ($subtypeConfig['accounts'] as $accountName => $accountDetails) {
58
-                Account::create([
60
+                $bankAccount = null;
61
+
62
+                if ($subtypeConfig['multi_currency'] && isset($subtypeConfig['bank_account_type'])) {
63
+                    $bankAccount = $this->createBankAccountForMultiCurrency($company, $subtypeConfig['bank_account_type']);
64
+                }
65
+
66
+                $account = Account::create([
59
                     'company_id' => $company->id,
67
                     'company_id' => $company->id,
68
+                    'subtype_id' => $subtype->id,
60
                     'category' => $subtype->type->getCategory()->value,
69
                     'category' => $subtype->type->getCategory()->value,
61
                     'type' => $subtype->type->value,
70
                     'type' => $subtype->type->value,
62
-                    'subtype_id' => $subtype->id,
63
                     'code' => $baseCode++,
71
                     'code' => $baseCode++,
64
                     'name' => $accountName,
72
                     'name' => $accountName,
73
+                    'currency_code' => CurrencyAccessor::getDefaultCurrency(),
65
                     'description' => $accountDetails['description'] ?? 'No description available.',
74
                     'description' => $accountDetails['description'] ?? 'No description available.',
66
-                    'ending_balance' => 0,
67
                     'active' => true,
75
                     'active' => true,
68
                     'default' => true,
76
                     'default' => true,
69
-                    'currency_code' => CurrencyAccessor::getDefaultCurrency(),
70
                     'created_by' => $company->owner->id,
77
                     'created_by' => $company->owner->id,
71
                     'updated_by' => $company->owner->id,
78
                     'updated_by' => $company->owner->id,
72
                 ]);
79
                 ]);
80
+
81
+                if ($bankAccount) {
82
+                    $account->accountable()->associate($bankAccount);
83
+                }
84
+
85
+                $account->save();
73
             }
86
             }
74
         }
87
         }
75
     }
88
     }
89
+
90
+    private function createBankAccountForMultiCurrency(Company $company, string $bankAccountType): BankAccount
91
+    {
92
+        $bankAccountType = BankAccountType::from($bankAccountType) ?? BankAccountType::Other;
93
+
94
+        return BankAccount::create([
95
+            'company_id' => $company->id,
96
+            'institution_id' => null,
97
+            'type' => $bankAccountType,
98
+            'number' => null,
99
+            'enabled' => BankAccount::where('company_id', $company->id)->where('enabled', true)->doesntExist(),
100
+            'created_by' => $company->owner->id,
101
+            'updated_by' => $company->owner->id,
102
+        ]);
103
+    }
76
 }
104
 }

+ 2
- 1
app/Listeners/UpdateAccountBalances.php View File

37
                 $account = $bankAccount->account;
37
                 $account = $bankAccount->account;
38
 
38
 
39
                 $oldConvertedBalanceInCents = $account->ending_balance->convert()->getConvertedValue();
39
                 $oldConvertedBalanceInCents = $account->ending_balance->convert()->getConvertedValue();
40
-                $newConvertedBalance = ($event->newRate / $event->oldRate) * $oldConvertedBalanceInCents;
40
+                $ratio = $event->newRate / $event->oldRate;
41
+                $newConvertedBalance = bcmul($oldConvertedBalanceInCents, $ratio, 2);
41
                 $newConvertedBalanceInCents = (int) round($newConvertedBalance);
42
                 $newConvertedBalanceInCents = (int) round($newConvertedBalance);
42
 
43
 
43
                 $differenceInCents = $newConvertedBalanceInCents - $oldConvertedBalanceInCents;
44
                 $differenceInCents = $newConvertedBalanceInCents - $oldConvertedBalanceInCents;

+ 69
- 89
app/Observers/TransactionObserver.php View File

3
 namespace App\Observers;
3
 namespace App\Observers;
4
 
4
 
5
 use App\Enums\Accounting\JournalEntryType;
5
 use App\Enums\Accounting\JournalEntryType;
6
-use App\Enums\Accounting\TransactionType;
7
 use App\Models\Accounting\Account;
6
 use App\Models\Accounting\Account;
8
 use App\Models\Accounting\JournalEntry;
7
 use App\Models\Accounting\JournalEntry;
9
 use App\Models\Accounting\Transaction;
8
 use App\Models\Accounting\Transaction;
18
      */
17
      */
19
     public function created(Transaction $transaction): void
18
     public function created(Transaction $transaction): void
20
     {
19
     {
21
-        if ($transaction->type === TransactionType::Journal) {
20
+        if ($transaction->type->isJournal()) {
22
             return;
21
             return;
23
         }
22
         }
24
 
23
 
25
-        $chartAccount = $transaction->account;
26
-        $bankAccount = $transaction->bankAccount->account;
24
+        [$debitAccount, $creditAccount] = $this->determineAccounts($transaction);
27
 
25
 
28
-        $debitAccount = $transaction->type === TransactionType::Withdrawal ? $chartAccount : $bankAccount;
29
-        $creditAccount = $transaction->type === TransactionType::Withdrawal ? $bankAccount : $chartAccount;
26
+        if ($debitAccount === null || $creditAccount === null) {
27
+            return;
28
+        }
30
 
29
 
31
         $this->createJournalEntries($transaction, $debitAccount, $creditAccount);
30
         $this->createJournalEntries($transaction, $debitAccount, $creditAccount);
32
     }
31
     }
33
 
32
 
34
-    private function createJournalEntries(Transaction $transaction, Account $debitAccount, Account $creditAccount): void
33
+    /**
34
+     * Handle the Transaction "updated" event.
35
+     */
36
+    public function updated(Transaction $transaction): void
35
     {
37
     {
36
-        $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
37
-        $transactionCurrency = $transaction->bankAccount->account->currency_code; // only account which would have a different currency compared to the default currency
38
+        if ($transaction->type->isJournal() || $this->hasRelevantChanges($transaction) === false) {
39
+            return;
40
+        }
38
 
41
 
39
-        if ($transactionCurrency !== $defaultCurrency) {
40
-            $convertedTransactionAmount = $this->convertToDefaultCurrency($transaction->amount, $transactionCurrency, $defaultCurrency);
41
-        } else {
42
-            $convertedTransactionAmount = $transaction->amount;
42
+        $journalEntries = $transaction->journalEntries;
43
+
44
+        $debitEntry = $journalEntries->where('type', JournalEntryType::Debit)->first();
45
+        $creditEntry = $journalEntries->where('type', JournalEntryType::Credit)->first();
46
+
47
+        if ($debitEntry === null || $creditEntry === null) {
48
+            return;
49
+        }
50
+
51
+        [$debitAccount, $creditAccount] = $this->determineAccounts($transaction);
52
+
53
+        if ($debitAccount === null || $creditAccount === null) {
54
+            return;
43
         }
55
         }
44
 
56
 
57
+        $convertedTransactionAmount = $this->getConvertedTransactionAmount($transaction);
58
+
59
+        $this->updateJournalEntriesForTransaction($debitEntry, $debitAccount, $convertedTransactionAmount);
60
+        $this->updateJournalEntriesForTransaction($creditEntry, $creditAccount, $convertedTransactionAmount);
61
+    }
62
+
63
+    /**
64
+     * Handle the Transaction "deleting" event.
65
+     */
66
+    public function deleting(Transaction $transaction): void
67
+    {
68
+        DB::transaction(static function () use ($transaction) {
69
+            $transaction->journalEntries()->each(fn (JournalEntry $entry) => $entry->delete());
70
+        });
71
+    }
72
+
73
+    private function determineAccounts(Transaction $transaction): array
74
+    {
75
+        $chartAccount = $transaction->account;
76
+        $bankAccount = $transaction->bankAccount?->account;
77
+
78
+        $debitAccount = $transaction->type->isWithdrawal() ? $chartAccount : $bankAccount;
79
+        $creditAccount = $transaction->type->isWithdrawal() ? $bankAccount : $chartAccount;
80
+
81
+        return [$debitAccount, $creditAccount];
82
+    }
83
+
84
+    private function createJournalEntries(Transaction $transaction, Account $debitAccount, Account $creditAccount): void
85
+    {
86
+        $convertedTransactionAmount = $this->getConvertedTransactionAmount($transaction);
87
+
45
         $debitAccount->journalEntries()->create([
88
         $debitAccount->journalEntries()->create([
46
             'company_id' => $transaction->company_id,
89
             'company_id' => $transaction->company_id,
47
             'transaction_id' => $transaction->id,
90
             'transaction_id' => $transaction->id,
59
         ]);
102
         ]);
60
     }
103
     }
61
 
104
 
62
-    private function convertToDefaultCurrency(string $amount, string $fromCurrency, string $toCurrency): string
105
+    private function getConvertedTransactionAmount(Transaction $transaction): string
63
     {
106
     {
64
-        $amountInCents = CurrencyConverter::prepareForAccessor($amount, $fromCurrency);
65
-
66
-        $convertedAmountInCents = CurrencyConverter::convertBalance($amountInCents, $fromCurrency, $toCurrency);
67
-
68
-        return CurrencyConverter::prepareForMutator($convertedAmountInCents, $toCurrency);
69
-    }
70
-
71
-    /**
72
-     * Handle the Transaction "updated" event.
73
-     */
74
-    public function updated(Transaction $transaction): void
75
-    {
76
-        if ($transaction->type === TransactionType::Journal || $this->hasRelevantChanges($transaction) === false) {
77
-            return;
78
-        }
79
-
80
-        $chartAccount = $transaction->account;
81
-        $bankAccount = $transaction->bankAccount?->account;
82
-
83
-        if (! $chartAccount || ! $bankAccount) {
84
-            return;
85
-        }
86
-
87
-        $journalEntries = $transaction->journalEntries;
88
-
89
-        $debitEntry = $journalEntries->where('type', JournalEntryType::Debit)->first();
90
-        $creditEntry = $journalEntries->where('type', JournalEntryType::Credit)->first();
91
-
92
-        if (! $debitEntry || ! $creditEntry) {
93
-            return;
94
-        }
95
-
96
         $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
107
         $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
97
         $transactionCurrency = $transaction->bankAccount->account->currency_code; // only account which would have a different currency compared to the default currency
108
         $transactionCurrency = $transaction->bankAccount->account->currency_code; // only account which would have a different currency compared to the default currency
98
 
109
 
99
         if ($transactionCurrency !== $defaultCurrency) {
110
         if ($transactionCurrency !== $defaultCurrency) {
100
-            $convertedTransactionAmount = $this->convertToDefaultCurrency($transaction->amount, $transactionCurrency, $defaultCurrency);
101
-        } else {
102
-            $convertedTransactionAmount = $transaction->amount;
111
+            return $this->convertToDefaultCurrency($transaction->amount, $transactionCurrency, $defaultCurrency);
103
         }
112
         }
104
 
113
 
105
-        $debitAccount = $transaction->type === TransactionType::Withdrawal ? $chartAccount : $bankAccount;
106
-        $creditAccount = $transaction->type === TransactionType::Withdrawal ? $bankAccount : $chartAccount;
114
+        return $transaction->amount;
115
+    }
107
 
116
 
108
-        $this->updateJournalEntriesForTransaction($debitEntry, $debitAccount, $convertedTransactionAmount);
109
-        $this->updateJournalEntriesForTransaction($creditEntry, $creditAccount, $convertedTransactionAmount);
117
+    private function convertToDefaultCurrency(string $amount, string $fromCurrency, string $toCurrency): string
118
+    {
119
+        $amountInCents = CurrencyConverter::prepareForAccessor($amount, $fromCurrency);
120
+
121
+        $convertedAmountInCents = CurrencyConverter::convertBalance($amountInCents, $fromCurrency, $toCurrency);
122
+
123
+        return CurrencyConverter::prepareForMutator($convertedAmountInCents, $toCurrency);
110
     }
124
     }
111
 
125
 
112
-    protected function hasRelevantChanges(Transaction $transaction): bool
126
+    private function hasRelevantChanges(Transaction $transaction): bool
113
     {
127
     {
114
         return $transaction->wasChanged(['amount', 'account_id', 'bank_account_id', 'type']);
128
         return $transaction->wasChanged(['amount', 'account_id', 'bank_account_id', 'type']);
115
     }
129
     }
116
 
130
 
117
-    protected function updateJournalEntriesForTransaction(JournalEntry $journalEntry, Account $account, string $convertedTransactionAmount): void
131
+    private function updateJournalEntriesForTransaction(JournalEntry $journalEntry, Account $account, string $convertedTransactionAmount): void
118
     {
132
     {
119
         DB::transaction(static function () use ($journalEntry, $account, $convertedTransactionAmount) {
133
         DB::transaction(static function () use ($journalEntry, $account, $convertedTransactionAmount) {
120
             $journalEntry->update([
134
             $journalEntry->update([
123
             ]);
137
             ]);
124
         });
138
         });
125
     }
139
     }
126
-
127
-    /**
128
-     * Handle the Transaction "deleting" event.
129
-     */
130
-    public function deleting(Transaction $transaction): void
131
-    {
132
-        DB::transaction(static function () use ($transaction) {
133
-            $transaction->journalEntries()->each(fn (JournalEntry $entry) => $entry->delete());
134
-        });
135
-    }
136
-
137
-    /**
138
-     * Handle the Transaction "deleted" event.
139
-     */
140
-    public function deleted(Transaction $transaction): void
141
-    {
142
-        //
143
-    }
144
-
145
-    /**
146
-     * Handle the Transaction "restored" event.
147
-     */
148
-    public function restored(Transaction $transaction): void
149
-    {
150
-        //
151
-    }
152
-
153
-    /**
154
-     * Handle the Transaction "force deleted" event.
155
-     */
156
-    public function forceDeleted(Transaction $transaction): void
157
-    {
158
-        //
159
-    }
160
 }
140
 }

+ 1
- 1
app/Providers/MacroServiceProvider.php View File

151
 
151
 
152
             $ratio = $newRate / $oldRate;
152
             $ratio = $newRate / $oldRate;
153
 
153
 
154
-            $convertedBalance = money($balanceInMajorUnits, $oldCurrency)->multiply($ratio)->getAmount();
154
+            $convertedBalance = bcmul($balanceInMajorUnits, $ratio, 2);
155
 
155
 
156
             return (int) round($convertedBalance);
156
             return (int) round($convertedBalance);
157
         });
157
         });

+ 13
- 0
app/Utilities/Currency/CurrencyConverter.php View File

4
 
4
 
5
 class CurrencyConverter
5
 class CurrencyConverter
6
 {
6
 {
7
+    public static function convertAndSet($newCurrency, $oldCurrency, $amount): ?string
8
+    {
9
+        if ($newCurrency === null || $oldCurrency === $newCurrency) {
10
+            return null;
11
+        }
12
+
13
+        $old_attr = currency($oldCurrency);
14
+        $new_attr = currency($newCurrency);
15
+        $temp_balance = str_replace([$old_attr->getThousandsSeparator(), $old_attr->getDecimalMark()], ['', '.'], $amount);
16
+
17
+        return number_format((float) $temp_balance, $new_attr->getPrecision(), $new_attr->getDecimalMark(), $new_attr->getThousandsSeparator());
18
+    }
19
+
7
     public static function convertBalance(int $balance, string $oldCurrency, string $newCurrency): int
20
     public static function convertBalance(int $balance, string $oldCurrency, string $newCurrency): int
8
     {
21
     {
9
         return money($balance, $oldCurrency)->swapAmountFor($newCurrency);
22
         return money($balance, $oldCurrency)->swapAmountFor($newCurrency);

+ 17
- 17
composer.lock View File

368
         },
368
         },
369
         {
369
         {
370
             "name": "awcodes/filament-table-repeater",
370
             "name": "awcodes/filament-table-repeater",
371
-            "version": "v3.0.2",
371
+            "version": "v3.0.3",
372
             "source": {
372
             "source": {
373
                 "type": "git",
373
                 "type": "git",
374
                 "url": "https://github.com/awcodes/filament-table-repeater.git",
374
                 "url": "https://github.com/awcodes/filament-table-repeater.git",
375
-                "reference": "1b185a51d2e5d2d33cdea02ca33b4796fdb0f2d2"
375
+                "reference": "1ebbcb8c04c0aaeac8771a6c2c62a8736f8eea9b"
376
             },
376
             },
377
             "dist": {
377
             "dist": {
378
                 "type": "zip",
378
                 "type": "zip",
379
-                "url": "https://api.github.com/repos/awcodes/filament-table-repeater/zipball/1b185a51d2e5d2d33cdea02ca33b4796fdb0f2d2",
380
-                "reference": "1b185a51d2e5d2d33cdea02ca33b4796fdb0f2d2",
379
+                "url": "https://api.github.com/repos/awcodes/filament-table-repeater/zipball/1ebbcb8c04c0aaeac8771a6c2c62a8736f8eea9b",
380
+                "reference": "1ebbcb8c04c0aaeac8771a6c2c62a8736f8eea9b",
381
                 "shasum": ""
381
                 "shasum": ""
382
             },
382
             },
383
             "require": {
383
             "require": {
431
             ],
431
             ],
432
             "support": {
432
             "support": {
433
                 "issues": "https://github.com/awcodes/filament-table-repeater/issues",
433
                 "issues": "https://github.com/awcodes/filament-table-repeater/issues",
434
-                "source": "https://github.com/awcodes/filament-table-repeater/tree/v3.0.2"
434
+                "source": "https://github.com/awcodes/filament-table-repeater/tree/v3.0.3"
435
             },
435
             },
436
             "funding": [
436
             "funding": [
437
                 {
437
                 {
439
                     "type": "github"
439
                     "type": "github"
440
                 }
440
                 }
441
             ],
441
             ],
442
-            "time": "2024-02-22T14:14:45+00:00"
442
+            "time": "2024-04-26T13:18:27+00:00"
443
         },
443
         },
444
         {
444
         {
445
             "name": "aws/aws-crt-php",
445
             "name": "aws/aws-crt-php",
497
         },
497
         },
498
         {
498
         {
499
             "name": "aws/aws-sdk-php",
499
             "name": "aws/aws-sdk-php",
500
-            "version": "3.305.3",
500
+            "version": "3.305.4",
501
             "source": {
501
             "source": {
502
                 "type": "git",
502
                 "type": "git",
503
                 "url": "https://github.com/aws/aws-sdk-php.git",
503
                 "url": "https://github.com/aws/aws-sdk-php.git",
504
-                "reference": "b190e24bd6568713436e1f13f9022bf28f491fc1"
504
+                "reference": "fc26a2ebf720e0b75a353d7e8fe206796671e00b"
505
             },
505
             },
506
             "dist": {
506
             "dist": {
507
                 "type": "zip",
507
                 "type": "zip",
508
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b190e24bd6568713436e1f13f9022bf28f491fc1",
509
-                "reference": "b190e24bd6568713436e1f13f9022bf28f491fc1",
508
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/fc26a2ebf720e0b75a353d7e8fe206796671e00b",
509
+                "reference": "fc26a2ebf720e0b75a353d7e8fe206796671e00b",
510
                 "shasum": ""
510
                 "shasum": ""
511
             },
511
             },
512
             "require": {
512
             "require": {
586
             "support": {
586
             "support": {
587
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
587
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
588
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
588
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
589
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.305.3"
589
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.305.4"
590
             },
590
             },
591
-            "time": "2024-04-25T18:07:15+00:00"
591
+            "time": "2024-04-26T18:06:31+00:00"
592
         },
592
         },
593
         {
593
         {
594
             "name": "aws/aws-sdk-php-laravel",
594
             "name": "aws/aws-sdk-php-laravel",
11785
         },
11785
         },
11786
         {
11786
         {
11787
             "name": "spatie/ignition",
11787
             "name": "spatie/ignition",
11788
-            "version": "1.13.2",
11788
+            "version": "1.14.0",
11789
             "source": {
11789
             "source": {
11790
                 "type": "git",
11790
                 "type": "git",
11791
                 "url": "https://github.com/spatie/ignition.git",
11791
                 "url": "https://github.com/spatie/ignition.git",
11792
-                "reference": "952798e239d9969e4e694b124c2cc222798dbb28"
11792
+                "reference": "80385994caed328f6f9c9952926932e65b9b774c"
11793
             },
11793
             },
11794
             "dist": {
11794
             "dist": {
11795
                 "type": "zip",
11795
                 "type": "zip",
11796
-                "url": "https://api.github.com/repos/spatie/ignition/zipball/952798e239d9969e4e694b124c2cc222798dbb28",
11797
-                "reference": "952798e239d9969e4e694b124c2cc222798dbb28",
11796
+                "url": "https://api.github.com/repos/spatie/ignition/zipball/80385994caed328f6f9c9952926932e65b9b774c",
11797
+                "reference": "80385994caed328f6f9c9952926932e65b9b774c",
11798
                 "shasum": ""
11798
                 "shasum": ""
11799
             },
11799
             },
11800
             "require": {
11800
             "require": {
11864
                     "type": "github"
11864
                     "type": "github"
11865
                 }
11865
                 }
11866
             ],
11866
             ],
11867
-            "time": "2024-04-16T08:49:17+00:00"
11867
+            "time": "2024-04-26T08:45:51+00:00"
11868
         },
11868
         },
11869
         {
11869
         {
11870
             "name": "spatie/laravel-ignition",
11870
             "name": "spatie/laravel-ignition",

+ 12
- 13
config/chart-of-accounts.php View File

7
                 'description' => 'The most liquid assets a company holds. This includes physical currency, bank balances, and short-term investments a company can quickly convert to cash.',
7
                 'description' => 'The most liquid assets a company holds. This includes physical currency, bank balances, and short-term investments a company can quickly convert to cash.',
8
                 'multi_currency' => true,
8
                 'multi_currency' => true,
9
                 'base_code' => '1000',
9
                 'base_code' => '1000',
10
+                'bank_account_type' => 'depository',
10
                 'accounts' => [
11
                 'accounts' => [
11
                     'Cash on Hand' => [
12
                     'Cash on Hand' => [
12
                         'description' => 'The amount of money held by the company in the form of cash.',
13
                         'description' => 'The amount of money held by the company in the form of cash.',
150
                 'multi_currency' => true,
151
                 'multi_currency' => true,
151
                 'base_code' => '3000',
152
                 'base_code' => '3000',
152
                 'accounts' => [
153
                 'accounts' => [
153
-                    'Owner\'s Equity' => [
154
-                        'description' => 'The owner\'s financial interest in the business, representing the residual interest in the assets of the business after deducting liabilities.',
154
+                    'Owner\'s Investment' => [
155
+                        'description' => 'The amount of money invested by the owner(s) or shareholders to start or expand the business.',
156
+                    ],
157
+                    'Owner\'s Drawings' => [
158
+                        'description' => 'The amount of money withdrawn by the owner(s) or shareholders from the business for personal use.',
155
                     ],
159
                     ],
156
                 ],
160
                 ],
157
             ],
161
             ],
158
-            'Retained Earnings' => [
162
+            'Retained Earnings: Profit' => [
159
                 'description' => 'Cumulative profits retained in the business and not distributed as dividends. Indicates the company\'s financial health and profit-generating ability.',
163
                 'description' => 'Cumulative profits retained in the business and not distributed as dividends. Indicates the company\'s financial health and profit-generating ability.',
160
                 'multi_currency' => false,
164
                 'multi_currency' => false,
161
                 'base_code' => '3100',
165
                 'base_code' => '3100',
162
-            ],
163
-            'Drawings' => [
164
-                'description' => 'The amount of money taken out of the business by the owner(s) for personal use.',
165
-                'multi_currency' => false,
166
-                'base_code' => '3200',
167
-            ],
168
-            'Equity Reserves and Adjustments' => [
169
-                'description' => 'Includes adjustments like revaluation reserves, foreign exchange adjustments, or other components of comprehensive income that affect the equity but are not classified under capital, retained earnings, or drawings.',
170
-                'multi_currency' => true,
171
-                'base_code' => '3300',
166
+                'accounts' => [
167
+                    'Owner\'s Equity' => [
168
+                        'description' => 'Owner\'s equity is what remains after you subtract business liabilities from business assets. In other words, it\'s what\'s left over for you if you sell all your assets and pay all your debts.',
169
+                    ],
170
+                ],
172
             ],
171
             ],
173
         ],
172
         ],
174
         'contra_equity' => [
173
         'contra_equity' => [

+ 6
- 6
package-lock.json View File

727
             }
727
             }
728
         },
728
         },
729
         "node_modules/@tailwindcss/typography": {
729
         "node_modules/@tailwindcss/typography": {
730
-            "version": "0.5.12",
731
-            "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.12.tgz",
732
-            "integrity": "sha512-CNwpBpconcP7ppxmuq3qvaCxiRWnbhANpY/ruH4L5qs2GCiVDJXde/pjj2HWPV1+Q4G9+V/etrwUYopdcjAlyg==",
730
+            "version": "0.5.13",
731
+            "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.13.tgz",
732
+            "integrity": "sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==",
733
             "dev": true,
733
             "dev": true,
734
             "dependencies": {
734
             "dependencies": {
735
                 "lodash.castarray": "^4.4.0",
735
                 "lodash.castarray": "^4.4.0",
1079
             "dev": true
1079
             "dev": true
1080
         },
1080
         },
1081
         "node_modules/electron-to-chromium": {
1081
         "node_modules/electron-to-chromium": {
1082
-            "version": "1.4.749",
1083
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.749.tgz",
1084
-            "integrity": "sha512-LRMMrM9ITOvue0PoBrvNIraVmuDbJV5QC9ierz/z5VilMdPOVMjOtpICNld3PuXuTZ3CHH/UPxX9gHhAPwi+0Q==",
1082
+            "version": "1.4.750",
1083
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.750.tgz",
1084
+            "integrity": "sha512-9ItEpeu15hW5m8jKdriL+BQrgwDTXEL9pn4SkillWFu73ZNNNQ2BKKLS+ZHv2vC9UkNhosAeyfxOf/5OSeTCPA==",
1085
             "dev": true
1085
             "dev": true
1086
         },
1086
         },
1087
         "node_modules/emoji-regex": {
1087
         "node_modules/emoji-regex": {

Loading…
Cancel
Save