|
|
@@ -3,9 +3,7 @@
|
|
|
namespace App\Services;
|
|
|
|
|
|
use App\Models\InventoryMovement;
|
|
|
-use App\Models\ReclamationSparePart;
|
|
|
use App\Models\Reservation;
|
|
|
-use App\Models\Shortage;
|
|
|
use App\Models\SparePartOrder;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
@@ -116,92 +114,6 @@ class SparePartIssueService
|
|
|
return $results;
|
|
|
}
|
|
|
|
|
|
- public function issueReservationFromSelectedOrder(
|
|
|
- Reservation $reservation,
|
|
|
- int $selectedOrderId,
|
|
|
- string $note = ''
|
|
|
- ): IssueResult {
|
|
|
- if (!$reservation->isActive()) {
|
|
|
- throw new \InvalidArgumentException('Резерв не активен, списание невозможно');
|
|
|
- }
|
|
|
-
|
|
|
- if ((int) $reservation->spare_part_order_id === $selectedOrderId) {
|
|
|
- $result = $this->issueReservation($reservation, $note);
|
|
|
- app(SparePartReservationService::class)->syncPivotRowForReservation($reservation);
|
|
|
-
|
|
|
- return $result;
|
|
|
- }
|
|
|
-
|
|
|
- return DB::transaction(function () use ($reservation, $selectedOrderId, $note) {
|
|
|
- $reservation = Reservation::query()->lockForUpdate()->findOrFail($reservation->id);
|
|
|
- $selectedOrder = SparePartOrder::query()->lockForUpdate()->findOrFail($selectedOrderId);
|
|
|
-
|
|
|
- if (!$reservation->isActive()) {
|
|
|
- throw new \InvalidArgumentException('Резерв не активен, списание невозможно');
|
|
|
- }
|
|
|
-
|
|
|
- if ((int) $selectedOrder->spare_part_id !== (int) $reservation->spare_part_id) {
|
|
|
- throw new \InvalidArgumentException('Выбран заказ другой запчасти');
|
|
|
- }
|
|
|
-
|
|
|
- if ((bool) $selectedOrder->with_documents !== (bool) $reservation->with_documents) {
|
|
|
- throw new \InvalidArgumentException('Выбран заказ с другим признаком документов');
|
|
|
- }
|
|
|
-
|
|
|
- if ($selectedOrder->status !== SparePartOrder::STATUS_IN_STOCK) {
|
|
|
- throw new \InvalidArgumentException('Списание возможно только из партии на складе');
|
|
|
- }
|
|
|
-
|
|
|
- $alreadyReservedOnSelected = Reservation::query()
|
|
|
- ->where('spare_part_order_id', $selectedOrder->id)
|
|
|
- ->where('status', Reservation::STATUS_ACTIVE)
|
|
|
- ->sum('reserved_qty');
|
|
|
-
|
|
|
- $freeQty = max(0, (int) $selectedOrder->available_qty - (int) $alreadyReservedOnSelected);
|
|
|
- if ($freeQty < 1) {
|
|
|
- throw new \RuntimeException('В выбранном заказе нет доступного остатка');
|
|
|
- }
|
|
|
-
|
|
|
- $requestedQty = (int) $reservation->reserved_qty;
|
|
|
- $issueQty = min($requestedQty, $freeQty);
|
|
|
-
|
|
|
- app(SparePartReservationService::class)
|
|
|
- ->cancelReservation($reservation, 'Перенос резерва на другой заказ для списания');
|
|
|
-
|
|
|
- $reserveMovement = InventoryMovement::create([
|
|
|
- 'spare_part_order_id' => $selectedOrder->id,
|
|
|
- 'spare_part_id' => $reservation->spare_part_id,
|
|
|
- 'qty' => $issueQty,
|
|
|
- 'movement_type' => InventoryMovement::TYPE_RESERVE,
|
|
|
- 'source_type' => InventoryMovement::SOURCE_RECLAMATION,
|
|
|
- 'source_id' => $reservation->reclamation_id,
|
|
|
- 'with_documents' => $reservation->with_documents,
|
|
|
- 'user_id' => auth()->id(),
|
|
|
- 'note' => "Резервирование для списания из выбранного заказа #{$selectedOrder->id}",
|
|
|
- ]);
|
|
|
-
|
|
|
- $selectedReservation = Reservation::create([
|
|
|
- 'spare_part_id' => $reservation->spare_part_id,
|
|
|
- 'spare_part_order_id' => $selectedOrder->id,
|
|
|
- 'reclamation_id' => $reservation->reclamation_id,
|
|
|
- 'reserved_qty' => $issueQty,
|
|
|
- 'with_documents' => $reservation->with_documents,
|
|
|
- 'status' => Reservation::STATUS_ACTIVE,
|
|
|
- 'movement_id' => $reserveMovement->id,
|
|
|
- ]);
|
|
|
-
|
|
|
- $result = $this->issueReservation($selectedReservation, $note);
|
|
|
-
|
|
|
- if ($issueQty < $requestedQty) {
|
|
|
- $this->splitPivotRowAfterPartialIssue($reservation, $issueQty, $requestedQty - $issueQty);
|
|
|
- } else {
|
|
|
- app(SparePartReservationService::class)->syncPivotRowForReservation($reservation);
|
|
|
- }
|
|
|
-
|
|
|
- return $result;
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Прямое списание без резерва (для ручных операций)
|
|
|
*
|
|
|
@@ -305,48 +217,6 @@ class SparePartIssueService
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- private function splitPivotRowAfterPartialIssue(Reservation $reservation, int $issuedQty, int $remainingQty): void
|
|
|
- {
|
|
|
- $row = ReclamationSparePart::query()
|
|
|
- ->where('reclamation_id', $reservation->reclamation_id)
|
|
|
- ->where('spare_part_id', $reservation->spare_part_id)
|
|
|
- ->where('with_documents', $reservation->with_documents)
|
|
|
- ->where('quantity', '>=', $reservation->reserved_qty)
|
|
|
- ->orderBy('id')
|
|
|
- ->first();
|
|
|
-
|
|
|
- if (!$row) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- $row->update([
|
|
|
- 'quantity' => $issuedQty,
|
|
|
- 'reserved_qty' => 0,
|
|
|
- 'issued_qty' => $issuedQty,
|
|
|
- 'status' => 'issued',
|
|
|
- ]);
|
|
|
-
|
|
|
- ReclamationSparePart::query()->create([
|
|
|
- 'reclamation_id' => $row->reclamation_id,
|
|
|
- 'spare_part_id' => $row->spare_part_id,
|
|
|
- 'quantity' => $remainingQty,
|
|
|
- 'with_documents' => $row->with_documents,
|
|
|
- 'status' => 'pending',
|
|
|
- 'reserved_qty' => 0,
|
|
|
- 'issued_qty' => 0,
|
|
|
- ]);
|
|
|
-
|
|
|
- Shortage::query()->create([
|
|
|
- 'spare_part_id' => $row->spare_part_id,
|
|
|
- 'reclamation_id' => $row->reclamation_id,
|
|
|
- 'with_documents' => $row->with_documents,
|
|
|
- 'required_qty' => $remainingQty,
|
|
|
- 'reserved_qty' => 0,
|
|
|
- 'missing_qty' => $remainingQty,
|
|
|
- 'status' => Shortage::STATUS_OPEN,
|
|
|
- 'note' => 'Остаток после частичного списания из выбранного заказа',
|
|
|
- ]);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
/**
|