2026_01_24_200005_migrate_shipments_to_movements.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. <?php
  2. use Illuminate\Database\Migrations\Migration;
  3. use Illuminate\Support\Facades\DB;
  4. return new class extends Migration
  5. {
  6. /**
  7. * Run the migrations.
  8. *
  9. * Миграция данных:
  10. * 1. Перенос shipments в inventory_movements
  11. * 2. Конвертация отрицательных остатков в shortages
  12. * 3. Обновление pivot таблицы reclamation_spare_part
  13. */
  14. public function up(): void
  15. {
  16. // 1. Миграция shipments в movements
  17. $shipments = DB::table('spare_part_order_shipments')
  18. ->join('spare_part_orders', 'spare_part_order_shipments.spare_part_order_id', '=', 'spare_part_orders.id')
  19. ->select([
  20. 'spare_part_order_shipments.*',
  21. 'spare_part_orders.spare_part_id',
  22. 'spare_part_orders.with_documents',
  23. ])
  24. ->get();
  25. foreach ($shipments as $shipment) {
  26. DB::table('inventory_movements')->insert([
  27. 'spare_part_order_id' => $shipment->spare_part_order_id,
  28. 'spare_part_id' => $shipment->spare_part_id,
  29. 'qty' => $shipment->quantity,
  30. 'movement_type' => 'issue',
  31. 'source_type' => $shipment->reclamation_id ? 'reclamation' : 'manual',
  32. 'source_id' => $shipment->reclamation_id,
  33. 'with_documents' => $shipment->with_documents,
  34. 'user_id' => $shipment->user_id,
  35. 'note' => $shipment->note ?? ($shipment->is_automatic ? 'Автоматическое списание (мигрировано)' : 'Ручное списание (мигрировано)'),
  36. 'created_at' => $shipment->created_at,
  37. 'updated_at' => $shipment->updated_at,
  38. ]);
  39. }
  40. // 2. Находим "виртуальные заказы" с отрицательным остатком (до рефакторинга)
  41. // Примечание: колонка уже переименована в available_qty и обнулена в предыдущей миграции
  42. // Но нам нужно создать shortages на основе данных в pivot таблице,
  43. // где quantity есть, но не было реальной партии
  44. // Получаем записи из pivot где запчасть привязана к рекламации
  45. $pivotRecords = DB::table('reclamation_spare_part')
  46. ->join('spare_parts', 'reclamation_spare_part.spare_part_id', '=', 'spare_parts.id')
  47. ->select([
  48. 'reclamation_spare_part.*',
  49. 'spare_parts.article',
  50. ])
  51. ->get();
  52. foreach ($pivotRecords as $pivot) {
  53. // Проверяем, есть ли достаточно резервов/списаний для этой привязки
  54. $totalIssued = DB::table('inventory_movements')
  55. ->where('source_type', 'reclamation')
  56. ->where('source_id', $pivot->reclamation_id)
  57. ->where('spare_part_id', $pivot->spare_part_id)
  58. ->where('with_documents', $pivot->with_documents)
  59. ->where('movement_type', 'issue')
  60. ->sum('qty');
  61. $missing = $pivot->quantity - $totalIssued;
  62. if ($missing > 0) {
  63. // Создаём shortage для недостающего количества
  64. DB::table('shortages')->insert([
  65. 'spare_part_id' => $pivot->spare_part_id,
  66. 'reclamation_id' => $pivot->reclamation_id,
  67. 'with_documents' => $pivot->with_documents,
  68. 'required_qty' => $pivot->quantity,
  69. 'reserved_qty' => $totalIssued,
  70. 'missing_qty' => $missing,
  71. 'status' => 'open',
  72. 'note' => 'Создано при миграции данных',
  73. 'created_at' => $pivot->created_at ?? now(),
  74. 'updated_at' => now(),
  75. ]);
  76. }
  77. // Обновляем pivot с новыми полями
  78. DB::table('reclamation_spare_part')
  79. ->where('id', $pivot->id)
  80. ->update([
  81. 'status' => $totalIssued >= $pivot->quantity ? 'issued' : ($totalIssued > 0 ? 'reserved' : 'pending'),
  82. 'reserved_qty' => 0, // Старые данные были issue, не reserve
  83. 'issued_qty' => $totalIssued,
  84. ]);
  85. }
  86. // 3. Удаляем виртуальные заказы с нулевым ordered_quantity
  87. // (они были созданы для учёта недостач)
  88. DB::table('spare_part_orders')
  89. ->where('ordered_quantity', 0)
  90. ->delete();
  91. }
  92. /**
  93. * Reverse the migrations.
  94. */
  95. public function down(): void
  96. {
  97. // Удаляем созданные при миграции данные
  98. DB::table('inventory_movements')
  99. ->where('note', 'like', '%мигрировано%')
  100. ->delete();
  101. DB::table('shortages')
  102. ->where('note', 'Создано при миграции данных')
  103. ->delete();
  104. // Сбрасываем новые поля в pivot
  105. DB::table('reclamation_spare_part')->update([
  106. 'status' => 'pending',
  107. 'reserved_qty' => 0,
  108. 'issued_qty' => 0,
  109. ]);
  110. }
  111. };