Browse Source

mass statement assigment

Alexander Musikhin 3 days ago
parent
commit
3fe13f7b9b

+ 35 - 0
app/Http/Controllers/OrderController.php

@@ -756,6 +756,41 @@ class OrderController extends Controller
             ->deleteFileAfterSend();
             ->deleteFileAfterSend();
     }
     }
 
 
+    public function addStatementToMafs(Request $request, int $order): RedirectResponse
+    {
+        abort_unless(
+            $request->user()?->canUpdateField('maf', 'statement_number')
+            && $request->user()?->canUpdateField('maf', 'statement_date'),
+            403
+        );
+
+        $validated = $request->validate([
+            'statement_number' => ['required', 'string', 'max:255'],
+            'statement_date' => ['required', 'date'],
+            'skus' => ['required', 'array', 'min:1'],
+            'skus.*' => ['required', 'integer', 'exists:products_sku,id'],
+        ]);
+
+        $orderModel = Order::query()
+            ->withoutGlobalScope(\App\Models\Scopes\YearScope::class)
+            ->findOrFail($order);
+
+        $updated = ProductSKU::query()
+            ->withoutGlobalScope(\App\Models\Scopes\YearScope::class)
+            ->where('order_id', $orderModel->id)
+            ->whereIn('id', $validated['skus'])
+            ->update([
+                'statement_number' => $validated['statement_number'],
+                'statement_date' => $validated['statement_date'],
+            ]);
+
+        if ($updated === 0) {
+            return redirect()->back()->with(['danger' => 'Выбранные МАФ не найдены на этой площадке.']);
+        }
+
+        return redirect()->back()->with(['success' => "Ведомость добавлена к МАФ: {$updated}."]);
+    }
+
     public function export(Request $request)
     public function export(Request $request)
     {
     {
         $request->validate([
         $request->validate([

+ 4 - 0
config/access_routes.php

@@ -125,6 +125,10 @@ return [
             'get-maf' => 'orders.maf.manage',
             'get-maf' => 'orders.maf.manage',
             'revert-maf' => 'orders.maf.manage',
             'revert-maf' => 'orders.maf.manage',
             'move-maf' => 'orders.maf.manage',
             'move-maf' => 'orders.maf.manage',
+            'add-statement-to-mafs' => [
+                'maf.fields.statement_number.update',
+                'maf.fields.statement_date.update',
+            ],
             'create-ttn' => 'orders.ttn.create',
             'create-ttn' => 'orders.ttn.create',
             'contractor-specification' => 'orders.contractor_specification.create',
             'contractor-specification' => 'orders.contractor_specification.create',
             'chat-messages.store' => 'orders.chat.create',
             'chat-messages.store' => 'orders.chat.create',

+ 92 - 0
resources/views/orders/show.blade.php

@@ -242,6 +242,8 @@
                                     <th>RFID</th>
                                     <th>RFID</th>
                                     <th>Заводской номер</th>
                                     <th>Заводской номер</th>
                                     <th>Дата производства</th>
                                     <th>Дата производства</th>
+                                    <th>Номер ведомости</th>
+                                    <th>Дата ведомости</th>
                                     <th>Склад</th>
                                     <th>Склад</th>
                                     <th>Паспорт</th>
                                     <th>Паспорт</th>
                                 </tr>
                                 </tr>
@@ -325,6 +327,32 @@
                                                 {{ $p->manufacture_date }}
                                                 {{ $p->manufacture_date }}
                                             @endif
                                             @endif
                                         </td>
                                         </td>
+                                        <td>
+                                            @if(canUpdateField('maf', 'statement_number'))
+                                                <input
+                                                    type="text"
+                                                    class="form-control form-control-sm inline-product-sku-field"
+                                                    data-url="{{ route('product_sku.inline-update', $p->id) }}"
+                                                    data-field="statement_number"
+                                                    value="{{ $p->statement_number }}"
+                                                >
+                                            @else
+                                                {{ $p->statement_number }}
+                                            @endif
+                                        </td>
+                                        <td>
+                                            @if(canUpdateField('maf', 'statement_date'))
+                                                <input
+                                                    type="date"
+                                                    class="form-control form-control-sm inline-product-sku-field"
+                                                    data-url="{{ route('product_sku.inline-update', $p->id) }}"
+                                                    data-field="statement_date"
+                                                    value="{{ $p->statement_date }}"
+                                                >
+                                            @else
+                                                {{ $p->statement_date }}
+                                            @endif
+                                        </td>
                                         <td class="text-center">
                                         <td class="text-center">
                                             @if($p->maf_order?->order_number)
                                             @if($p->maf_order?->order_number)
                                                 <i class="bi bi-check-all text-success fw-bold"></i>
                                                 <i class="bi bi-check-all text-success fw-bold"></i>
@@ -410,6 +438,9 @@
                             @if(hasRole('admin'))
                             @if(hasRole('admin'))
                                 <a href="#" class="btn btn-primary btn-sm mb-1" id="ttnBtn">ТН</a>
                                 <a href="#" class="btn btn-primary btn-sm mb-1" id="ttnBtn">ТН</a>
                             @endif
                             @endif
+                            @if(canUpdateField('maf', 'statement_number') && canUpdateField('maf', 'statement_date'))
+                                <a href="#" class="btn btn-primary btn-sm mb-1" id="addStatementToMafsBtn">Добавить Ведомость</a>
+                            @endif
                             @if(hasRole('admin,assistant_head'))
                             @if(hasRole('admin,assistant_head'))
                                 <a href="#" class="btn btn-primary btn-sm mb-1" id="contractorSpecificationBtn">Спецификация</a>
                                 <a href="#" class="btn btn-primary btn-sm mb-1" id="contractorSpecificationBtn">Спецификация</a>
                             @endif
                             @endif
@@ -538,7 +569,35 @@
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>
+    @endif
+
+    @if(canUpdateField('maf', 'statement_number') && canUpdateField('maf', 'statement_date'))
+        <!-- Модальное окно добавления ведомости к МАФ -->
+        <div class="modal fade" id="addStatementToMafsModal" tabindex="-1" aria-labelledby="addStatementToMafsModalLabel" aria-hidden="true">
+            <div class="modal-dialog modal-fullscreen-sm-down modal-lg">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h1 class="modal-title fs-5" id="addStatementToMafsModalLabel">Добавить ведомость</h1>
+                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
+                    </div>
+                    <div class="modal-body">
+                        <form action="{{ route('order.add-statement-to-mafs', $order) }}" method="post" id="addStatementToMafsForm">
+                            @csrf
+                            <label for="statement_number" class="form-label">Номер ведомости</label>
+                            <input type="text" class="form-control mb-3" id="statement_number" name="statement_number" required>
 
 
+                            <label for="statement_date" class="form-label">Дата ведомости</label>
+                            <input type="date" class="form-control mb-3" id="statement_date" name="statement_date" value="{{ date('Y-m-d') }}" required>
+
+                            <button type="button" class="btn btn-primary" id="addStatementToMafs">Добавить</button>
+                        </form>
+                    </div>
+                </div>
+            </div>
+        </div>
+    @endif
+
+    @if(hasRole('admin,assistant_head'))
         <style>
         <style>
             #select_order {
             #select_order {
                 max-width: 100%;
                 max-width: 100%;
@@ -626,6 +685,39 @@
             });
             });
         @endif
         @endif
 
 
+        @if(canUpdateField('maf', 'statement_number') && canUpdateField('maf', 'statement_date'))
+            $('#addStatementToMafsBtn').on('click', function () {
+                if ($('input.check-maf:checkbox:checked').length > 0) {
+                    let modal = new bootstrap.Modal(document.getElementById("addStatementToMafsModal"), {});
+                    modal.show();
+                } else {
+                    customAlert('Выберите хотя бы один МАФ');
+                }
+            });
+
+            $('#addStatementToMafs').on('click', function () {
+                const form = document.getElementById('addStatementToMafsForm');
+                if (!form.reportValidity()) {
+                    return;
+                }
+
+                $('#addStatementToMafsForm input[name="skus[]"]').remove();
+                let ids = Array();
+                $('.check-maf').each(function () {
+                    if ($(this).prop('checked')) {
+                        ids.push($(this).attr('data-maf-id'));
+                        $('#addStatementToMafsForm').append('<input type="hidden" name="skus[]" value="' + $(this).attr('data-maf-id') + '">');
+                    }
+                });
+
+                if (ids.length) {
+                    form.submit();
+                } else {
+                    customAlert('Выберите хотя бы один МАФ');
+                }
+            });
+        @endif
+
         $('#create-reclamation-button').on('click', function () {
         $('#create-reclamation-button').on('click', function () {
             let ids = Array();
             let ids = Array();
             $('.check-maf').each(function () {
             $('.check-maf').each(function () {

+ 2 - 0
routes/web.php

@@ -250,6 +250,8 @@ Route::middleware(['auth:web', 'route.permission'])->group(function () {
     Route::post('order/{order}/contractor-specification', [OrderController::class, 'createContractorSpecification'])
     Route::post('order/{order}/contractor-specification', [OrderController::class, 'createContractorSpecification'])
         ->name('order.contractor-specification')
         ->name('order.contractor-specification')
         ->middleware('role:admin,' . Role::ASSISTANT_HEAD);
         ->middleware('role:admin,' . Role::ASSISTANT_HEAD);
+    Route::post('order/{order}/add-statement-to-mafs', [OrderController::class, 'addStatementToMafs'])
+        ->name('order.add-statement-to-mafs');
 
 
     Route::middleware('role:' . Role::ADMIN)->group(function () {
     Route::middleware('role:' . Role::ADMIN)->group(function () {
         Route::get('catalog/create', [ProductController::class, 'create'])->name('catalog.create');
         Route::get('catalog/create', [ProductController::class, 'create'])->name('catalog.create');

+ 72 - 0
tests/Feature/OrderControllerTest.php

@@ -504,6 +504,8 @@ class OrderControllerTest extends TestCase
             'rfid' => 'RFID-ORDER-OLD',
             'rfid' => 'RFID-ORDER-OLD',
             'factory_number' => 'FN-ORDER-OLD',
             'factory_number' => 'FN-ORDER-OLD',
             'manufacture_date' => '2026-05-19',
             'manufacture_date' => '2026-05-19',
+            'statement_number' => 'STAT-ORDER-OLD',
+            'statement_date' => '2026-05-18',
         ]);
         ]);
 
 
         $response = $this->actingAs($this->adminUser)
         $response = $this->actingAs($this->adminUser)
@@ -514,6 +516,10 @@ class OrderControllerTest extends TestCase
         $response->assertSee('data-field="rfid"', false);
         $response->assertSee('data-field="rfid"', false);
         $response->assertSee('data-field="factory_number"', false);
         $response->assertSee('data-field="factory_number"', false);
         $response->assertSee('data-field="manufacture_date"', false);
         $response->assertSee('data-field="manufacture_date"', false);
+        $response->assertSee('data-field="statement_number"', false);
+        $response->assertSee('data-field="statement_date"', false);
+        $response->assertSee('Добавить Ведомость');
+        $response->assertSee(route('order.add-statement-to-mafs', $order), false);
         $response->assertSee(route('product_sku.inline-update', $sku->id), false);
         $response->assertSee(route('product_sku.inline-update', $sku->id), false);
         $response->assertSee('upload-maf-passport', false);
         $response->assertSee('upload-maf-passport', false);
         $response->assertSee(route('product-sku.upload-passport', ['product_sku' => $sku]), false);
         $response->assertSee(route('product-sku.upload-passport', ['product_sku' => $sku]), false);
@@ -538,6 +544,72 @@ class OrderControllerTest extends TestCase
         $response->assertDontSee(route('product-sku.upload-passport', ['product_sku' => $sku]), false);
         $response->assertDontSee(route('product-sku.upload-passport', ['product_sku' => $sku]), false);
     }
     }
 
 
+    public function test_can_add_statement_to_selected_mafs_on_order(): void
+    {
+        $order = Order::factory()->create();
+        $otherOrder = Order::factory()->create();
+        $product = Product::factory()->create();
+        $selectedSku = ProductSKU::factory()->create([
+            'order_id' => $order->id,
+            'product_id' => $product->id,
+            'statement_number' => null,
+            'statement_date' => null,
+        ]);
+        $notSelectedSku = ProductSKU::factory()->create([
+            'order_id' => $order->id,
+            'product_id' => $product->id,
+            'statement_number' => null,
+            'statement_date' => null,
+        ]);
+        $otherOrderSku = ProductSKU::factory()->create([
+            'order_id' => $otherOrder->id,
+            'product_id' => $product->id,
+            'statement_number' => null,
+            'statement_date' => null,
+        ]);
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('order.add-statement-to-mafs', $order), [
+                'statement_number' => 'STAT-2026-05',
+                'statement_date' => '2026-05-19',
+                'skus' => [$selectedSku->id, $otherOrderSku->id],
+            ]);
+
+        $response->assertRedirect();
+        $response->assertSessionHas('success', 'Ведомость добавлена к МАФ: 1.');
+
+        $this->assertDatabaseHas('products_sku', [
+            'id' => $selectedSku->id,
+            'statement_number' => 'STAT-2026-05',
+            'statement_date' => '2026-05-19',
+        ]);
+        $this->assertDatabaseHas('products_sku', [
+            'id' => $notSelectedSku->id,
+            'statement_number' => null,
+            'statement_date' => null,
+        ]);
+        $this->assertDatabaseHas('products_sku', [
+            'id' => $otherOrderSku->id,
+            'statement_number' => null,
+            'statement_date' => null,
+        ]);
+    }
+
+    public function test_add_statement_to_mafs_requires_field_permissions(): void
+    {
+        $order = Order::factory()->create();
+        $sku = ProductSKU::factory()->create(['order_id' => $order->id]);
+
+        $response = $this->actingAs($this->managerUser)
+            ->post(route('order.add-statement-to-mafs', $order), [
+                'statement_number' => 'STAT-DENIED',
+                'statement_date' => '2026-05-19',
+                'skus' => [$sku->id],
+            ]);
+
+        $response->assertForbidden();
+    }
+
     public function test_brigadier_cannot_view_handed_over_order_details(): void
     public function test_brigadier_cannot_view_handed_over_order_details(): void
     {
     {
         $order = Order::factory()->create([
         $order = Order::factory()->create([