ProductionOrderService.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Services;
  4. use App\Enums\ProductionOrderSource;
  5. use App\Models\ProductionOrder;
  6. use App\Models\User;
  7. use Illuminate\Support\Arr;
  8. use Illuminate\Support\Facades\DB;
  9. class ProductionOrderService
  10. {
  11. public function __construct(
  12. private readonly ProductionCalendarService $calendar,
  13. ) {}
  14. /** @param array<string, mixed> $data */
  15. public function create(array $data, User $actor): ProductionOrder
  16. {
  17. return DB::transaction(function () use ($data, $actor): ProductionOrder {
  18. $items = Arr::pull($data, 'items', []);
  19. unset($data['nav']);
  20. $data['source'] = ProductionOrderSource::Manual;
  21. $data['contract_shipment_date'] = $this->contractShipmentDate($data);
  22. $data['created_by'] = $actor->id;
  23. $data['updated_by'] = $actor->id;
  24. $order = ProductionOrder::query()->create($data);
  25. $this->syncItems($order, $items, $actor);
  26. return $order->load(['manager', 'items.catalogItem']);
  27. });
  28. }
  29. /** @param array<string, mixed> $data */
  30. public function update(
  31. ProductionOrder $order,
  32. array $data,
  33. User $actor,
  34. bool $syncItems,
  35. array $editableItemFields = [],
  36. ): ProductionOrder {
  37. return DB::transaction(function () use (
  38. $order,
  39. $data,
  40. $actor,
  41. $syncItems,
  42. $editableItemFields,
  43. ): ProductionOrder {
  44. $items = Arr::pull($data, 'items', null);
  45. unset($data['nav']);
  46. if (array_key_exists('payment_date', $data) || array_key_exists('supply_working_days', $data)) {
  47. $data['contract_shipment_date'] = $this->contractShipmentDate([
  48. 'payment_date' => $data['payment_date'] ?? $order->payment_date,
  49. 'supply_working_days' => $data['supply_working_days'] ?? $order->supply_working_days,
  50. ]);
  51. }
  52. $data['updated_by'] = $actor->id;
  53. $order->update($data);
  54. if ($syncItems && is_array($items)) {
  55. $this->syncItems($order, $items, $actor);
  56. } elseif (is_array($items) && $editableItemFields !== []) {
  57. $this->updateItemDetails($order, $items, $editableItemFields);
  58. }
  59. return $order->refresh()->load(['manager', 'items.catalogItem']);
  60. });
  61. }
  62. /**
  63. * @param array<int, array<string, mixed>> $items
  64. * @param array<int, string> $editableFields
  65. */
  66. private function updateItemDetails(ProductionOrder $order, array $items, array $editableFields): void
  67. {
  68. foreach ($items as $itemData) {
  69. $id = isset($itemData['id']) ? (int) $itemData['id'] : 0;
  70. if ($id < 1) {
  71. continue;
  72. }
  73. $order->items()->whereKey($id)->firstOrFail()->update(
  74. Arr::only($itemData, $editableFields),
  75. );
  76. }
  77. }
  78. /**
  79. * @param array<int, array<string, mixed>> $items
  80. */
  81. private function syncItems(ProductionOrder $order, array $items, User $actor): void
  82. {
  83. $keptIds = [];
  84. foreach (array_values($items) as $sort => $itemData) {
  85. $id = isset($itemData['id']) ? (int) $itemData['id'] : null;
  86. $payload = Arr::only($itemData, [
  87. 'common_catalog_item_id',
  88. 'order_item_number',
  89. 'factory_number',
  90. 'manufacture_date',
  91. ]);
  92. $payload['sort'] = $sort;
  93. if ($id !== null && $id > 0) {
  94. $item = $order->items()->whereKey($id)->firstOrFail();
  95. $item->update($payload);
  96. } else {
  97. $item = $order->items()->create($payload + ['created_by' => $actor->id]);
  98. }
  99. $keptIds[] = $item->id;
  100. }
  101. $order->items()->whereNotIn('id', $keptIds)->delete();
  102. }
  103. /** @param array<string, mixed> $data */
  104. private function contractShipmentDate(array $data): string
  105. {
  106. return $this->calendar
  107. ->addWorkingDays((string) $data['payment_date'], (int) $data['supply_working_days'])
  108. ->toDateString();
  109. }
  110. }