|  | @@ -382,78 +382,124 @@ class Invoice extends Document
 | 
		
	
		
			
			| 382 | 382 |  
 | 
		
	
		
			
			| 383 | 383 |      public function createApprovalTransaction(): void
 | 
		
	
		
			
			| 384 | 384 |      {
 | 
		
	
		
			
			| 385 |  | -        $total = $this->formatAmountToDefaultCurrency($this->getRawOriginal('total'));
 | 
		
	
		
			
			| 386 |  | -
 | 
		
	
		
			
			| 387 |  | -        $transaction = $this->transactions()->create([
 | 
		
	
		
			
			| 388 |  | -            'company_id' => $this->company_id,
 | 
		
	
		
			
			| 389 |  | -            'type' => TransactionType::Journal,
 | 
		
	
		
			
			| 390 |  | -            'posted_at' => $this->date,
 | 
		
	
		
			
			| 391 |  | -            'amount' => $total,
 | 
		
	
		
			
			| 392 |  | -            'description' => 'Invoice Approval for Invoice #' . $this->invoice_number,
 | 
		
	
		
			
			| 393 |  | -        ]);
 | 
		
	
		
			
			| 394 |  | -
 | 
		
	
		
			
			| 395 | 385 |          $baseDescription = "{$this->client->name}: Invoice #{$this->invoice_number}";
 | 
		
	
		
			
			| 396 | 386 |  
 | 
		
	
		
			
			| 397 |  | -        $transaction->journalEntries()->create([
 | 
		
	
		
			
			| 398 |  | -            'company_id' => $this->company_id,
 | 
		
	
		
			
			|  | 387 | +        $journalEntryData = [];
 | 
		
	
		
			
			|  | 388 | +
 | 
		
	
		
			
			|  | 389 | +        $totalInInvoiceCurrency = $this->getRawOriginal('total');
 | 
		
	
		
			
			|  | 390 | +        $journalEntryData[] = [
 | 
		
	
		
			
			| 399 | 391 |              'type' => JournalEntryType::Debit,
 | 
		
	
		
			
			| 400 | 392 |              'account_id' => Account::getAccountsReceivableAccount($this->company_id)->id,
 | 
		
	
		
			
			| 401 |  | -            'amount' => $total,
 | 
		
	
		
			
			|  | 393 | +            'amount_in_invoice_currency' => $totalInInvoiceCurrency,
 | 
		
	
		
			
			| 402 | 394 |              'description' => $baseDescription,
 | 
		
	
		
			
			| 403 |  | -        ]);
 | 
		
	
		
			
			|  | 395 | +        ];
 | 
		
	
		
			
			| 404 | 396 |  
 | 
		
	
		
			
			| 405 |  | -        $totalLineItemSubtotalCents = $this->convertAmountToDefaultCurrency((int) $this->lineItems()->sum('subtotal'));
 | 
		
	
		
			
			| 406 |  | -        $invoiceDiscountTotalCents = $this->convertAmountToDefaultCurrency((int) $this->getRawOriginal('discount_total'));
 | 
		
	
		
			
			| 407 |  | -        $remainingDiscountCents = $invoiceDiscountTotalCents;
 | 
		
	
		
			
			|  | 397 | +        $totalLineItemSubtotalInInvoiceCurrency = (int) $this->lineItems()->sum('subtotal');
 | 
		
	
		
			
			|  | 398 | +        $invoiceDiscountTotalInInvoiceCurrency = (int) $this->getRawOriginal('discount_total');
 | 
		
	
		
			
			|  | 399 | +        $remainingDiscountInInvoiceCurrency = $invoiceDiscountTotalInInvoiceCurrency;
 | 
		
	
		
			
			| 408 | 400 |  
 | 
		
	
		
			
			| 409 | 401 |          foreach ($this->lineItems as $index => $lineItem) {
 | 
		
	
		
			
			| 410 | 402 |              $lineItemDescription = "{$baseDescription} › {$lineItem->offering->name}";
 | 
		
	
		
			
			|  | 403 | +            $lineItemSubtotalInInvoiceCurrency = $lineItem->getRawOriginal('subtotal');
 | 
		
	
		
			
			| 411 | 404 |  
 | 
		
	
		
			
			| 412 |  | -            $lineItemSubtotal = $this->formatAmountToDefaultCurrency($lineItem->getRawOriginal('subtotal'));
 | 
		
	
		
			
			| 413 |  | -
 | 
		
	
		
			
			| 414 |  | -            $transaction->journalEntries()->create([
 | 
		
	
		
			
			| 415 |  | -                'company_id' => $this->company_id,
 | 
		
	
		
			
			|  | 405 | +            $journalEntryData[] = [
 | 
		
	
		
			
			| 416 | 406 |                  'type' => JournalEntryType::Credit,
 | 
		
	
		
			
			| 417 | 407 |                  'account_id' => $lineItem->offering->income_account_id,
 | 
		
	
		
			
			| 418 |  | -                'amount' => $lineItemSubtotal,
 | 
		
	
		
			
			|  | 408 | +                'amount_in_invoice_currency' => $lineItemSubtotalInInvoiceCurrency,
 | 
		
	
		
			
			| 419 | 409 |                  'description' => $lineItemDescription,
 | 
		
	
		
			
			| 420 |  | -            ]);
 | 
		
	
		
			
			|  | 410 | +            ];
 | 
		
	
		
			
			| 421 | 411 |  
 | 
		
	
		
			
			| 422 | 412 |              foreach ($lineItem->adjustments as $adjustment) {
 | 
		
	
		
			
			| 423 |  | -                $adjustmentAmount = $this->formatAmountToDefaultCurrency($lineItem->calculateAdjustmentTotalAmount($adjustment));
 | 
		
	
		
			
			|  | 413 | +                $adjustmentAmountInInvoiceCurrency = $lineItem->calculateAdjustmentTotalAmount($adjustment);
 | 
		
	
		
			
			| 424 | 414 |  
 | 
		
	
		
			
			| 425 |  | -                $transaction->journalEntries()->create([
 | 
		
	
		
			
			| 426 |  | -                    'company_id' => $this->company_id,
 | 
		
	
		
			
			|  | 415 | +                $journalEntryData[] = [
 | 
		
	
		
			
			| 427 | 416 |                      'type' => $adjustment->category->isDiscount() ? JournalEntryType::Debit : JournalEntryType::Credit,
 | 
		
	
		
			
			| 428 | 417 |                      'account_id' => $adjustment->account_id,
 | 
		
	
		
			
			| 429 |  | -                    'amount' => $adjustmentAmount,
 | 
		
	
		
			
			|  | 418 | +                    'amount_in_invoice_currency' => $adjustmentAmountInInvoiceCurrency,
 | 
		
	
		
			
			| 430 | 419 |                      'description' => $lineItemDescription,
 | 
		
	
		
			
			| 431 |  | -                ]);
 | 
		
	
		
			
			|  | 420 | +                ];
 | 
		
	
		
			
			| 432 | 421 |              }
 | 
		
	
		
			
			| 433 | 422 |  
 | 
		
	
		
			
			| 434 |  | -            if ($this->discount_method->isPerDocument() && $totalLineItemSubtotalCents > 0) {
 | 
		
	
		
			
			| 435 |  | -                $lineItemSubtotalCents = $this->convertAmountToDefaultCurrency((int) $lineItem->getRawOriginal('subtotal'));
 | 
		
	
		
			
			|  | 423 | +            // Handle per-document discount allocation
 | 
		
	
		
			
			|  | 424 | +            if ($this->discount_method->isPerDocument() && $totalLineItemSubtotalInInvoiceCurrency > 0) {
 | 
		
	
		
			
			|  | 425 | +                $lineItemSubtotalInInvoiceCurrency = (int) $lineItem->getRawOriginal('subtotal');
 | 
		
	
		
			
			| 436 | 426 |  
 | 
		
	
		
			
			| 437 | 427 |                  if ($index === $this->lineItems->count() - 1) {
 | 
		
	
		
			
			| 438 |  | -                    $lineItemDiscount = $remainingDiscountCents;
 | 
		
	
		
			
			|  | 428 | +                    $lineItemDiscountInInvoiceCurrency = $remainingDiscountInInvoiceCurrency;
 | 
		
	
		
			
			| 439 | 429 |                  } else {
 | 
		
	
		
			
			| 440 |  | -                    $lineItemDiscount = (int) round(
 | 
		
	
		
			
			| 441 |  | -                        ($lineItemSubtotalCents / $totalLineItemSubtotalCents) * $invoiceDiscountTotalCents
 | 
		
	
		
			
			|  | 430 | +                    $lineItemDiscountInInvoiceCurrency = (int) round(
 | 
		
	
		
			
			|  | 431 | +                        ($lineItemSubtotalInInvoiceCurrency / $totalLineItemSubtotalInInvoiceCurrency) * $invoiceDiscountTotalInInvoiceCurrency
 | 
		
	
		
			
			| 442 | 432 |                      );
 | 
		
	
		
			
			| 443 |  | -                    $remainingDiscountCents -= $lineItemDiscount;
 | 
		
	
		
			
			|  | 433 | +                    $remainingDiscountInInvoiceCurrency -= $lineItemDiscountInInvoiceCurrency;
 | 
		
	
		
			
			| 444 | 434 |                  }
 | 
		
	
		
			
			| 445 | 435 |  
 | 
		
	
		
			
			| 446 |  | -                if ($lineItemDiscount > 0) {
 | 
		
	
		
			
			| 447 |  | -                    $transaction->journalEntries()->create([
 | 
		
	
		
			
			| 448 |  | -                        'company_id' => $this->company_id,
 | 
		
	
		
			
			|  | 436 | +                if ($lineItemDiscountInInvoiceCurrency > 0) {
 | 
		
	
		
			
			|  | 437 | +                    $journalEntryData[] = [
 | 
		
	
		
			
			| 449 | 438 |                          'type' => JournalEntryType::Debit,
 | 
		
	
		
			
			| 450 | 439 |                          'account_id' => Account::getSalesDiscountAccount($this->company_id)->id,
 | 
		
	
		
			
			| 451 |  | -                        'amount' => $lineItemDiscount,
 | 
		
	
		
			
			|  | 440 | +                        'amount_in_invoice_currency' => $lineItemDiscountInInvoiceCurrency,
 | 
		
	
		
			
			| 452 | 441 |                          'description' => "{$lineItemDescription} (Proportional Discount)",
 | 
		
	
		
			
			| 453 |  | -                    ]);
 | 
		
	
		
			
			|  | 442 | +                    ];
 | 
		
	
		
			
			| 454 | 443 |                  }
 | 
		
	
		
			
			| 455 | 444 |              }
 | 
		
	
		
			
			| 456 | 445 |          }
 | 
		
	
		
			
			|  | 446 | +
 | 
		
	
		
			
			|  | 447 | +        // Convert amounts to default currency
 | 
		
	
		
			
			|  | 448 | +        $totalDebitsInDefaultCurrency = 0;
 | 
		
	
		
			
			|  | 449 | +        $totalCreditsInDefaultCurrency = 0;
 | 
		
	
		
			
			|  | 450 | +
 | 
		
	
		
			
			|  | 451 | +        foreach ($journalEntryData as &$entry) {
 | 
		
	
		
			
			|  | 452 | +            $entry['amount_in_default_currency'] = $this->formatAmountToDefaultCurrency($entry['amount_in_invoice_currency']);
 | 
		
	
		
			
			|  | 453 | +
 | 
		
	
		
			
			|  | 454 | +            if ($entry['type'] === JournalEntryType::Debit) {
 | 
		
	
		
			
			|  | 455 | +                $totalDebitsInDefaultCurrency += $entry['amount_in_default_currency'];
 | 
		
	
		
			
			|  | 456 | +            } else {
 | 
		
	
		
			
			|  | 457 | +                $totalCreditsInDefaultCurrency += $entry['amount_in_default_currency'];
 | 
		
	
		
			
			|  | 458 | +            }
 | 
		
	
		
			
			|  | 459 | +        }
 | 
		
	
		
			
			|  | 460 | +
 | 
		
	
		
			
			|  | 461 | +        unset($entry);
 | 
		
	
		
			
			|  | 462 | +
 | 
		
	
		
			
			|  | 463 | +        // Handle currency conversion imbalance
 | 
		
	
		
			
			|  | 464 | +        $imbalance = $totalDebitsInDefaultCurrency - $totalCreditsInDefaultCurrency;
 | 
		
	
		
			
			|  | 465 | +        if ($imbalance !== 0) {
 | 
		
	
		
			
			|  | 466 | +            $targetType = $imbalance > 0 ? JournalEntryType::Credit : JournalEntryType::Debit;
 | 
		
	
		
			
			|  | 467 | +            $adjustmentAmount = abs($imbalance);
 | 
		
	
		
			
			|  | 468 | +
 | 
		
	
		
			
			|  | 469 | +            // Find last entry of target type and adjust it
 | 
		
	
		
			
			|  | 470 | +            $lastKey = array_key_last(array_filter($journalEntryData, fn ($entry) => $entry['type'] === $targetType, ARRAY_FILTER_USE_BOTH));
 | 
		
	
		
			
			|  | 471 | +            $journalEntryData[$lastKey]['amount_in_default_currency'] += $adjustmentAmount;
 | 
		
	
		
			
			|  | 472 | +
 | 
		
	
		
			
			|  | 473 | +            if ($targetType === JournalEntryType::Debit) {
 | 
		
	
		
			
			|  | 474 | +                $totalDebitsInDefaultCurrency += $adjustmentAmount;
 | 
		
	
		
			
			|  | 475 | +            } else {
 | 
		
	
		
			
			|  | 476 | +                $totalCreditsInDefaultCurrency += $adjustmentAmount;
 | 
		
	
		
			
			|  | 477 | +            }
 | 
		
	
		
			
			|  | 478 | +        }
 | 
		
	
		
			
			|  | 479 | +
 | 
		
	
		
			
			|  | 480 | +        if ($totalDebitsInDefaultCurrency !== $totalCreditsInDefaultCurrency) {
 | 
		
	
		
			
			|  | 481 | +            throw new \Exception('Journal entries do not balance. Debits: ' . $totalDebitsInDefaultCurrency . ', Credits: ' . $totalCreditsInDefaultCurrency);
 | 
		
	
		
			
			|  | 482 | +        }
 | 
		
	
		
			
			|  | 483 | +
 | 
		
	
		
			
			|  | 484 | +        // Create the transaction using the sum of debits
 | 
		
	
		
			
			|  | 485 | +        $transaction = $this->transactions()->create([
 | 
		
	
		
			
			|  | 486 | +            'company_id' => $this->company_id,
 | 
		
	
		
			
			|  | 487 | +            'type' => TransactionType::Journal,
 | 
		
	
		
			
			|  | 488 | +            'posted_at' => $this->date,
 | 
		
	
		
			
			|  | 489 | +            'amount' => $totalDebitsInDefaultCurrency,
 | 
		
	
		
			
			|  | 490 | +            'description' => 'Invoice Approval for Invoice #' . $this->invoice_number,
 | 
		
	
		
			
			|  | 491 | +        ]);
 | 
		
	
		
			
			|  | 492 | +
 | 
		
	
		
			
			|  | 493 | +        // Create all journal entries
 | 
		
	
		
			
			|  | 494 | +        foreach ($journalEntryData as $entry) {
 | 
		
	
		
			
			|  | 495 | +            $transaction->journalEntries()->create([
 | 
		
	
		
			
			|  | 496 | +                'company_id' => $this->company_id,
 | 
		
	
		
			
			|  | 497 | +                'type' => $entry['type'],
 | 
		
	
		
			
			|  | 498 | +                'account_id' => $entry['account_id'],
 | 
		
	
		
			
			|  | 499 | +                'amount' => $entry['amount_in_default_currency'],
 | 
		
	
		
			
			|  | 500 | +                'description' => $entry['description'],
 | 
		
	
		
			
			|  | 501 | +            ]);
 | 
		
	
		
			
			|  | 502 | +        }
 | 
		
	
		
			
			| 457 | 503 |      }
 | 
		
	
		
			
			| 458 | 504 |  
 | 
		
	
		
			
			| 459 | 505 |      public function updateApprovalTransaction(): void
 |