瀏覽代碼

Update report MAF charts

Alexander Musikhin 1 天之前
父節點
當前提交
cfb25c312b

+ 12 - 0
app/Http/Controllers/ReportController.php

@@ -28,6 +28,12 @@ class ReportController extends Controller
     public function index()
     {
         $mountStatuses = [5, 7, 8, 9, 10];
+        $installedStatuses = [
+            Order::STATUS_READY_TO_HAND_OVER,
+            Order::STATUS_NOT_HANDED_OVER_WITH_NOTES,
+            Order::STATUS_HANDED_OVER_WITH_NOTES,
+            Order::STATUS_HANDED_OVER,
+        ];
         $doneStatuses = [9, 10];
         $handedOverStatus = Order::STATUS_HANDED_OVER;
         $objectTypes = ObjectType::query()->get()->pluck('name', 'id')->toArray();
@@ -42,6 +48,7 @@ class ReportController extends Controller
         $this->data['totalOrders']  = Order::all()->count();
         $this->data['doneOrders']  = Order::query()->whereIn('order_status_id', $doneStatuses)->count();
         $this->data['mountOrders']  = Order::query()->whereIn('order_status_id', $mountStatuses)->count();
+        $this->data['installedOrders'] = Order::query()->whereIn('order_status_id', $installedStatuses)->count();
 
         // всего маф / завершено маф / установлено маф
         $this->data['totalMafs']    = ProductSKU::all()->count();
@@ -53,6 +60,11 @@ class ReportController extends Controller
         whereHas('order', function ($query) use ($mountStatuses){
             $query->whereIn('order_status_id', $mountStatuses);
         })->count();
+        $this->data['installedMafs'] = ProductSKU::query()
+            ->whereHas('order', function ($query) use ($installedStatuses) {
+                $query->whereIn('order_status_id', $installedStatuses);
+            })
+            ->count();
 
         // сумма сданных МАФ по площадкам со статусом "Сдана"
         $this->data['totalHandedOverSum'] = Price::format(

+ 0 - 130
app/Services/SparePartIssueService.php

@@ -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' => 'Остаток после частичного списания из выбранного заказа',
-        ]);
-    }
 }
 
 /**

+ 5 - 0
resources/sass/app.scss

@@ -1024,9 +1024,14 @@ td p {
 .schedule-week-table {
   thead th {
     border-top: 1px solid #000;
+    border-right: 1px solid #000;
     border-bottom: 1px solid #000;
   }
 
+  thead th:first-child {
+    border-left: 1px solid #000;
+  }
+
   tbody td {
     border-bottom-color: #000;
   }

+ 24 - 32
resources/views/reclamations/edit.blade.php

@@ -13,8 +13,14 @@
                 </h4>
             </div>
             <div class="col-xl-6 action-toolbar mb-2">
-                <a href="{{ $back_url ?? route('reclamations.index', session('gp_reclamations')) }}"
-                   class="btn btn-sm btn-outline-secondary">Назад</a>
+                @if(hasRole('admin'))
+                    <a href="#" onclick="customConfirm('Удалить рекламацию?', function () { $('form#destroy').submit(); }, 'Подтверждение удаления'); return false;"
+                       class="btn btn-sm btn-danger">Удалить</a>
+                    <form action="{{ route('reclamations.delete', $reclamation) }}" method="post" class="d-none" id="destroy">
+                        @csrf
+                        @method('DELETE')
+                    </form>
+                @endif
                 @if(hasRole('admin') && !is_null($reclamation->brigadier_id) && !is_null($reclamation->start_work_date))
                     <button class="btn btn-sm btn-primary" id="createScheduleButton">Перенести в график</button>
                 @endif
@@ -24,14 +30,8 @@
                     <a href="{{ route('reclamation.generate-reclamation-payment-pack', ['reclamation' => $reclamation, 'nav' => $nav ?? null]) }}"
                        class="btn btn-primary btn-sm">Пакет документов на оплату</a>
                 @endif
-                @if(hasRole('admin'))
-                    <a href="#" onclick="customConfirm('Удалить рекламацию?', function () { $('form#destroy').submit(); }, 'Подтверждение удаления'); return false;"
-                       class="btn btn-sm btn-danger">Удалить</a>
-                    <form action="{{ route('reclamations.delete', $reclamation) }}" method="post" class="d-none" id="destroy">
-                        @csrf
-                        @method('DELETE')
-                    </form>
-                @endif
+                <a href="{{ $back_url ?? route('reclamations.index', session('gp_reclamations')) }}"
+                   class="btn btn-sm btn-outline-secondary">Назад</a>
             </div>
         </div>
         <div class="row">
@@ -317,21 +317,20 @@
                                                     </td>
                                                     @if(hasRole('admin,manager'))
                                                         <td class="text-end">
-                                                            @if($issueCandidateOrders->count() > 1)
-                                                                <button type="button"
-                                                                        class="btn btn-sm btn-outline-primary"
-                                                                        title="Изменить заказ резерва"
-                                                                        data-bs-toggle="modal"
-                                                                        data-bs-target="#reassignReservationModal-{{ $reservation->id }}">
-                                                                    <i class="bi bi-pencil"></i>
+                                                            <button type="button"
+                                                                    class="btn btn-sm btn-outline-primary"
+                                                                    title="Изменить заказ резерва"
+                                                                    data-bs-toggle="modal"
+                                                                    data-bs-target="#reassignReservationModal-{{ $reservation->id }}">
+                                                                <i class="bi bi-pencil"></i>
+                                                            </button>
+
+                                                            <form action="{{ route('spare_part_reservations.issue', $reservation) }}" method="POST" class="d-inline">
+                                                                @csrf
+                                                                <button type="submit" class="btn btn-sm btn-success" title="Списать">
+                                                                    <i class="bi bi-check-lg"></i>
                                                                 </button>
-
-                                                                <form action="{{ route('spare_part_reservations.issue', $reservation) }}" method="POST" class="d-inline">
-                                                                    @csrf
-                                                                    <button type="submit" class="btn btn-sm btn-success" title="Списать">
-                                                                        <i class="bi bi-check-lg"></i>
-                                                                    </button>
-                                                                </form>
+                                                            </form>
 
                                                                 <div class="modal fade" id="reassignReservationModal-{{ $reservation->id }}" tabindex="-1" aria-labelledby="reassignReservationModalLabel-{{ $reservation->id }}" aria-hidden="true">
                                                                     <div class="modal-dialog modal-dialog-scrollable">
@@ -360,6 +359,7 @@
                                                                                                    type="radio"
                                                                                                    name="selected_order_id"
                                                                                                    value="{{ $candidateOrder->id }}"
+                                                                                                   @disabled(!$isCurrentCandidate && $freeCandidateQty < 1)
                                                                                                    @checked($isCurrentCandidate)>
                                                                                             <span class="form-check-label">
                                                                                                 {{ $candidateOrder->display_order_number }}
@@ -379,14 +379,6 @@
                                                                         </div>
                                                                     </div>
                                                                 </div>
-                                                            @else
-                                                                <form action="{{ route('spare_part_reservations.issue', $reservation) }}" method="POST" class="d-inline">
-                                                                    @csrf
-                                                                    <button type="submit" class="btn btn-sm btn-success" title="Списать">
-                                                                        <i class="bi bi-check-lg"></i>
-                                                                    </button>
-                                                                </form>
-                                                            @endif
                                                             <form action="{{ route('spare_part_reservations.cancel', $reservation) }}" method="POST" class="d-inline">
                                                                 @csrf
                                                                 <button type="submit" class="btn btn-sm btn-outline-warning" title="Отменить резерв">

+ 30 - 2
resources/views/reports/index.blade.php

@@ -311,6 +311,16 @@
 @push('scripts')
     <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
     <script>
+        const totalMafs = {{ $totalMafs }};
+
+        function mafTooltipLabel(context) {
+            const value = Number(context.parsed || 0);
+            const percent = totalMafs > 0 ? (value / totalMafs * 100) : 0;
+            const formattedPercent = percent.toFixed(2).replace('.', ',');
+
+            return context.label + ': ' + value + ' МАФ (' + formattedPercent + '%)';
+        }
+
         const ctx = document.getElementById('order_types');
         new Chart(ctx, {
             type: 'bar',
@@ -393,7 +403,7 @@
             data: {
                 labels: ['Установлено', 'Осталось установить'],
                 datasets: [{
-                    data: [{{ $doneMafs }}, {{ $totalMafs - $doneMafs }}],
+                    data: [{{ $installedMafs }}, {{ $totalMafs - $installedMafs }}],
                     backgroundColor: [
                         'rgb(114,180,150)',
                         'rgb(251,215,105)',
@@ -401,6 +411,15 @@
                 }],
 
             },
+            options: {
+                plugins: {
+                    tooltip: {
+                        callbacks: {
+                            label: mafTooltipLabel
+                        }
+                    }
+                }
+            }
         });
 
         // круговая диаграмма 2
@@ -408,7 +427,7 @@
         new Chart(circleDiag2, {
             type: 'doughnut',
             data: {
-                labels: ['Установлено', 'Осталось установить'],
+                labels: ['Сдано', 'Осталось сдать'],
                 datasets: [{
                     data: [{{ $doneMafs }}, {{ $totalMafs - $doneMafs }}],
                     backgroundColor: [
@@ -418,6 +437,15 @@
                 }],
 
             },
+            options: {
+                plugins: {
+                    tooltip: {
+                        callbacks: {
+                            label: mafTooltipLabel
+                        }
+                    }
+                }
+            }
         });
 
         // Сортировка таблиц рекламаций

+ 67 - 0
resources/views/spare_part_orders/edit.blade.php

@@ -130,6 +130,19 @@
                             </thead>
                             <tbody>
                                 @foreach($spare_part_order->reservations->where('status', 'active') as $reservation)
+                                    @php
+                                        $reserveCandidateOrders = \App\Models\SparePartOrder::query()
+                                            ->where('spare_part_id', $reservation->spare_part_id)
+                                            ->where('with_documents', $reservation->with_documents)
+                                            ->where(function ($query) use ($reservation) {
+                                                $query->where(function ($inner) {
+                                                    $inner->where('status', \App\Models\SparePartOrder::STATUS_IN_STOCK)
+                                                        ->where('available_qty', '>', 0);
+                                                })->orWhere('id', $reservation->spare_part_order_id);
+                                            })
+                                            ->orderBy('created_at')
+                                            ->get();
+                                    @endphp
                                     <tr>
                                         <td>{{ $reservation->reserved_qty }}</td>
                                         <td>
@@ -142,6 +155,13 @@
                                         <td>{{ $reservation->created_at->format('d.m.Y H:i') }}</td>
                                         <td>
                                             @if(hasRole('admin,manager'))
+                                                <button type="button"
+                                                        class="btn btn-sm btn-outline-primary"
+                                                        title="Изменить заказ резерва"
+                                                        data-bs-toggle="modal"
+                                                        data-bs-target="#reassignReservationModal-{{ $reservation->id }}">
+                                                    <i class="bi bi-pencil"></i>
+                                                </button>
                                                 <form action="{{ route('spare_part_reservations.issue', $reservation) }}" method="POST" class="d-inline">
                                                     @csrf
                                                     <button type="submit" class="btn btn-sm btn-success" title="Списать">
@@ -154,6 +174,53 @@
                                                         <i class="fas fa-times"></i>
                                                     </button>
                                                 </form>
+                                                <div class="modal fade" id="reassignReservationModal-{{ $reservation->id }}" tabindex="-1" aria-labelledby="reassignReservationModalLabel-{{ $reservation->id }}" aria-hidden="true">
+                                                    <div class="modal-dialog modal-dialog-scrollable">
+                                                        <div class="modal-content text-start">
+                                                            <div class="modal-header">
+                                                                <h1 class="modal-title fs-5" id="reassignReservationModalLabel-{{ $reservation->id }}">Выбор заказа для резерва</h1>
+                                                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
+                                                            </div>
+                                                            <form action="{{ route('spare_part_reservations.reassign', $reservation) }}" method="POST">
+                                                                @csrf
+                                                                <div class="modal-body">
+                                                                    <div class="mb-2 small text-muted">
+                                                                        {{ $spare_part_order->sparePart?->article ?? 'Запчасть' }}, {{ $reservation->reserved_qty }} шт.
+                                                                    </div>
+                                                                    @foreach($reserveCandidateOrders as $candidateOrder)
+                                                                        @php
+                                                                            $activeReservedForCandidate = \App\Models\Reservation::query()
+                                                                                ->where('spare_part_order_id', $candidateOrder->id)
+                                                                                ->where('status', \App\Models\Reservation::STATUS_ACTIVE)
+                                                                                ->sum('reserved_qty');
+                                                                            $freeCandidateQty = max(0, (int) $candidateOrder->available_qty - (int) $activeReservedForCandidate);
+                                                                            $isCurrentCandidate = (int) $candidateOrder->id === (int) $reservation->spare_part_order_id;
+                                                                        @endphp
+                                                                        <label class="form-check mb-2">
+                                                                            <input class="form-check-input"
+                                                                                   type="radio"
+                                                                                   name="selected_order_id"
+                                                                                   value="{{ $candidateOrder->id }}"
+                                                                                   @disabled(!$isCurrentCandidate && $freeCandidateQty < 1)
+                                                                                   @checked($isCurrentCandidate)>
+                                                                            <span class="form-check-label">
+                                                                                {{ $candidateOrder->display_order_number }}
+                                                                                @if($isCurrentCandidate)
+                                                                                    <span class="text-muted">, текущий резерв</span>
+                                                                                @endif
+                                                                                <span class="text-muted">, доступно {{ $freeCandidateQty }}</span>
+                                                                            </span>
+                                                                        </label>
+                                                                    @endforeach
+                                                                </div>
+                                                                <div class="modal-footer">
+                                                                    <button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Закрыть</button>
+                                                                    <button type="submit" class="btn btn-primary btn-sm">Сохранить резерв</button>
+                                                                </div>
+                                                            </form>
+                                                        </div>
+                                                    </div>
+                                                </div>
                                             @endif
                                         </td>
                                     </tr>

+ 35 - 0
tests/Feature/ReportControllerTest.php

@@ -78,6 +78,41 @@ class ReportControllerTest extends TestCase
         $response->assertViewHas('mountOrders', fn($v) => $v >= 0);
     }
 
+    public function test_reports_index_counts_installed_mafs_by_handover_statuses(): void
+    {
+        foreach ([
+            Order::STATUS_READY_TO_HAND_OVER,
+            Order::STATUS_NOT_HANDED_OVER_WITH_NOTES,
+            Order::STATUS_HANDED_OVER_WITH_NOTES,
+            Order::STATUS_HANDED_OVER,
+        ] as $statusId) {
+            $order = Order::factory()->create([
+                'user_id' => $this->adminUser->id,
+                'order_status_id' => $statusId,
+            ]);
+
+            ProductSKU::factory()
+                ->forOrder($order)
+                ->create();
+        }
+
+        $inMountOrder = Order::factory()->create([
+            'user_id' => $this->adminUser->id,
+            'order_status_id' => Order::STATUS_IN_MOUNT,
+        ]);
+
+        ProductSKU::factory()
+            ->forOrder($inMountOrder)
+            ->create();
+
+        $response = $this->actingAs($this->adminUser)->get(route('reports.index'));
+
+        $response->assertViewHas('installedOrders', 4);
+        $response->assertViewHas('installedMafs', 4);
+        $response->assertViewHas('mountOrders', 5);
+        $response->assertViewHas('mountMafs', 5);
+    }
+
     public function test_reports_index_has_reclamations_data(): void
     {
         $response = $this->actingAs($this->adminUser)->get(route('reports.index'));

+ 0 - 106
tests/Unit/Services/SparePartIssueServiceTest.php

@@ -367,112 +367,6 @@ class SparePartIssueServiceTest extends TestCase
         $this->assertEquals(3, $results[0]->issued);
     }
 
-    public function test_issue_reservation_from_selected_order_cancels_old_reservation_and_issues_from_new_order(): void
-    {
-        $user = User::factory()->create();
-        $this->actingAs($user);
-
-        $sparePart = SparePart::factory()->create();
-        $reclamation = Reclamation::factory()->create();
-        $oldOrder = SparePartOrder::factory()->inStock()->withDocuments(false)->withQuantity(10)->forSparePart($sparePart)->create();
-        $newOrder = SparePartOrder::factory()->inStock()->withDocuments(false)->withQuantity(8)->forSparePart($sparePart)->create();
-
-        \App\Models\ReclamationSparePart::query()->create([
-            'reclamation_id' => $reclamation->id,
-            'spare_part_id' => $sparePart->id,
-            'quantity' => 5,
-            'with_documents' => false,
-            'status' => 'reserved',
-            'reserved_qty' => 5,
-            'issued_qty' => 0,
-        ]);
-
-        $reservation = Reservation::factory()
-            ->active()
-            ->withQuantity(5)
-            ->withDocuments(false)
-            ->fromOrder($oldOrder)
-            ->forReclamation($reclamation)
-            ->create();
-
-        $result = $this->service->issueReservationFromSelectedOrder($reservation, $newOrder->id);
-
-        $this->assertEquals(5, $result->issued);
-        $this->assertDatabaseHas('reservations', [
-            'id' => $reservation->id,
-            'status' => Reservation::STATUS_CANCELLED,
-        ]);
-        $this->assertDatabaseHas('reservations', [
-            'spare_part_order_id' => $newOrder->id,
-            'reclamation_id' => $reclamation->id,
-            'reserved_qty' => 5,
-            'status' => Reservation::STATUS_ISSUED,
-        ]);
-        $this->assertDatabaseHas('spare_part_orders', [
-            'id' => $newOrder->id,
-            'available_qty' => 3,
-        ]);
-    }
-
-    public function test_issue_reservation_from_selected_order_splits_row_when_selected_order_has_partial_quantity(): void
-    {
-        $user = User::factory()->create();
-        $this->actingAs($user);
-
-        $sparePart = SparePart::factory()->create();
-        $reclamation = Reclamation::factory()->create();
-        $oldOrder = SparePartOrder::factory()->inStock()->withDocuments(true)->withQuantity(10)->forSparePart($sparePart)->create();
-        $newOrder = SparePartOrder::factory()->inStock()->withDocuments(true)->withQuantity(2)->forSparePart($sparePart)->create([
-            'available_qty' => 2,
-        ]);
-
-        \App\Models\ReclamationSparePart::query()->create([
-            'reclamation_id' => $reclamation->id,
-            'spare_part_id' => $sparePart->id,
-            'quantity' => 5,
-            'with_documents' => true,
-            'status' => 'reserved',
-            'reserved_qty' => 5,
-            'issued_qty' => 0,
-        ]);
-
-        $reservation = Reservation::factory()
-            ->active()
-            ->withQuantity(5)
-            ->withDocuments(true)
-            ->fromOrder($oldOrder)
-            ->forReclamation($reclamation)
-            ->create();
-
-        $result = $this->service->issueReservationFromSelectedOrder($reservation, $newOrder->id);
-
-        $this->assertEquals(2, $result->issued);
-        $this->assertDatabaseHas('reclamation_spare_part', [
-            'reclamation_id' => $reclamation->id,
-            'spare_part_id' => $sparePart->id,
-            'quantity' => 2,
-            'with_documents' => true,
-            'status' => 'issued',
-            'issued_qty' => 2,
-        ]);
-        $this->assertDatabaseHas('reclamation_spare_part', [
-            'reclamation_id' => $reclamation->id,
-            'spare_part_id' => $sparePart->id,
-            'quantity' => 3,
-            'with_documents' => true,
-            'status' => 'pending',
-            'reserved_qty' => 0,
-            'issued_qty' => 0,
-        ]);
-        $this->assertDatabaseHas('shortages', [
-            'reclamation_id' => $reclamation->id,
-            'spare_part_id' => $sparePart->id,
-            'missing_qty' => 3,
-            'with_documents' => true,
-            'status' => \App\Models\Shortage::STATUS_OPEN,
-        ]);
-    }
-
     // ==================== directIssue ====================
 
     public function test_direct_issue_decreases_available_qty(): void