*/ private array $attributeColors = []; public function handle(): int { $this->attributeColors = Attribute::query()->where('attribute_type_id', 1)->get() ->mapWithKeys(fn (Attribute $a) => [(string) $a->id => $this->normalizeColor(strtok((string) $a->name, ' ') ?: '')]) ->all(); $findings = 0; $findings += $this->ordersWithoutAssignment((string) $this->option('since')); $findings += $this->colorMismatches(); $findings += $this->customerImagesWithoutMatch(); $findings += $this->missingFiles(); $findings += $this->duplicates(); $findings += $this->productsWithoutOrMultipleStandardLabels(); $this->newLine(); $this->info($findings === 0 ? 'Keine Befunde.' : $findings.' Befund(e). Es wurde nichts geändert.'); return self::SUCCESS; } /** Bestellungen mit White-Label-Produkt ohne Kundenzuordnung, obwohl der Partner anderswo Logos hat. */ private function ordersWithoutAssignment(string $since): int { $rows = DB::table('shopping_orders as so') ->join('shopping_order_items as soi', 'soi.shopping_order_id', '=', 'so.id') ->join('products as p', 'p.id', '=', 'soi.product_id') ->where('p.whitelabel', true) ->where('so.created_at', '>=', $since) ->whereNull('soi.deleted_at') ->whereNotNull('so.auth_user_id') ->whereIn('so.auth_user_id', fn ($q) => $q->select('user_id')->from('user_whitelabel_products')) ->whereNotExists(fn ($q) => $q->selectRaw('1')->from('user_whitelabel_products as uwp') ->whereColumn('uwp.user_id', 'so.auth_user_id') ->whereColumn('uwp.product_id', 'soi.product_id')) ->select('so.id as order_id', 'so.auth_user_id as user_id', 'soi.product_id', 'p.name', 'so.user_white_label', 'so.created_at') ->distinct() ->orderBy('so.id') ->get(); $this->section('A) Bestellungen ab '.$since.' ohne Kundenzuordnung (Partner hat Logos bei anderen Produkten)', $rows->count()); if ($rows->isNotEmpty()) { $this->table(['Bestellung', 'Partner', 'Produkt', 'Name', 'user_white_label', 'Datum'], $rows->map(fn ($r) => [$r->order_id, $r->user_id, $r->product_id, $r->name, $r->user_white_label ? 'ja' : 'nein', substr((string) $r->created_at, 0, 10)])->all()); } return $rows->count(); } /** Farbe am Kundenbild (Attribut) widerspricht der Farbe im Produktnamen "(Tiegel X)". */ private function colorMismatches(): int { $rows = []; foreach (UserWhitelabelProduct::with(['product', 'whitelabel_images'])->orderBy('id')->get() as $assignment) { $productColor = $this->productColor($assignment->product); if ($productColor === null) { continue; } foreach ($assignment->whitelabel_images as $image) { $imageColors = collect((array) $image->attributes) ->map(fn ($id) => $this->attributeColors[(string) $id] ?? null) ->filter()->unique()->values(); if ($imageColors->isNotEmpty() && ! $imageColors->contains($productColor)) { $rows[] = [$assignment->id, $assignment->user_id, $assignment->product_id, $assignment->product->name, $image->id, $imageColors->join(', ')]; } } } $this->section('B) Farbwiderspruch: Kundenbild-Variante passt nicht zur Produktfarbe', count($rows)); if ($rows !== []) { $this->table(['Zuordnung', 'Partner', 'Produkt', 'Produktname', 'Bild', 'Bildfarbe(n)'], $rows); } return count($rows); } /** Kundenbilder, die im Lieferschein nie greifen: ohne Variante oder ohne Überschneidung mit dem Standardetikett. */ private function customerImagesWithoutMatch(): int { $rows = []; foreach (UserWhitelabelProduct::with(['product.whitelabel_images', 'whitelabel_images'])->orderBy('id')->get() as $assignment) { if (! $assignment->product) { $rows[] = [$assignment->id, $assignment->user_id, $assignment->product_id, '-', 'Produkt existiert nicht']; continue; } $standardAttributes = $assignment->product->whitelabel_images ->flatMap(fn ($img) => (array) $img->attributes)->map(fn ($v) => (string) $v)->unique(); foreach ($assignment->whitelabel_images as $image) { $own = collect((array) $image->attributes)->map(fn ($v) => (string) $v); if ($own->isEmpty()) { $rows[] = [$assignment->id, $assignment->user_id, $assignment->product_id, $image->id, 'keine Variante am Kundenbild']; } elseif ($standardAttributes->isEmpty()) { $rows[] = [$assignment->id, $assignment->user_id, $assignment->product_id, $image->id, 'Produkt hat kein Standardetikett']; } elseif ($own->intersect($standardAttributes)->isEmpty()) { $rows[] = [$assignment->id, $assignment->user_id, $assignment->product_id, $image->id, 'keine Überschneidung mit Standardetikett ('.$own->join(',').' vs. '.$standardAttributes->join(',').')']; } } if ($assignment->whitelabel_images->isEmpty()) { $rows[] = [$assignment->id, $assignment->user_id, $assignment->product_id, '-', 'Zuordnung ohne Kundenbild']; } } $this->section('C) Kundenzuordnungen, die im Lieferschein nicht greifen', count($rows)); if ($rows !== []) { $this->table(['Zuordnung', 'Partner', 'Produkt', 'Bild', 'Grund'], $rows); } return count($rows); } private function missingFiles(): int { $rows = []; foreach (ProductImage::query()->whereIn('type', ['uwllogo', 'wllogo'])->orderBy('id')->get() as $image) { if (! file_exists($image->getBaseImagePath())) { $rows[] = [$image->id, $image->type, $image->product_id ?? '-', $image->user_wl_product_id ?? '-', $image->getImagePath()]; } } $this->section('D) Etikettbilder ohne Datei', count($rows)); if ($rows !== []) { $this->table(['Bild', 'Typ', 'Produkt', 'Zuordnung', 'Pfad'], $rows); } return count($rows); } private function duplicates(): int { $rows = DB::table('user_whitelabel_products') ->select('user_id', 'product_id', DB::raw('count(*) as c')) ->groupBy('user_id', 'product_id')->having('c', '>', 1)->get(); $this->section('E) Dubletten (user_id, product_id)', $rows->count()); if ($rows->isNotEmpty()) { $this->table(['Partner', 'Produkt', 'Anzahl'], $rows->map(fn ($r) => [$r->user_id, $r->product_id, $r->c])->all()); } return $rows->count(); } private function productsWithoutOrMultipleStandardLabels(): int { $rows = []; foreach (Product::query()->where('whitelabel', true)->withCount('whitelabel_images')->orderBy('id')->get() as $product) { if ($product->whitelabel_images_count === 0) { $rows[] = [$product->id, $product->name, 'kein Standardetikett']; } elseif ($product->whitelabel_images_count > 1) { $rows[] = [$product->id, $product->name, $product->whitelabel_images_count.' Standardetiketten (Mehrfachetikett prüfen)']; } if ($this->productColor($product) === null) { $rows[] = [$product->id, $product->name, 'keine Farbe im Produktnamen erkennbar']; } } $this->section('F) White-Label-Produkte mit fehlendem/mehrfachem Standardetikett oder ohne erkennbare Farbe', count($rows)); if ($rows !== []) { $this->table(['Produkt', 'Name', 'Befund'], $rows); } return count($rows); } private function productColor(?Product $product): ?string { if (! $product || ! preg_match('/\(Tiegel\s+([^)]+)\)/iu', (string) $product->name, $m)) { return null; } return $this->normalizeColor($m[1]); } private function normalizeColor(string $value): string { return str_replace(['ß', 'ss'], 'ss', mb_strtolower(trim($value))); } private function section(string $title, int $count): void { $this->newLine(); $this->line(''.$title.' – '.$count); } }