ProductionOrderService.php 4.5 KB

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