| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- <?php
- declare(strict_types=1);
- namespace App\Services\Import;
- use App\Models\CommonCatalogItem;
- use App\Models\Import;
- use App\Models\StockOrder;
- use App\Models\User;
- use App\Services\StockInventoryService;
- use Illuminate\Support\Facades\Storage;
- use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
- use RuntimeException;
- use Throwable;
- class ImportStockOrdersService
- {
- public const HEADERS = [
- 'Номер заказа',
- 'Артикул',
- 'Статус',
- 'Кол-во заказано',
- 'Примечание',
- ];
- public function __construct(
- private readonly Import $import,
- private readonly int $userId,
- private readonly StockInventoryService $inventoryService,
- ) {}
- public function handle(): bool
- {
- try {
- $actor = User::query()->findOrFail($this->userId);
- $spreadsheet = (new Xlsx)->load(Storage::disk('upload')->path((string) $this->import->filename));
- $sheet = $spreadsheet->getActiveSheet();
- $headers = $sheet->rangeToArray('A1:E1', null, true, true, false)[0];
- if (array_map($this->normalize(...), $headers) !== array_map($this->normalize(...), self::HEADERS)) {
- throw new RuntimeException('Некорректные заголовки файла складских заказов.');
- }
- $created = 0;
- $updated = 0;
- $errors = 0;
- for ($row = 2; $row <= $sheet->getHighestDataRow(); $row++) {
- $orderNumber = trim((string) $sheet->getCell("A{$row}")->getFormattedValue());
- $article = trim((string) $sheet->getCell("B{$row}")->getFormattedValue());
- if ($orderNumber === '' && $article === '') {
- continue;
- }
- try {
- if ($orderNumber === '') {
- throw new RuntimeException('Не указан номер заказа.');
- }
- if ($article === '') {
- throw new RuntimeException('Не указан артикул.');
- }
- $item = CommonCatalogItem::query()->where('article', $article)->firstOrFail();
- $status = $this->status((string) $sheet->getCell("C{$row}")->getFormattedValue());
- $quantity = filter_var(
- $sheet->getCell("D{$row}")->getCalculatedValue(),
- FILTER_VALIDATE_INT,
- ['options' => ['min_range' => 1]],
- );
- if ($quantity === false) {
- throw new RuntimeException('Количество должно быть целым числом больше нуля.');
- }
- $data = [
- 'common_catalog_item_id' => $item->id,
- 'order_number' => $orderNumber,
- 'status' => $status,
- 'ordered_quantity' => $quantity,
- 'note' => $this->nullable((string) $sheet->getCell("E{$row}")->getValue()),
- ];
- $order = StockOrder::withTrashed()
- ->where('order_number', $orderNumber)
- ->where('common_catalog_item_id', $item->id)
- ->first();
- if ($order) {
- if ($order->trashed()) {
- $order->restore();
- }
- $this->inventoryService->updateOrder($order, $data, $actor);
- $updated++;
- } else {
- $this->inventoryService->createOrder($data, $actor);
- $created++;
- }
- } catch (Throwable $exception) {
- $errors++;
- $this->import->log("Строка {$row}: {$exception->getMessage()}", 'WARNING');
- }
- }
- $spreadsheet->disconnectWorksheets();
- $this->import->log("Создано: {$created}; обновлено: {$updated}; ошибок: {$errors}.");
- $this->import->update(['status' => 'DONE']);
- return true;
- } catch (Throwable $exception) {
- $this->import->log($exception->getMessage(), 'ERROR');
- $this->import->update(['status' => 'ERROR']);
- throw $exception;
- }
- }
- private function status(string $value): string
- {
- return match (mb_strtolower(trim($value))) {
- 'заказан', 'заказано', StockOrder::STATUS_ORDERED => StockOrder::STATUS_ORDERED,
- 'на складе', StockOrder::STATUS_IN_STOCK => StockOrder::STATUS_IN_STOCK,
- 'отгружено', 'отгружен', StockOrder::STATUS_SHIPPED => StockOrder::STATUS_SHIPPED,
- default => throw new RuntimeException("Неизвестный статус: {$value}."),
- };
- }
- private function normalize(mixed $value): string
- {
- return mb_strtolower(trim((string) preg_replace('/\s+/u', ' ', (string) $value)));
- }
- private function nullable(string $value): ?string
- {
- $value = trim($value);
- return $value === '' ? null : $value;
- }
- }
|