Andrew Wallo 9 月之前
父節點
當前提交
8acaeab07b
共有 2 個文件被更改,包括 69 次插入19 次删除
  1. 8
    7
      app/Filament/Company/Resources/Purchases/BillResource.php
  2. 61
    12
      app/Models/Accounting/Bill.php

+ 8
- 7
app/Filament/Company/Resources/Purchases/BillResource.php 查看文件

@@ -324,15 +324,16 @@ class BillResource extends Resource
324 324
                             Forms\Components\TextInput::make('amount')
325 325
                                 ->label('Amount')
326 326
                                 ->required()
327
-                                ->money()
327
+                                ->money(fn (Bill $record) => $record->currency_code)
328 328
                                 ->live(onBlur: true)
329 329
                                 ->helperText(function (Bill $record, $state) {
330
-                                    if (! CurrencyConverter::isValidAmount($state)) {
330
+                                    $billCurrency = $record->currency_code;
331
+                                    if (! CurrencyConverter::isValidAmount($state, $billCurrency)) {
331 332
                                         return null;
332 333
                                     }
333 334
 
334 335
                                     $amountDue = $record->getRawOriginal('amount_due');
335
-                                    $amount = CurrencyConverter::convertToCents($state);
336
+                                    $amount = CurrencyConverter::convertToCents($state, $billCurrency);
336 337
 
337 338
                                     if ($amount <= 0) {
338 339
                                         return 'Please enter a valid positive amount';
@@ -341,14 +342,14 @@ class BillResource extends Resource
341 342
                                     $newAmountDue = $amountDue - $amount;
342 343
 
343 344
                                     return match (true) {
344
-                                        $newAmountDue > 0 => 'Amount due after payment will be ' . CurrencyConverter::formatCentsToMoney($newAmountDue),
345
+                                        $newAmountDue > 0 => 'Amount due after payment will be ' . CurrencyConverter::formatCentsToMoney($newAmountDue, $billCurrency),
345 346
                                         $newAmountDue === 0 => 'Bill will be fully paid',
346
-                                        default => 'Amount exceeds bill total by ' . CurrencyConverter::formatCentsToMoney(abs($newAmountDue)),
347
+                                        default => 'Amount exceeds bill total by ' . CurrencyConverter::formatCentsToMoney(abs($newAmountDue), $billCurrency),
347 348
                                     };
348 349
                                 })
349 350
                                 ->rules([
350
-                                    static fn (): Closure => static function (string $attribute, $value, Closure $fail) {
351
-                                        if (! CurrencyConverter::isValidAmount($value)) {
351
+                                    static fn (Bill $record): Closure => static function (string $attribute, $value, Closure $fail) use ($record) {
352
+                                        if (! CurrencyConverter::isValidAmount($value, $record->currency_code)) {
352 353
                                             $fail('Please enter a valid amount');
353 354
                                         }
354 355
                                     },

+ 61
- 12
app/Models/Accounting/Bill.php 查看文件

@@ -12,9 +12,11 @@ use App\Enums\Accounting\DocumentDiscountMethod;
12 12
 use App\Enums\Accounting\JournalEntryType;
13 13
 use App\Enums\Accounting\TransactionType;
14 14
 use App\Filament\Company\Resources\Purchases\BillResource;
15
+use App\Models\Banking\BankAccount;
15 16
 use App\Models\Common\Vendor;
16 17
 use App\Models\Setting\Currency;
17 18
 use App\Observers\BillObserver;
19
+use App\Utilities\Currency\CurrencyAccessor;
18 20
 use App\Utilities\Currency\CurrencyConverter;
19 21
 use Filament\Actions\MountableAction;
20 22
 use Filament\Actions\ReplicateAction;
@@ -194,13 +196,35 @@ class Bill extends Model
194 196
         $transactionType = TransactionType::Withdrawal;
195 197
         $transactionDescription = "Bill #{$this->bill_number}: Payment to {$this->vendor->name}";
196 198
 
197
-        // Create transaction
199
+        // Add multi-currency handling
200
+        $bankAccount = BankAccount::findOrFail($data['bank_account_id']);
201
+        $bankAccountCurrency = $bankAccount->account->currency_code ?? CurrencyAccessor::getDefaultCurrency();
202
+
203
+        $billCurrency = $this->currency_code;
204
+        $requiresConversion = $billCurrency !== $bankAccountCurrency;
205
+
206
+        if ($requiresConversion) {
207
+            $amountInBillCurrencyCents = CurrencyConverter::convertToCents($data['amount'], $billCurrency);
208
+            $amountInBankCurrencyCents = CurrencyConverter::convertBalance(
209
+                $amountInBillCurrencyCents,
210
+                $billCurrency,
211
+                $bankAccountCurrency
212
+            );
213
+            $formattedAmountForBankCurrency = CurrencyConverter::convertCentsToFormatSimple(
214
+                $amountInBankCurrencyCents,
215
+                $bankAccountCurrency
216
+            );
217
+        } else {
218
+            $formattedAmountForBankCurrency = $data['amount']; // Already in simple format
219
+        }
220
+
221
+        // Create transaction with converted amount
198 222
         $this->transactions()->create([
199 223
             'company_id' => $this->company_id,
200 224
             'type' => $transactionType,
201 225
             'is_payment' => true,
202 226
             'posted_at' => $data['posted_at'],
203
-            'amount' => $data['amount'],
227
+            'amount' => $formattedAmountForBankCurrency,
204 228
             'payment_method' => $data['payment_method'],
205 229
             'bank_account_id' => $data['bank_account_id'],
206 230
             'account_id' => Account::getAccountsPayableAccount()->id,
@@ -213,11 +237,13 @@ class Bill extends Model
213 237
     {
214 238
         $postedAt ??= $this->date;
215 239
 
240
+        $total = $this->formatAmountToDefaultCurrency($this->getRawOriginal('total'));
241
+
216 242
         $transaction = $this->transactions()->create([
217 243
             'company_id' => $this->company_id,
218 244
             'type' => TransactionType::Journal,
219 245
             'posted_at' => $postedAt,
220
-            'amount' => $this->total,
246
+            'amount' => $total,
221 247
             'description' => 'Bill Creation for Bill #' . $this->bill_number,
222 248
         ]);
223 249
 
@@ -227,32 +253,36 @@ class Bill extends Model
227 253
             'company_id' => $this->company_id,
228 254
             'type' => JournalEntryType::Credit,
229 255
             'account_id' => Account::getAccountsPayableAccount()->id,
230
-            'amount' => $this->total,
256
+            'amount' => $total,
231 257
             'description' => $baseDescription,
232 258
         ]);
233 259
 
234
-        $totalLineItemSubtotal = (int) $this->lineItems()->sum('subtotal');
235
-        $billDiscountTotalCents = (int) $this->getRawOriginal('discount_total');
260
+        $totalLineItemSubtotalCents = $this->convertAmountToDefaultCurrency((int) $this->lineItems()->sum('subtotal'));
261
+        $billDiscountTotalCents = $this->convertAmountToDefaultCurrency((int) $this->getRawOriginal('discount_total'));
236 262
         $remainingDiscountCents = $billDiscountTotalCents;
237 263
 
238 264
         foreach ($this->lineItems as $index => $lineItem) {
239 265
             $lineItemDescription = "{$baseDescription} › {$lineItem->offering->name}";
240 266
 
267
+            $lineItemSubtotal = $this->formatAmountToDefaultCurrency($lineItem->getRawOriginal('subtotal'));
268
+
241 269
             $transaction->journalEntries()->create([
242 270
                 'company_id' => $this->company_id,
243 271
                 'type' => JournalEntryType::Debit,
244 272
                 'account_id' => $lineItem->offering->expense_account_id,
245
-                'amount' => $lineItem->subtotal,
273
+                'amount' => $lineItemSubtotal,
246 274
                 'description' => $lineItemDescription,
247 275
             ]);
248 276
 
249 277
             foreach ($lineItem->adjustments as $adjustment) {
278
+                $adjustmentAmount = $this->formatAmountToDefaultCurrency($lineItem->calculateAdjustmentTotalAmount($adjustment));
279
+
250 280
                 if ($adjustment->isNonRecoverablePurchaseTax()) {
251 281
                     $transaction->journalEntries()->create([
252 282
                         'company_id' => $this->company_id,
253 283
                         'type' => JournalEntryType::Debit,
254 284
                         'account_id' => $lineItem->offering->expense_account_id,
255
-                        'amount' => $lineItem->calculateAdjustmentTotal($adjustment)->getAmount(),
285
+                        'amount' => $adjustmentAmount,
256 286
                         'description' => "{$lineItemDescription} ({$adjustment->name})",
257 287
                     ]);
258 288
                 } elseif ($adjustment->account_id) {
@@ -260,20 +290,20 @@ class Bill extends Model
260 290
                         'company_id' => $this->company_id,
261 291
                         'type' => $adjustment->category->isDiscount() ? JournalEntryType::Credit : JournalEntryType::Debit,
262 292
                         'account_id' => $adjustment->account_id,
263
-                        'amount' => $lineItem->calculateAdjustmentTotal($adjustment)->getAmount(),
293
+                        'amount' => $adjustmentAmount,
264 294
                         'description' => $lineItemDescription,
265 295
                     ]);
266 296
                 }
267 297
             }
268 298
 
269
-            if ($this->discount_method->isPerDocument() && $totalLineItemSubtotal > 0) {
270
-                $lineItemSubtotalCents = (int) $lineItem->getRawOriginal('subtotal');
299
+            if ($this->discount_method->isPerDocument() && $totalLineItemSubtotalCents > 0) {
300
+                $lineItemSubtotalCents = $this->convertAmountToDefaultCurrency((int) $lineItem->getRawOriginal('subtotal'));
271 301
 
272 302
                 if ($index === $this->lineItems->count() - 1) {
273 303
                     $lineItemDiscount = $remainingDiscountCents;
274 304
                 } else {
275 305
                     $lineItemDiscount = (int) round(
276
-                        ($lineItemSubtotalCents / $totalLineItemSubtotal) * $billDiscountTotalCents
306
+                        ($lineItemSubtotalCents / $totalLineItemSubtotalCents) * $billDiscountTotalCents
277 307
                     );
278 308
                     $remainingDiscountCents -= $lineItemDiscount;
279 309
                 }
@@ -302,6 +332,25 @@ class Bill extends Model
302 332
         $this->createInitialTransaction();
303 333
     }
304 334
 
335
+    public function convertAmountToDefaultCurrency(int $amountCents): int
336
+    {
337
+        $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
338
+        $needsConversion = $this->currency_code !== $defaultCurrency;
339
+
340
+        if ($needsConversion) {
341
+            return CurrencyConverter::convertBalance($amountCents, $this->currency_code, $defaultCurrency);
342
+        }
343
+
344
+        return $amountCents;
345
+    }
346
+
347
+    public function formatAmountToDefaultCurrency(int $amountCents): string
348
+    {
349
+        $convertedCents = $this->convertAmountToDefaultCurrency($amountCents);
350
+
351
+        return CurrencyConverter::convertCentsToFormatSimple($convertedCents);
352
+    }
353
+
305 354
     public static function getReplicateAction(string $action = ReplicateAction::class): MountableAction
306 355
     {
307 356
         return $action::make()

Loading…
取消
儲存