ImportMafOrdersService.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. namespace App\Services\Import;
  3. use App\Models\MafOrder;
  4. use App\Models\Product;
  5. use Exception;
  6. use Illuminate\Support\Facades\DB;
  7. use Illuminate\Support\Facades\Log;
  8. use PhpOffice\PhpSpreadsheet\IOFactory;
  9. class ImportMafOrdersService
  10. {
  11. private array $logs = [];
  12. private array $productCache = [];
  13. public function __construct(
  14. private readonly string $filePath,
  15. private readonly int $userId,
  16. private readonly int $year,
  17. ) {}
  18. public function handle(): array
  19. {
  20. try {
  21. $this->log("Начало импорта заказов МАФ (год: {$this->year})");
  22. // Загружаем кэш товаров по артикулу для выбранного года
  23. $this->loadProductCache();
  24. DB::beginTransaction();
  25. try {
  26. $spreadsheet = IOFactory::load($this->filePath);
  27. $sheet = $spreadsheet->getActiveSheet();
  28. $highestRow = $sheet->getHighestRow();
  29. $imported = 0;
  30. $skipped = 0;
  31. $errors = 0;
  32. // Начинаем со 2-й строки (пропускаем заголовок)
  33. // Колонки: A=Артикул МАФ, B=кол-во, C=Номер заказа, D=Статус
  34. for ($row = 2; $row <= $highestRow; $row++) {
  35. $article = trim((string) $sheet->getCell('A' . $row)->getValue());
  36. $quantity = (int) $sheet->getCell('B' . $row)->getValue();
  37. $orderNumber = trim((string) $sheet->getCell('C' . $row)->getValue());
  38. $statusRaw = trim((string) $sheet->getCell('D' . $row)->getValue());
  39. // Пропускаем пустые строки
  40. if (empty($article)) {
  41. $skipped++;
  42. continue;
  43. }
  44. // Проверяем наличие товара в каталоге выбранного года
  45. if (!isset($this->productCache[$article])) {
  46. $this->log("Строка {$row}: МАФ с артикулом '{$article}' не найден в каталоге {$this->year} года", 'ERROR');
  47. $errors++;
  48. continue;
  49. }
  50. $productId = $this->productCache[$article];
  51. // Парсим статус
  52. $status = $this->parseStatus($statusRaw);
  53. if ($status === null) {
  54. $this->log("Строка {$row}: неизвестный статус '{$statusRaw}'", 'ERROR');
  55. $errors++;
  56. continue;
  57. }
  58. // Определяем in_stock в зависимости от статуса
  59. $inStock = $status === 'на складе' ? $quantity : 0;
  60. // Создаём заказ МАФ
  61. MafOrder::create([
  62. 'year' => $this->year,
  63. 'product_id' => $productId,
  64. 'order_number' => $orderNumber ?: null,
  65. 'status' => $status,
  66. 'quantity' => $quantity,
  67. 'in_stock' => $inStock,
  68. 'user_id' => $this->userId,
  69. ]);
  70. $imported++;
  71. }
  72. DB::commit();
  73. $this->log("=== РЕЗЮМЕ ИМПОРТА ===");
  74. $this->log("Импортировано: {$imported}");
  75. $this->log("Пропущено (пустые строки): {$skipped}");
  76. $this->log("Ошибок: {$errors}");
  77. return [
  78. 'success' => $errors === 0,
  79. 'imported' => $imported,
  80. 'skipped' => $skipped,
  81. 'errors' => $errors,
  82. 'logs' => $this->logs,
  83. ];
  84. } catch (Exception $e) {
  85. DB::rollBack();
  86. throw $e;
  87. }
  88. } catch (Exception $e) {
  89. $this->log("КРИТИЧЕСКАЯ ОШИБКА: " . $e->getMessage(), 'ERROR');
  90. Log::error("Ошибка импорта заказов МАФ: " . $e->getMessage(), [
  91. 'trace' => $e->getTraceAsString(),
  92. ]);
  93. return [
  94. 'success' => false,
  95. 'error' => $e->getMessage(),
  96. 'logs' => $this->logs,
  97. ];
  98. }
  99. }
  100. private function loadProductCache(): void
  101. {
  102. $this->productCache = Product::withoutGlobalScopes()
  103. ->where('year', $this->year)
  104. ->pluck('id', 'article')
  105. ->toArray();
  106. $this->log("Загружено " . count($this->productCache) . " товаров в кэш для {$this->year} года");
  107. }
  108. private function parseStatus(string $value): ?string
  109. {
  110. $value = mb_strtolower(trim($value));
  111. $statusMap = [
  112. 'на складе' => 'на складе',
  113. 'in_stock' => 'на складе',
  114. 'in stock' => 'на складе',
  115. 'заказан' => 'заказан',
  116. 'заказано' => 'заказан',
  117. 'ordered' => 'заказан',
  118. ];
  119. return $statusMap[$value] ?? null;
  120. }
  121. private function log(string $message, string $level = 'INFO'): void
  122. {
  123. $this->logs[] = '[' . date('H:i:s') . '] ' . $level . ': ' . $message;
  124. Log::info($message);
  125. }
  126. public function getLogs(): array
  127. {
  128. return $this->logs;
  129. }
  130. }