Ungültige Geräte-ID.';
exit;
}
try {
$db = new KremplDB(true);
$buy = $db->getAvailableItemsForGeraet($gid, 200);
if ($buy) {
echo '
Zu diesem Gerät sind in den Herstellerdaten '
. 'keine Ersatzteile hinterlegt.
';
} else {
echo 'Für dieses Gerät sind Ersatzteile bekannt, aber '
. 'derzeit keines davon im Shop verfügbar.
';
}
}
} catch (Exception $e) {
http_response_code(500);
echo 'Artikel konnten nicht geladen werden.
';
}
exit;
}
$search_term = isset($_GET['q']) ? trim($_GET['q']) : '';
$results = [];
$item_results = [];
$search_time = 0;
$item_search_time = 0;
$error_message = '';
// "Nur verfuegbare Shop-Artikel" ist jetzt der IST-Zustand (Default AN): das verkleinert die
// Trefferliste auf real bestellbare Teile, und getAvailableItemsForGeraet() liefert dabei
// items.id mit -- also sind die Chips direkt in den Shop verlinkbar. Abschaltbar mit
// ?shop_items=0 (die Checkbox sendet das ueber ein vorgeschaltetes hidden-Feld).
$show_shop_items = !isset($_GET['shop_items']) || $_GET['shop_items'] !== '0';
$v2_guided = false; // gefuehrte Ansicht aktiv (ein klarer Top-Treffer)
$v2_top_detail = null; // Volldaten des Top-Geraets aus geraete
$v2_brandpick = []; // marke => anzahl, wenn erst der Hersteller geklaert werden muss
$v2_marke = isset($_GET['marke']) ? trim($_GET['marke']) : ''; // gewaehlter Hersteller
$v2_variants = []; // BSH-Bauzustaende /01 /02 ..., wenn nur der Zeitraum unterscheidet
$v2_variant_base = ''; // gemeinsame Typnummer ohne Suffix, z.B. "WT46W560BY"
$v2_shopmap = []; // navision_id => Shop-Artikel, fuer die Links an den Chips
$v2_partcounts = []; // geraet_id => Anzahl Ersatzteile (fuer die eingeklappten Kacheln)
try {
// MySQL wird in beiden Fällen gebraucht (rechte Spalte = Artikel-Suche)
$db = new KremplDB(true);
if ($search_term !== '') {
// Links: Geräte-Suche (PostgreSQL)
$start_time = microtime(true);
// Ersatzteile je Treffer nur im Debug-Modus (?shop_items=0) mitladen -- dort zeigen die
// Kacheln Ersatzteil-Chips statt des Lazy-Aufklappers. Im Normalfall kostet das 20
// ueberfluessige PG-Queries, deren Ergebnis nur der Top-Treffer braucht.
$results = $db->searchGeraete($search_term, 20, !$show_shop_items);
$search_time = (microtime(true) - $start_time) * 1000;
// Rechts: Artikel-/Ersatzteil-Suche (MySQL)
$start_time2 = microtime(true);
$item_results = $db->searchItems($search_term, 20);
$item_search_time = (microtime(true) - $start_time2) * 1000;
// --- Hersteller-Filter (kommt aus der Hersteller-Rueckfrage) ---
if ($v2_marke !== '') {
$results = array_values(array_filter($results, function ($d) use ($v2_marke) {
return isset($d['marke']) && trim($d['marke']) === $v2_marke;
}));
}
// --- Stufe 1: muss erst der Hersteller geklaert werden? ---
// Dasselbe Modell laeuft oft unter mehreren Konzern-/Versandhausmarken. Dann haben
// alle Top-Treffer denselben Score und denselben Modellnamen -- eine Bestaetigung
// waere geraten. Der Hersteller steht dem Kunden aber auf dem Typenschild zur
// Verfuegung, also fragen wir zuerst danach. Beispiel MUFF21VLQS: 10x Score 100
// ueber AEG, Electrolux, Frigidaire, Gibson.
if (count($results) > 1) {
$top_score = (int)$results[0]['score'];
$topgroup = array_filter($results, function ($d) use ($top_score) {
return (int)$d['score'] === $top_score;
});
$brands = [];
$models = [];
foreach ($topgroup as $d) {
$m = trim($d['marke'] ?? '');
if ($m !== '') { $brands[$m] = ($brands[$m] ?? 0) + 1; }
$models[mb_strtolower(trim($d['modell_bezeichnung'] ?? ''))] = true;
}
// Nur fragen, wenn es wirklich EIN Modell bei MEHREREN Marken ist.
if (count($brands) > 1 && count($models) === 1) {
arsort($brands);
$v2_brandpick = $brands;
}
// --- Stufe 1b: Varianten-Auswahl (BSH-Bauzustaende) ---
// SIEMENS/BOSCH haengen "/01", "/02" ... an die Typnummer in nr. Laesst der Kunde
// das Suffix weg ("WT46W560BY"), kommen alle Bauzustaende mit gleichem Score
// zurueck und unterscheiden sich NUR im Produktionszeitraum. Dann fragen wir
// danach, statt zehn optisch identische Zeilen zu zeigen.
if (!$v2_brandpick) {
$basis = [];
foreach ($topgroup as $d) {
$n = trim($d['nr'] ?? '');
$basis[($p = strpos($n, '/')) !== false ? substr($n, 0, $p) : $n] = true;
}
if (count($basis) === 1 && count($topgroup) > 1 && count($brands) <= 1) {
$v2_variant_base = (string)array_key_first($basis);
// Nur wenn die Treffer wirklich Suffix-Varianten sind, nicht zufaellig
// gleichnamige Geraete: mindestens einer muss ein "/" tragen.
$has_suffix = false;
foreach ($topgroup as $d) { if (strpos((string)($d['nr'] ?? ''), '/') !== false) { $has_suffix = true; break; } }
if ($has_suffix) {
$det_batch = $db->getGeraetDetailsBatch(array_column($topgroup, 'geraet_id'));
foreach ($topgroup as $d) {
$det = $det_batch[$d['geraet_id']] ?? [];
$v2_variants[] = [
'nr' => (string)($d['nr'] ?? ''),
'id' => $d['geraet_id'],
'von' => $det['produktionsstart'] ?? '',
'bis' => $det['produktionsende'] ?? '',
'typ_de'=> $det['typ_de'] ?? '',
'marke' => $d['marke'] ?? '',
];
}
usort($v2_variants, function ($a, $b) { return strcmp($a['nr'], $b['nr']); });
}
}
}
}
// --- Stufe 2: gibt es EINEN klaren Top-Treffer? ---
// Nur dann darf man "Das ist dein Geraet" behaupten. Bedingung: Top-Score >= 95
// UND mindestens 10 Punkte Abstand zum Zweiten. Beispiel "Lavatherm 400": 100 vs. 75.
// Entfaellt, solange die Hersteller-Frage offen ist.
if (!$v2_brandpick && !$v2_variants && count($results) > 0) {
$v2_top_score_raw = (int)$results[0]['score'];
$v2_second_raw = isset($results[1]) ? (int)$results[1]['score'] : 0;
if ($v2_top_score_raw >= 95 && ($v2_top_score_raw - $v2_second_raw) >= 10) {
$v2_guided = true;
$v2_top_detail = $db->getGeraetDetails($results[0]['geraet_id']);
}
}
// --- Ersatzteile + Shop-Artikel NUR fuer den Top-Treffer vorladen ---
// Bewusst erst hier, nach Hersteller-Filter und Stufen-Ermittlung: vorher stand an
// Position 0 evtl. ein anderes Geraet. Die uebrigen Kacheln sind eingeklappt und
// laden per AJAX nach (siehe Endpoint am Dateianfang) -- sie brauchen bis dahin nur
// die Anzahl, die ein einzelner Batch-Query liefert statt 20 Einzelabfragen.
if (isset($results[0])) {
$results[0]['ersatzteile'] = $db->getErsatzteileForGeraet($results[0]['geraet_id']);
if ($show_shop_items) {
$results[0]['shop_items'] = $db->getAvailableItemsForGeraet($results[0]['geraet_id'], 200);
}
}
$v2_partcounts = count($results) > 1
? $db->getErsatzteilCountsBatch(array_column($results, 'geraet_id'))
: [];
// --- Shop-Links fuer die Ersatzteil-Chips ---
// Die Chips kommen aus PostgreSQL und kennen nur navision_id. Damit sie -- wie die
// Artikel weiter unten -- in den Shop verlinken, wird einmalig gesammelt aufgeloest.
// Nur die tatsaechlich gerenderten (max. 10 je Geraet), sonst waeren es Tausende.
// Nur fuer den Top-Treffer: dessen Ersatzteil-Chips sind die einzigen, die ohne
// Lazy-Load gerendert werden. Vorher sammelte das die navision_ids ALLER 20 Geraete
// (bis 200 Stueck) fuer einen IN-Query, dessen Ergebnis niemand brauchte.
if (isset($results[0])) {
$nav_ids = [];
foreach (array_slice($results[0]['ersatzteile'] ?? [], 0, 10) as $p) {
if (!empty($p['navision_id'])) { $nav_ids[] = $p['navision_id']; }
}
if ($nav_ids) { $v2_shopmap = $db->getShopItemsByNavisionIds($nav_ids); }
}
}
} catch (Exception $e) {
$error_message = $e->getMessage();
}
?>
Krempl PostgreSQL Search Test
Fehler bei der Datenbankverbindung:
Bauzustand -> Bestaetigung.
// ==================================================================
// Treffer in "stark" (>=75) und "schwach" (<75) teilen
$v2_strong = []; $v2_weak = [];
foreach ($results as $d) { ((int)$d['score'] >= 75) ? $v2_strong[] = $d : $v2_weak[] = $d; }
// Artikel klassifizieren: kaufbar / ausgelaufen-mit-Nachfolger / tot
$g_buy = []; $g_succ = []; $g_dead = [];
foreach ($item_results as $it) {
$it['_nml'] = v2_nml_full($it);
// Ob ein Ersatz existiert, entscheidet primaer die im Backend gepflegte Zuordnung
// (item_item_assign type=3, angehaengt als $it['alt'] von KremplDB). Sie erreicht
// 4.150 aktive Artikel gegenueber 71 beim Namens-Regex, und ihr Ziel ist nachweislich
// kaufbar. Der Namenshinweis bleibt nur als Rueckfallebene.
if (!$it['_nml']['is_nml']) $g_buy[] = $it; // regulär
elseif (!empty($it['alt']) || $it['_nml']['nachfolger']) $g_succ[] = $it; // ausgelaufen, Ersatz da
else $g_dead[] = $it; // ausgelaufen, kein Ersatz
}
$v2_top_score = isset($results[0]) ? (int)$results[0]['score'] : 0;
// Intent-Einordnung
// Der Leerfall MUSS zuerst geprueft werden: sonst greift der else-Zweig und verspricht
// "Mehrere moegliche Treffer", obwohl 0 Geraete und 0 Artikel gefunden wurden
// (z.B. bei einem Tippfehler wie "spannrolle boisch").
$v2_leer = (count($results) === 0 && count($item_results) === 0);
if ($v2_leer) $v2_headline = 'Dazu haben wir nichts gefunden.';
elseif ($v2_top_score >= 90) $v2_headline = 'Wir haben dein Gerät gefunden.';
elseif (count($v2_strong) > 0) $v2_headline = 'Das könnte dein Gerät sein:';
elseif (count($item_results)) $v2_headline = 'Passende Artikel gefunden:';
else $v2_headline = 'Mehrere mögliche Treffer – grenz gern ein.';
?>
Prüf bitte die Schreibweise, oder gib die Typnummer vom Typenschild ein
(z.B. WT46W560BY oder Lavatherm 400).
· kaufbare Artikel
Mehrere Hersteller führen dieses Modell
Welcher Hersteller steht auf deinem Gerät?
Der Hersteller steht meist auf dem Typenschild oder als Logo auf der Blende.
Dieses Modell gibt es in mehreren Bauzuständen
· Varianten
Wann wurde dein Gerät gebaut? Die Nummer auf dem Typenschild endet auf
/01, /02 … – wenn du sie findest, ist das der schnellste Weg.
Ist das dein Gerät?
Verfügbare Artikel für dieses Gerät ():
passende Ersatzteile — Auswahl:
10): ?>
+ weitere
count($a); });
$v2_list = [];
foreach ($by_marke as $m => $ds) {
$v2_mcount[$m] = count($ds);
foreach ($ds as $d) { $d['_mgroup'] = $m; $v2_list[] = $d; }
}
}
?>
$d):
list($clabel,$ccol,$cbg,$cicon) = v2_confidence($d['score']);
$parts = $d['ersatzteile'] ?? [];
$buy = $d['shop_items'] ?? null; // nur wenn shop_items-Toggle an
if (!$v2_alt_open && ($v2_guided && $v2_top_detail)) {
echo '
Nicht mein Gerät – '
. $v2_alt_total . ' weitere Treffer anzeigen
';
$v2_alt_open = true;
}
// Bei der Varianten-Karte stehen dieselben Geraete schon als Buttons oben --
// die ausfuehrliche Liste (mit Ersatzteilen) deshalb ebenfalls einklappen.
if (!$v2_alt_open && $v2_variants) {
echo 'Alle '
. count($v2_variants) . ' Bauzustände mit Ersatzteilen anzeigen
';
$v2_alt_open = true;
}
// Dito bei der Hersteller-Rueckfrage: erst die Marke klaeren, die Rohliste
// (oft 20 optisch identische Kacheln) gehoert dahinter.
if (!$v2_alt_open && $v2_brandpick) {
echo 'Alle '
. count($results) . ' Treffer ungefiltert anzeigen
';
$v2_alt_open = true;
}
if (isset($d['_mgroup']) && $d['_mgroup'] !== $v2_last_mgroup) {
$v2_last_mgroup = $d['_mgroup'];
echo ''.htmlspecialchars($v2_last_mgroup)
. ' '.(int)($v2_mcount[$v2_last_mgroup] ?? 0).'
';
}
?>
Verfügbare Artikel für dieses Gerät ():
Verfügbare Artikel im Shop anzeigen
Zu diesem Gerät sind in den Herstellerdaten keine Ersatzteile hinterlegt.
passende Ersatzteile — Auswahl:
10): ?>
+ weitere
Weitere mögliche Geräte anzeigen () – schwächere Treffer
'; }
?>
🛒 Diese Artikel kannst du direkt kaufen
0 -- 10.160x 0, 8.724x NULL).
// Bestellbar steuert sie ueber items.active, und searchItems() filtert
// bereits mit WHERE i.active = 1 -> alles hier ist bestellbar.
// Shop-Artikel und Bilder liegen nur auf LIVE -> absolute URLs
$url = v2_shopurl($it['id'] ?? null, $it['short_uri'] ?? null);
$img = !empty($it['default_image_file_name'])
? tba_url('web/intelectra_shop/images/article/'.rawurlencode($it['default_image_file_name']))
: '';
$pb = isset($it['price_brutto']) ? (float)$it['price_brutto'] : 0;
$bb = !empty($it['bargain_brutto']) ? (float)$it['bargain_brutto'] : 0;
?>
kein Bild
0 && $pb > 0 && $bb < $pb): ?>
€
€
0): ?>
€
Preis auf Anfrage
0): ?>
inkl. MwSt.
Zum Artikel
🔄 Ausgelaufen – aber es gibt einen Nachfolger
Art-Nr · nicht mehr lieferbar
landete in
// 18 von 58 Faellen wieder bei einem ausgelaufenen Artikel.
$ap = isset($alt['price_brutto']) ? (float)$alt['price_brutto'] : 0;
$ab = !empty($alt['bargain_brutto']) ? (float)$alt['bargain_brutto'] : 0;
?>
→ Ersatz verfügbar
Art-Nr
0 && $ap > 0 && $ab < $ap): ?>
· €
0): ?>
· €
1): ?>
· über Nachfolger ermittelt
→ Zum Ersatzartikel
Nicht mehr lieferbar, kein Ersatz () – ausgeblendet
Gib einen Suchbegriff ein (z.B. "AWB 921 PH", "ZCS 2100B", "Kohlefilter Bauknecht")