您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

ManagesLineItems.php 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. <?php
  2. namespace App\Concerns;
  3. use App\Enums\Accounting\AdjustmentComputation;
  4. use App\Models\Accounting\DocumentLineItem;
  5. use App\Models\Accounting\Invoice;
  6. use App\Utilities\Currency\CurrencyConverter;
  7. use Illuminate\Support\Collection;
  8. trait ManagesLineItems
  9. {
  10. protected function handleLineItems(Invoice $record, Collection $lineItems): void
  11. {
  12. foreach ($lineItems as $itemData) {
  13. $lineItem = isset($itemData['id'])
  14. ? $record->lineItems->find($itemData['id'])
  15. : $record->lineItems()->make();
  16. $lineItem->fill([
  17. 'offering_id' => $itemData['offering_id'],
  18. 'description' => $itemData['description'],
  19. 'quantity' => $itemData['quantity'],
  20. 'unit_price' => $itemData['unit_price'],
  21. ]);
  22. if (! $lineItem->exists) {
  23. $lineItem->documentable()->associate($record);
  24. }
  25. $lineItem->save();
  26. $this->handleLineItemAdjustments($lineItem, $itemData, $record->discount_method);
  27. $this->updateLineItemTotals($lineItem, $record->discount_method);
  28. }
  29. }
  30. protected function deleteRemovedLineItems(Invoice $record, Collection $lineItems): void
  31. {
  32. $existingLineItemIds = $record->lineItems->pluck('id');
  33. $updatedLineItemIds = $lineItems->pluck('id')->filter();
  34. $lineItemsToDelete = $existingLineItemIds->diff($updatedLineItemIds);
  35. if ($lineItemsToDelete->isNotEmpty()) {
  36. $record
  37. ->lineItems()
  38. ->whereIn('id', $lineItemsToDelete)
  39. ->each(fn (DocumentLineItem $lineItem) => $lineItem->delete());
  40. }
  41. }
  42. protected function handleLineItemAdjustments(DocumentLineItem $lineItem, array $itemData, string $discountMethod): void
  43. {
  44. $adjustmentIds = collect($itemData['salesTaxes'] ?? [])
  45. ->merge($discountMethod === 'line_items' ? ($itemData['salesDiscounts'] ?? []) : [])
  46. ->filter()
  47. ->unique();
  48. $lineItem->adjustments()->sync($adjustmentIds);
  49. $lineItem->refresh();
  50. }
  51. protected function updateLineItemTotals(DocumentLineItem $lineItem, string $discountMethod): void
  52. {
  53. $lineItem->updateQuietly([
  54. 'tax_total' => $lineItem->calculateTaxTotal()->getAmount(),
  55. 'discount_total' => $discountMethod === 'line_items'
  56. ? $lineItem->calculateDiscountTotal()->getAmount()
  57. : 0,
  58. ]);
  59. }
  60. protected function updateInvoiceTotals(Invoice $record, array $data): array
  61. {
  62. $subtotalCents = $record->lineItems()->sum('subtotal');
  63. $taxTotalCents = $record->lineItems()->sum('tax_total');
  64. $discountTotalCents = $this->calculateDiscountTotal(
  65. $data['discount_method'],
  66. AdjustmentComputation::parse($data['discount_computation']),
  67. $data['discount_rate'] ?? null,
  68. $subtotalCents,
  69. $record
  70. );
  71. $grandTotalCents = $subtotalCents + $taxTotalCents - $discountTotalCents;
  72. return [
  73. 'subtotal' => CurrencyConverter::convertCentsToFormatSimple($subtotalCents),
  74. 'tax_total' => CurrencyConverter::convertCentsToFormatSimple($taxTotalCents),
  75. 'discount_total' => CurrencyConverter::convertCentsToFormatSimple($discountTotalCents),
  76. 'total' => CurrencyConverter::convertCentsToFormatSimple($grandTotalCents),
  77. ];
  78. }
  79. protected function calculateDiscountTotal(
  80. string $discountMethod,
  81. ?AdjustmentComputation $discountComputation,
  82. ?string $discountRate,
  83. int $subtotalCents,
  84. Invoice $record
  85. ): int {
  86. if ($discountMethod === 'line_items') {
  87. return $record->lineItems()->sum('discount_total');
  88. }
  89. if ($discountComputation === AdjustmentComputation::Percentage) {
  90. return (int) ($subtotalCents * ((float) $discountRate / 100));
  91. }
  92. return CurrencyConverter::convertToCents($discountRate);
  93. }
  94. }