ProductionOrderService.php 4.6 KB

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