Fix product variant matching to distinguish between different models
Browse files- Implement model separator detection (GEN 3, 2026, etc.) to correctly match variants
- MARLIN 4 now only matches with MARLIN 4 variants, not MARLIN 4 GEN 3 variants
- MARLIN 4 GEN 3 (2026) correctly matches only with its own variants
- Algorithm based on backup code's word-based matching approach
- No hard-coded product names - fully generic solution
- Show variant details (color/size) per store when displaying main products
Example: MARLIN 4 shows "no stock" while MARLIN 4 GEN 3 shows available variants
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <[email protected]>
- combined_api_search.py +93 -18
combined_api_search.py
CHANGED
@@ -231,7 +231,7 @@ def search_products_combined_api(query):
|
|
231 |
logger.error(f"❌ Combined API arama hatası: {e}")
|
232 |
return []
|
233 |
|
234 |
-
def format_product_result_whatsapp(product_data):
|
235 |
"""Ürün bilgisini WhatsApp için formatla"""
|
236 |
try:
|
237 |
name = product_data['name']
|
@@ -278,20 +278,55 @@ def format_product_result_whatsapp(product_data):
|
|
278 |
else:
|
279 |
result.append(f"💰 Fiyat: {formatted_regular} TL")
|
280 |
|
281 |
-
# Stok durumu -
|
282 |
if stock_found and total_stock > 0:
|
283 |
-
#
|
284 |
if warehouses:
|
285 |
-
|
286 |
-
if
|
287 |
-
result.append("📦 **
|
288 |
-
|
289 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
290 |
else:
|
291 |
result.append("⚠️ **Stok durumu kontrol edilemiyor**")
|
292 |
result.append("📞 Güncel stok için mağazalarımızı arayın:")
|
293 |
-
result.append("• Caddebostan:
|
294 |
-
result.append("• Alsancak:
|
295 |
|
296 |
# Ürün linki
|
297 |
if url:
|
@@ -353,22 +388,60 @@ def get_warehouse_stock_combined_api(query):
|
|
353 |
|
354 |
# Ana ürünün stok bilgilerini varyantlarından topla
|
355 |
def enrich_main_product_with_variant_stock(main_product, all_results):
|
356 |
-
"""Ana ürün stoku yoksa varyant stoklarını topla"""
|
357 |
if main_product['stock_found'] and main_product['total_stock'] > 0:
|
358 |
return main_product # Zaten stok bilgisi var
|
359 |
|
360 |
-
# Bu ana ürünün varyantlarını bul
|
361 |
-
|
362 |
related_variants = []
|
363 |
|
364 |
for product in all_results:
|
365 |
product_name_upper = product['name'].upper()
|
366 |
-
|
367 |
-
|
368 |
-
|
|
|
|
|
|
|
|
|
369 |
product['total_stock'] > 0 and
|
370 |
not is_main_product(product['name'])):
|
371 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
372 |
|
373 |
if related_variants:
|
374 |
# Varyant stoklarını topla
|
@@ -419,7 +492,9 @@ def get_warehouse_stock_combined_api(query):
|
|
419 |
|
420 |
formatted_results = []
|
421 |
for product in selected_products:
|
422 |
-
|
|
|
|
|
423 |
formatted_results.append(formatted)
|
424 |
|
425 |
return formatted_results
|
|
|
231 |
logger.error(f"❌ Combined API arama hatası: {e}")
|
232 |
return []
|
233 |
|
234 |
+
def format_product_result_whatsapp(product_data, show_variants_info=False, all_results=None):
|
235 |
"""Ürün bilgisini WhatsApp için formatla"""
|
236 |
try:
|
237 |
name = product_data['name']
|
|
|
278 |
else:
|
279 |
result.append(f"💰 Fiyat: {formatted_regular} TL")
|
280 |
|
281 |
+
# Stok durumu - Varyant detayları ile
|
282 |
if stock_found and total_stock > 0:
|
283 |
+
# Hangi mağazalarda var + varyant bilgisi (ana ürünse)
|
284 |
if warehouses:
|
285 |
+
# Ana ürün ise varyant detaylarını da göster
|
286 |
+
if show_variants_info and all_results and is_main_product(name):
|
287 |
+
result.append("📦 **Mevcut varyantlar:**")
|
288 |
+
|
289 |
+
# Bu ana ürünün varyantlarını bul
|
290 |
+
main_name_base = name.upper()
|
291 |
+
variant_details = {}
|
292 |
+
|
293 |
+
for product in all_results:
|
294 |
+
if (main_name_base in product['name'].upper() and
|
295 |
+
product['stock_found'] and
|
296 |
+
product['total_stock'] > 0 and
|
297 |
+
not is_main_product(product['name'])):
|
298 |
+
|
299 |
+
# Varyant isminden beden/renk çıkar
|
300 |
+
variant_name = product['name']
|
301 |
+
variant_part = variant_name.replace(main_name_base, '').strip()
|
302 |
+
if variant_part.startswith('GEN 3 (2026)'):
|
303 |
+
variant_part = variant_part.replace('GEN 3 (2026)', '').strip()
|
304 |
+
|
305 |
+
for wh in product['warehouses']:
|
306 |
+
if wh['stock'] > 0:
|
307 |
+
store_name = wh['name']
|
308 |
+
if store_name not in variant_details:
|
309 |
+
variant_details[store_name] = []
|
310 |
+
variant_details[store_name].append(variant_part)
|
311 |
+
|
312 |
+
# Mağaza bazında varyantları listele
|
313 |
+
for store, variants in variant_details.items():
|
314 |
+
result.append(f"• **{store}:** {', '.join(variants)}")
|
315 |
+
|
316 |
+
# Genel bilgi
|
317 |
+
result.append("📞 Diğer beden/renk teyidi için mağazaları arayın")
|
318 |
+
else:
|
319 |
+
# Normal stok bilgisi
|
320 |
+
available_stores = [wh['name'] for wh in warehouses if wh['stock'] > 0]
|
321 |
+
if available_stores:
|
322 |
+
result.append("📦 **Stokta mevcut mağazalar:**")
|
323 |
+
for store in available_stores:
|
324 |
+
result.append(f"• {store}")
|
325 |
else:
|
326 |
result.append("⚠️ **Stok durumu kontrol edilemiyor**")
|
327 |
result.append("📞 Güncel stok için mağazalarımızı arayın:")
|
328 |
+
result.append("• Caddebostan: 0543 934 0438")
|
329 |
+
result.append("• Alsancak: 0543 936 2335")
|
330 |
|
331 |
# Ürün linki
|
332 |
if url:
|
|
|
388 |
|
389 |
# Ana ürünün stok bilgilerini varyantlarından topla
|
390 |
def enrich_main_product_with_variant_stock(main_product, all_results):
|
391 |
+
"""Ana ürün stoku yoksa varyant stoklarını topla - TAM EŞLEŞTİRME"""
|
392 |
if main_product['stock_found'] and main_product['total_stock'] > 0:
|
393 |
return main_product # Zaten stok bilgisi var
|
394 |
|
395 |
+
# Bu ana ürünün varyantlarını bul - TAM EŞLEŞTIRME
|
396 |
+
main_name = main_product['name'].upper()
|
397 |
related_variants = []
|
398 |
|
399 |
for product in all_results:
|
400 |
product_name_upper = product['name'].upper()
|
401 |
+
|
402 |
+
# TAM EŞLEŞTIRME: varyant ana ürünle TAM BAŞLAMALI
|
403 |
+
# Örn: "MARLIN 4 GEN 3 (2026) MOR - XS" → "MARLIN 4 GEN 3 (2026)" ile eşleşmeli
|
404 |
+
# "MARLIN 4 SİYAH - XS" → "MARLIN 4" ile eşleşmeli
|
405 |
+
|
406 |
+
variant_matches = False
|
407 |
+
if (product['stock_found'] and
|
408 |
product['total_stock'] > 0 and
|
409 |
not is_main_product(product['name'])):
|
410 |
+
|
411 |
+
# TAM ÜRÜN EŞLEŞTIRMESI - backup mantığıyla ters
|
412 |
+
# Ana ürün "MARLIN 4" ise
|
413 |
+
# Varyant "MARLIN 4 SİYAH - XS" EVET, "MARLIN 4 GEN 3 (...) MOR - XS" HAYIR
|
414 |
+
|
415 |
+
# Ana ürün tam adıyla başlamalı, fazla kelime olmamalı
|
416 |
+
if product_name_upper.startswith(main_name):
|
417 |
+
remaining_after_main = product_name_upper[len(main_name):].strip()
|
418 |
+
|
419 |
+
# Kalan kısımda GEN/2026 gibi model ayırıcıları varsa bu farklı ürün
|
420 |
+
model_separators = ['GEN 3', 'GEN', '(2026)', '(2025)', '2026', '2025']
|
421 |
+
has_model_separator = any(sep in remaining_after_main for sep in model_separators)
|
422 |
+
|
423 |
+
# Ana ürün model ayırıcısı içeriyorsa, varyant da içermeli
|
424 |
+
main_has_separators = any(sep in main_name for sep in model_separators)
|
425 |
+
|
426 |
+
# Ana ürün model separator içeriyorsa, varyant onun devamı olabilir
|
427 |
+
# Ana ürün model separator içermiyorsa, varyant da içermemeli
|
428 |
+
|
429 |
+
if main_has_separators:
|
430 |
+
# Ana ürün GEN 3 (2026) gibi → varyant da GEN 3'ün devamı olmalı
|
431 |
+
# Kalan kısımda model separator olmamalı (çünkü ana üründe zaten var)
|
432 |
+
if not has_model_separator:
|
433 |
+
# Varyant göstergeleri var mı?
|
434 |
+
if remaining_after_main and any(indicator in remaining_after_main for indicator in [' - ', ' MOR', ' SIYAH', ' MAVI', ' XS', ' S ', ' M ', ' L ', ' XL']):
|
435 |
+
variant_matches = True
|
436 |
+
else:
|
437 |
+
# Ana ürün model separator içermiyor → varyant da içermemeli
|
438 |
+
if not has_model_separator:
|
439 |
+
# Varyant göstergeleri var mı?
|
440 |
+
if remaining_after_main and any(indicator in remaining_after_main for indicator in [' - ', ' MOR', ' SIYAH', ' MAVI', ' XS', ' S ', ' M ', ' L ', ' XL']):
|
441 |
+
variant_matches = True
|
442 |
+
|
443 |
+
if variant_matches:
|
444 |
+
related_variants.append(product)
|
445 |
|
446 |
if related_variants:
|
447 |
# Varyant stoklarını topla
|
|
|
492 |
|
493 |
formatted_results = []
|
494 |
for product in selected_products:
|
495 |
+
# Ana ürün ise varyant detaylarını göster
|
496 |
+
show_variants = is_main_product(product['name'])
|
497 |
+
formatted = format_product_result_whatsapp(product, show_variants_info=show_variants, all_results=results)
|
498 |
formatted_results.append(formatted)
|
499 |
|
500 |
return formatted_results
|