浏览代码

feat: exchange rate fluctuation adjustments pt.2

3.x
wallo 1年前
父节点
当前提交
fac97c527f

+ 0
- 4
app/Casts/MoneyCast.php 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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": {

正在加载...
取消
保存