選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

InvoiceTotalViewModel.php 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. <?php
  2. namespace App\View\Models;
  3. use App\Enums\Accounting\AdjustmentComputation;
  4. use App\Enums\Accounting\DocumentDiscountMethod;
  5. use App\Models\Accounting\Adjustment;
  6. use App\Models\Accounting\Invoice;
  7. use App\Utilities\Currency\CurrencyAccessor;
  8. use App\Utilities\Currency\CurrencyConverter;
  9. use App\Utilities\RateCalculator;
  10. class InvoiceTotalViewModel
  11. {
  12. public function __construct(
  13. public ?Invoice $invoice,
  14. public ?array $data = null
  15. ) {}
  16. public function buildViewData(): array
  17. {
  18. $currencyCode = $this->data['currency_code'] ?? CurrencyAccessor::getDefaultCurrency();
  19. $defaultCurrencyCode = CurrencyAccessor::getDefaultCurrency();
  20. $lineItems = collect($this->data['lineItems'] ?? []);
  21. $subtotalInCents = $lineItems->sum(static function ($item) use ($currencyCode) {
  22. $quantity = max((float) ($item['quantity'] ?? 0), 0);
  23. $unitPrice = max((float) ($item['unit_price'] ?? 0), 0);
  24. $subtotal = $quantity * $unitPrice;
  25. return CurrencyConverter::convertToCents($subtotal, $currencyCode);
  26. });
  27. $taxTotalInCents = $lineItems->reduce(function ($carry, $item) use ($currencyCode) {
  28. $quantity = max((float) ($item['quantity'] ?? 0), 0);
  29. $unitPrice = max((float) ($item['unit_price'] ?? 0), 0);
  30. $salesTaxes = $item['salesTaxes'] ?? [];
  31. $lineTotal = $quantity * $unitPrice;
  32. $lineTotalInCents = CurrencyConverter::convertToCents($lineTotal, $currencyCode);
  33. $taxAmountInCents = Adjustment::whereIn('id', $salesTaxes)
  34. ->get()
  35. ->sum(function (Adjustment $adjustment) use ($lineTotalInCents) {
  36. if ($adjustment->computation->isPercentage()) {
  37. return RateCalculator::calculatePercentage($lineTotalInCents, $adjustment->getRawOriginal('rate'));
  38. } else {
  39. return $adjustment->getRawOriginal('rate');
  40. }
  41. });
  42. return $carry + $taxAmountInCents;
  43. }, 0);
  44. // Calculate discount based on method
  45. $discountMethod = DocumentDiscountMethod::parse($this->data['discount_method']) ?? DocumentDiscountMethod::PerLineItem;
  46. if ($discountMethod->isPerLineItem()) {
  47. $discountTotalInCents = $lineItems->reduce(function ($carry, $item) use ($currencyCode) {
  48. $quantity = max((float) ($item['quantity'] ?? 0), 0);
  49. $unitPrice = max((float) ($item['unit_price'] ?? 0), 0);
  50. $salesDiscounts = $item['salesDiscounts'] ?? [];
  51. $lineTotalInCents = CurrencyConverter::convertToCents($quantity * $unitPrice, $currencyCode);
  52. $discountAmountInCents = Adjustment::whereIn('id', $salesDiscounts)
  53. ->get()
  54. ->sum(function (Adjustment $adjustment) use ($lineTotalInCents) {
  55. if ($adjustment->computation->isPercentage()) {
  56. return RateCalculator::calculatePercentage($lineTotalInCents, $adjustment->getRawOriginal('rate'));
  57. } else {
  58. return $adjustment->getRawOriginal('rate');
  59. }
  60. });
  61. return $carry + $discountAmountInCents;
  62. }, 0);
  63. } else {
  64. $discountComputation = AdjustmentComputation::parse($this->data['discount_computation']) ?? AdjustmentComputation::Percentage;
  65. $discountRate = $this->data['discount_rate'] ?? '0';
  66. if ($discountComputation->isPercentage()) {
  67. $scaledDiscountRate = RateCalculator::parseLocalizedRate($discountRate);
  68. $discountTotalInCents = RateCalculator::calculatePercentage($subtotalInCents, $scaledDiscountRate);
  69. } else {
  70. $discountTotalInCents = CurrencyConverter::convertToCents($discountRate, $currencyCode);
  71. }
  72. }
  73. $grandTotalInCents = $subtotalInCents + ($taxTotalInCents - $discountTotalInCents);
  74. $conversionMessage = null;
  75. if ($currencyCode !== $defaultCurrencyCode) {
  76. $rate = currency($currencyCode)->getRate();
  77. $convertedTotalInCents = CurrencyConverter::convertBalance($grandTotalInCents, $currencyCode, $defaultCurrencyCode);
  78. $conversionMessage = sprintf(
  79. 'Currency conversion: %s (%s) at an exchange rate of %s',
  80. CurrencyConverter::formatCentsToMoney($convertedTotalInCents, $defaultCurrencyCode),
  81. $defaultCurrencyCode,
  82. $rate
  83. );
  84. }
  85. return [
  86. 'subtotal' => CurrencyConverter::formatCentsToMoney($subtotalInCents, $currencyCode),
  87. 'taxTotal' => CurrencyConverter::formatCentsToMoney($taxTotalInCents, $currencyCode),
  88. 'discountTotal' => CurrencyConverter::formatCentsToMoney($discountTotalInCents, $currencyCode),
  89. 'grandTotal' => CurrencyConverter::formatCentsToMoney($grandTotalInCents, $currencyCode),
  90. 'conversionMessage' => $conversionMessage,
  91. ];
  92. }
  93. }