Преглед на файлове

orders: totals column and filtered footer stats

Alexander Musikhin преди 2 седмици
родител
ревизия
a879cb0390

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

@@ -51,6 +51,7 @@ class OrderController extends Controller
             'object_address'            => 'Адрес объекта',
             'object_type_name'          => 'Тип объекта',
             'comment'                   => 'Комментарий',
+            'products_total'            => 'Всего МАФ',
             'installation_date'         => 'Дата выхода на монтаж',
             'ready_date'                => 'Дата готовности площадки',
             'brigadier_name'            => 'Бригадир',
@@ -113,6 +114,9 @@ class OrderController extends Controller
             $q->whereNotNull('installation_date');
         }
 
+        $this->data['filtered_orders_count'] = (clone $q)->count();
+        $this->data['filtered_products_total'] = (clone $q)->sum('products_total');
+
         $this->applyStableSorting($q);
         $this->data['orders'] = $q->paginate($this->data['per_page'])->withQueryString();
 

+ 1 - 0
app/Models/OrderView.php

@@ -38,6 +38,7 @@ class OrderView extends Model
         'object_type_name',
         'brigadier_name',
         'order_status_name',
+        'products_total',
     ];
 
     public $appends = ['common_name', 'move_maf_name', 'products_with_count'];

+ 59 - 0
database/migrations/2026_04_11_120000_add_products_total_to_orders_view.php

@@ -0,0 +1,59 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Support\Facades\DB;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        DB::statement('DROP VIEW IF EXISTS orders_view');
+
+        DB::statement(<<<'SQL'
+            CREATE VIEW orders_view AS
+            SELECT
+                o.*,
+                (
+                    SELECT COUNT(*)
+                    FROM products_sku ps
+                    WHERE ps.order_id = o.id
+                      AND ps.deleted_at IS NULL
+                ) AS products_total,
+                u.name AS user_name,
+                d.shortname AS district_name,
+                a.name AS area_name,
+                ot.name AS object_type_name,
+                u2.name AS brigadier_name,
+                os.name AS order_status_name
+            FROM orders o
+                LEFT JOIN users u ON u.id = o.user_id
+                LEFT JOIN districts d ON d.id = o.district_id
+                LEFT JOIN areas a ON a.id = o.area_id
+                LEFT JOIN object_types ot ON ot.id = o.object_type_id
+                LEFT JOIN users u2 ON u2.id = o.brigadier_id
+                LEFT JOIN order_statuses os ON os.id = o.order_status_id
+            SQL);
+    }
+
+    public function down(): void
+    {
+        DB::statement('DROP VIEW IF EXISTS orders_view');
+
+        DB::statement(<<<'SQL'
+            CREATE VIEW orders_view AS
+            SELECT o.*, u.name as user_name,
+                d.shortname as district_name,
+                a.name as area_name,
+                ot.name as object_type_name,
+                u2.name as brigadier_name,
+                os.name as order_status_name
+            FROM orders o
+                LEFT JOIN users u ON u.id = o.user_id
+                LEFT JOIN districts d ON d.id = o.district_id
+                LEFT JOIN areas as a ON a.id = o.area_id
+                LEFT JOIN object_types ot ON ot.id = o.object_type_id
+                LEFT JOIN users u2 ON u2.id = o.brigadier_id
+                LEFT JOIN order_statuses os ON os.id = o.order_status_id
+            SQL);
+    }
+};

+ 7 - 1
resources/views/orders/index.blade.php

@@ -31,7 +31,13 @@
         'routeName' => 'order.show',
     ])
 
-    @include('partials.pagination', ['items' => $orders])
+    @include('partials.pagination', [
+        'items' => $orders,
+        'summary' => [
+            'Адресов' => $filtered_orders_count ?? 0,
+            'МАФ' => $filtered_products_total ?? 0,
+        ],
+    ])
 
     @if(hasRole('admin,manager'))
         <div class="modal fade" id="exportOrdersModal" tabindex="-1" aria-labelledby="exportOrdersModalLabel" aria-hidden="true">

+ 12 - 0
resources/views/partials/pagination.blade.php

@@ -15,6 +15,18 @@
     </div>
 </div>
 
+@if(isset($summary) && is_array($summary))
+    <div class="row px-3 pb-2">
+        <div class="col-12">
+            <div class="small text-muted">
+                @foreach($summary as $label => $value)
+                    <span class="me-3">{{ $label }}: {{ $value }}</span>
+                @endforeach
+            </div>
+        </div>
+    </div>
+@endif
+
 
 @push('scripts')
     <script type="module">

+ 55 - 0
tests/Feature/OrderControllerTest.php

@@ -72,6 +72,61 @@ class OrderControllerTest extends TestCase
         $response->assertSee($order->object_address);
     }
 
+    public function test_orders_index_displays_products_total_column_value(): void
+    {
+        $order = Order::factory()->create([
+            'object_address' => 'ул. Колоночная, д. 10',
+        ]);
+        $product = Product::factory()->create();
+
+        ProductSKU::factory()->count(3)->create([
+            'order_id' => $order->id,
+            'product_id' => $product->id,
+        ]);
+
+        $response = $this->actingAs($this->managerUser)
+            ->get(route('order.index'));
+
+        $response->assertOk();
+        $response->assertSee('Всего МАФ');
+        $response->assertSee('ул. Колоночная, д. 10');
+        $response->assertViewHas('orders', static function ($orders) use ($order) {
+            return (int) $orders->firstWhere('id', $order->id)?->products_total === 3;
+        });
+    }
+
+    public function test_orders_index_displays_filtered_footer_stats(): void
+    {
+        $district = District::factory()->create(['shortname' => 'ЦАО']);
+        $area = Area::factory()->create(['district_id' => $district->id, 'name' => 'Тверской']);
+        $matchingOrder = Order::factory()->create([
+            'district_id' => $district->id,
+            'area_id' => $area->id,
+            'object_address' => 'ул. Итоговая, д. 1',
+        ]);
+        $otherOrder = Order::factory()->create([
+            'object_address' => 'ул. Неучтенная, д. 2',
+        ]);
+        $product = Product::factory()->create();
+
+        ProductSKU::factory()->count(2)->create([
+            'order_id' => $matchingOrder->id,
+            'product_id' => $product->id,
+        ]);
+        ProductSKU::factory()->count(5)->create([
+            'order_id' => $otherOrder->id,
+            'product_id' => $product->id,
+        ]);
+
+        $response = $this->actingAs($this->managerUser)
+            ->get(route('order.index', ['s' => 'Итоговая']));
+
+        $response->assertOk();
+        $response->assertSee('Адресов: 1', false);
+        $response->assertSee('МАФ: 2', false);
+        $response->assertDontSee('МАФ: 7', false);
+    }
+
     public function test_brigadier_sees_only_assigned_orders(): void
     {
         $assignedOrder = Order::factory()->create([