first(); if (!$sparePart) { return false; } // Ищем заказ для списания (FIFO - первый созданный) $order = SparePartOrder::where('spare_part_id', $sparePart->id) ->where('status', SparePartOrder::STATUS_IN_STOCK) ->where('with_documents', $withDocuments) ->where('remaining_quantity', '>=', $quantity) ->orderBy('created_at', 'asc') ->first(); if ($order) { // Списываем $note = "Автоматическое списание для рекламации: #{$reclamationId}"; return $order->shipQuantity($quantity, $note, $reclamationId, null); } else { // Создаём виртуальный заказ с недостачей // Статус IN_STOCK чтобы учитывалось в вычисляемых полях! SparePartOrder::create([ 'spare_part_id' => $sparePart->id, 'source_text' => "Автоспискание для рекламации #{$reclamationId}", 'status' => SparePartOrder::STATUS_IN_STOCK, 'ordered_quantity' => 0, 'remaining_quantity' => -$quantity, 'with_documents' => $withDocuments, 'note' => "Недостача, созданная автоматически", 'user_id' => auth()->id() ?? 1, ]); return false; } } /** * Получить список запчастей с критическим недостатком (отрицательное количество) */ public function getCriticalShortages(): Collection { return SparePart::all()->filter(function ($sparePart) { return $sparePart->hasCriticalShortage(); }); } /** * Получить список запчастей ниже минимального остатка */ public function getBelowMinStock(): Collection { return SparePart::all()->filter(function ($sparePart) { return $sparePart->isBelowMinStock(); }); } /** * Рассчитать сколько нужно заказать для компенсации недостачи */ public function calculateOrderQuantity(SparePart $sparePart, bool $withDocuments): int { $quantity = $withDocuments ? $sparePart->quantity_with_docs : $sparePart->quantity_without_docs; if ($quantity >= 0) { return 0; } return abs($quantity); } }