Indizes auf shopping_orders.shipped/shipped_at/created_at (Board-Filter, Tages-Kacheln) und products.wp_number (SKU-Auflösung). Die API-Bestellungen-Seite lud das komplette JSON-payload für alle 500 Zeilen und serialisierte es in Inline-Attribute; jetzt persistierte positions_count, Liste ohne payload, Inhalt on-demand über show-Endpoint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
84 lines
3.2 KiB
PHP
84 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin\Inventory;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\ApiOrderLog;
|
|
use App\Models\Product;
|
|
use Illuminate\Http\Request;
|
|
|
|
/**
|
|
* AP-13: Übersicht der über die Partnercenter-API eingegangenen Bestell-Übertragungen.
|
|
* Einträge mit fehlgeschlagener Produktzuordnung (unbekannte SKU) sind markiert;
|
|
* das Modal zeigt den Inhalt der Übertragung je Position.
|
|
*/
|
|
class ApiOrderLogController extends Controller
|
|
{
|
|
private const INDEX_LIMIT = 500;
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$limit = $request->query('all') ? null : self::INDEX_LIMIT;
|
|
|
|
// Ohne payload (Review 20.07.2026): der Inhalt wird erst im Modal per show()
|
|
// nachgeladen, statt für alle Zeilen mehrere MB JSON in die Seite zu rendern.
|
|
$logs = ApiOrderLog::query()
|
|
->with(['shoppingUser:id,billing_firstname,billing_lastname'])
|
|
->select(['id', 'shopping_order_id', 'shopping_user_id', 'wp_order_number', 'positions_count', 'has_errors', 'error_summary', 'created_at'])
|
|
->orderByDesc('id')
|
|
->when($limit !== null, fn ($query) => $query->limit($limit))
|
|
->get();
|
|
|
|
$errorCount = ApiOrderLog::query()->where('has_errors', true)->count();
|
|
$logTotal = ApiOrderLog::query()->count();
|
|
|
|
// SKU-Konsistenz (Plan §4): doppelte WP-Nummern brechen die Zuordnung,
|
|
// aktive Produkte ohne WP-Nummer können nie per API zugeordnet werden.
|
|
$duplicateNumbers = Product::query()
|
|
->where('active', true)
|
|
->whereNotNull('wp_number')
|
|
->where('wp_number', '!=', 0)
|
|
->selectRaw('wp_number, COUNT(*) as sku_count')
|
|
->groupBy('wp_number')
|
|
->havingRaw('COUNT(*) > 1')
|
|
->pluck('wp_number');
|
|
|
|
$duplicateSkuProducts = $duplicateNumbers->isEmpty()
|
|
? collect()
|
|
: Product::query()
|
|
->where('active', true)
|
|
->whereIn('wp_number', $duplicateNumbers)
|
|
->orderBy('wp_number')
|
|
->orderBy('name')
|
|
->get(['id', 'name', 'wp_number']);
|
|
|
|
$missingSkuProducts = Product::query()
|
|
->where('active', true)
|
|
->where(fn ($query) => $query->whereNull('wp_number')->orWhere('wp_number', 0))
|
|
->orderBy('name')
|
|
->get(['id', 'name']);
|
|
|
|
return view('admin.inventory.api-orders.index', [
|
|
'logs' => $logs,
|
|
'listLimit' => $limit,
|
|
'listTotal' => $logTotal,
|
|
'errorCount' => $errorCount,
|
|
'duplicateSkuProducts' => $duplicateSkuProducts,
|
|
'missingSkuProducts' => $missingSkuProducts,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Inhalt einer Übertragung on-demand fürs Modal (Review 20.07.2026): liefert die
|
|
* Positionen bzw. den Roh-Body als JSON, damit die Liste ohne payload auskommt.
|
|
*/
|
|
public function show(ApiOrderLog $apiOrderLog)
|
|
{
|
|
return response()->json([
|
|
'lines' => $apiOrderLog->payload['lines'] ?? [],
|
|
'raw' => isset($apiOrderLog->payload['raw'])
|
|
? json_encode($apiOrderLog->payload['raw'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
|
: null,
|
|
]);
|
|
}
|
|
}
|