/** * Optimized BTS Stock & Price Sync * * CHANGES FROM ORIGINAL: * 1. Memory management: Cache flush and garbage collection every 500 products * 2. Chunking: Process products in batches instead of all at once * 3. Optimized queries: Single JOIN query to fetch all meta data at once * 4. Verbose logging: Memory usage and detailed progress reporting * 5. Configurable chunk size via --chunk-size parameter * * @param bool $dry_run * @param bool $safe_mode * @param bool $zero_missing * @param int $chunk_size Number of products to process per chunk (default: 500) * @return array */ function master_sync_bts_optimized($dry_run = false, $safe_mode = false, $zero_missing = false, $chunk_size = 500) { WP_CLI::log(''); WP_CLI::log('📦 BTS WHOLESALER SYNC (OPTIMIZED)'); WP_CLI::log('================================='); $start_time = microtime(true); $start_memory = memory_get_usage(true); try { require_once __DIR__ . '/../api/class-parfumselect-bts-api.php'; $bts_api = new Parfumselect_BTS_API(); // STEP 1: Fetch API products WP_CLI::log('📥 Fetching products from BTS API...'); $api_products = $bts_api->getProducts(); if (empty($api_products)) { throw new Exception("Geen producten ontvangen van BTS API"); } WP_CLI::log('✅ Received ' . number_format(count($api_products)) . ' products from BTS API'); // STEP 2: Build EAN map WP_CLI::log('🗂️ Building EAN map...'); $ean_map = []; foreach ($api_products as $product) { $ean = trim($product['ean'] ?? ''); if (!empty($ean)) { $ean_map[$ean] = $product; } } WP_CLI::log('✅ EAN map built with ' . number_format(count($ean_map)) . ' entries'); // Free up memory from API products array unset($api_products); gc_collect_cycles(); // STEP 3: Fetch products with EAN using optimized JOIN query WP_CLI::log('🔍 Fetching local products with EAN...'); global $wpdb; $local_products = $wpdb->get_results( "SELECT DISTINCT p.ID as post_id, ean.meta_value as ean, bts_stock.meta_value as current_bts_stock, bts_cost.meta_value as current_bts_cost FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} ean ON ( p.ID = ean.post_id AND ean.meta_key = '_global_unique_id' AND ean.meta_value != '' ) LEFT JOIN {$wpdb->postmeta} bts_stock ON ( p.ID = bts_stock.post_id AND bts_stock.meta_key = '_supplier_bts_stock' ) LEFT JOIN {$wpdb->postmeta} bts_cost ON ( p.ID = bts_cost.post_id AND bts_cost.meta_key = '_supplier_bts_cost' ) WHERE p.post_type = 'product' AND p.post_status = 'publish'", OBJECT_K ); WP_CLI::log('✅ Found ' . number_format(count($local_products)) . ' local products with EAN'); $results = [ 'updated' => 0, 'skipped' => 0, 'unchanged' => 0, ]; // STEP 4: Process in chunks WP_CLI::log(''); WP_CLI::log('🔄 Processing products in chunks of ' . number_format($chunk_size)); WP_CLI::log(' Memory limit: ' . ini_get('memory_limit')); WP_CLI::log(' Start memory: ' . size_format(memory_get_usage(true))); WP_CLI::log(''); $product_ids = array_keys($local_products); $total_products = count($product_ids); $chunks = array_chunk($product_ids, $chunk_size); $total_chunks = count($chunks); foreach ($chunks as $chunk_index => $chunk_product_ids) { $chunk_num = $chunk_index + 1; $chunk_start = $chunk_index * $chunk_size + 1; $chunk_end = min($chunk_num * $chunk_size, $total_products); WP_CLI::log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); WP_CLI::log("📦 Processing Chunk {$chunk_num}/{$total_chunks} (products {$chunk_start}-{$chunk_end})"); WP_CLI::log(" Memory before: " . size_format(memory_get_usage(true))); $chunk_updated = 0; $chunk_unchanged = 0; $chunk_skipped = 0; foreach ($chunk_product_ids as $product_id) { if (!isset($local_products[$product_id])) { $chunk_skipped++; continue; } $product_data = $local_products[$product_id]; $ean = trim($product_data->ean); if (empty($ean) || !isset($ean_map[$ean])) { $chunk_skipped++; continue; } $api_product = $ean_map[$ean]; // Clean price string (e.g., "13.31€" -> 13.31) $price_string = $api_product['price'] ?? '0'; $cleaned_string = preg_replace('/[^0-9,.]/', '', $price_string); $cleaned_string = str_replace(',', '.', $cleaned_string); $new_cost = (float) $cleaned_string; $new_stock = (int) ($api_product['stock'] ?? 0); $current_bts_stock = (int) ($product_data->current_bts_stock ?? 0); $current_bts_cost = (float) ($product_data->current_bts_cost ?? 0); if ($current_bts_stock !== $new_stock || $current_bts_cost !== $new_cost) { if (!$dry_run) { update_post_meta($product_id, '_supplier_bts_stock', $new_stock); update_post_meta($product_id, '_supplier_bts_cost', $new_cost); update_post_meta($product_id, '_supplier_bts_id', $ean); if (function_exists('calculate_best_supplier')) { calculate_best_supplier($product_id, $safe_mode); } wc_delete_product_transients($product_id); } $chunk_updated++; } else { $chunk_unchanged++; } } $results['updated'] += $chunk_updated; $results['unchanged'] += $chunk_unchanged; $results['skipped'] += $chunk_skipped; // MEMORY MANAGEMENT: Clear cache and run garbage collection wp_cache_flush(); if (function_exists('gc_collect_cycles')) { gc_collect_cycles(); } $peak_memory = memory_get_usage(true); WP_CLI::log(" Updated: {$chunk_updated} | Unchanged: {$chunk_unchanged} | Skipped: {$chunk_skipped}"); WP_CLI::log(" Memory after: " . size_format($peak_memory)); WP_CLI::log(" Peak memory: " . size_format(memory_get_peak_usage(true))); } WP_CLI::log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); WP_CLI::log(''); // STEP 5: Zero missing products (if enabled) if ($zero_missing && !$dry_run) { WP_CLI::log('🔍 Controleren op ontbrekende producten in BTS API...'); $zeroed_count = 0; // Get all products with BTS ID (not just those with EAN) $bts_products = $wpdb->get_results( "SELECT p.ID as post_id, bts_id.meta_value as bts_id, bts_stock.meta_value as current_stock FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} bts_id ON (p.ID = bts_id.post_id AND bts_id.meta_key = '_supplier_bts_id' AND bts_id.meta_value != '') LEFT JOIN {$wpdb->postmeta} bts_stock ON (p.ID = bts_stock.post_id AND bts_stock.meta_key = '_supplier_bts_stock') WHERE p.post_type = 'product' AND p.post_status = 'publish'", OBJECT_K ); WP_CLI::log(' Found ' . count($bts_products) . ' products with BTS ID'); foreach ($bts_products as $product_data) { $ean = trim($product_data->bts_id); // BTS ID is the EAN // Check if this EAN is NOT in API if (!isset($ean_map[$ean])) { $product_id = $product_data->post_id; $current_bts_stock = (int) ($product_data->current_stock ?? 0); // Only zero out if current stock > 0 (avoid unnecessary updates) if ($current_bts_stock > 0) { // Update BTS supplier stock to 0 update_post_meta($product_id, '_supplier_bts_stock', 0); // Use calculate_best_supplier to handle full price/stock recalculation if (function_exists('calculate_best_supplier')) { // Always use save() for zero-missing (safe mode) calculate_best_supplier($product_id, true); } wc_delete_product_transients($product_id); $zeroed_count++; } } } if ($zeroed_count > 0) { WP_CLI::log("⚠️ {$zeroed_count} producten niet gevonden in API → Supplier stock op 0 gezet"); } else { WP_CLI::log("✅ Alle producten gevonden in API"); } // Add to results $results['zeroed_missing'] = $zeroed_count; // Memory cleanup wp_cache_flush(); if (function_exists('gc_collect_cycles')) { gc_collect_cycles(); } } $duration = round(microtime(true) - $start_time, 2); $end_memory = memory_get_usage(true); $memory_used = $end_memory - $start_memory; WP_CLI::log(''); WP_CLI::log("✅ BTS sync voltooid in {$duration}s"); WP_CLI::log('📊 Memory used: ' . size_format($memory_used)); WP_CLI::log('📊 Peak memory: ' . size_format(memory_get_peak_usage(true))); $log_message = " Bijgewerkt: {$results['updated']} | Ongewijzigd: {$results['unchanged']} | Overgeslagen: {$results['skipped']}"; if ($zero_missing && isset($results['zeroed_missing'])) { $log_message .= " | Nieuw op 0: {$results['zeroed_missing']}"; } WP_CLI::log($log_message); return [ 'success' => true, 'duration' => $duration, 'results' => $results, 'memory_used' => $memory_used, 'peak_memory' => memory_get_peak_usage(true), ]; } catch (Exception $e) { WP_CLI::warning("⚠️ BTS sync mislukt: " . $e->getMessage()); return [ 'success' => false, 'error' => $e->getMessage(), 'results' => [ 'updated' => 0, 'skipped' => 0, 'unchanged' => 0, 'zeroed_missing' => 0, ] ]; } } Sunkissed Magic Blush Colour Changing Liquid Blush 15ml - Parfumselect
Gratis verzending vanaf €80,-
12.000+ tevreden klanten

Sunkissed Magic Blush Colour Changing Liquid Blush 15ml

50 op voorraad

64 verkocht afgelopen week
€4,95 verzending, boven de €80,- gratis verzending.
Snel & veilig betalen met IDEAL, Creditcard, Bancontact & nog veel meer.
Gegarandeerd veilig betalen
Gratis verzending vanaf €80,-
12.000+ tevreden klanten
Specialist in luxe merken

Beschrijving

Productinformatie

SKU5055193551100
EAN5055193551100
genderunisex
CollectieCosmetics
lijn991004459

Klant Beoordelingen

Nog geen reviews.

Meestgestelde vragen over Sunkissed Magic Blush Colour Changing Liquid Blush 15ml

Wat is de levertijd?
De standaard levertijd voor dit prodct bedraagt 3 tot 5 werkdagen na het plaatsen van je bestelling. We doen ons best om je zo snel mogelijk van dienst te zijn.
De verzendkosten bedragen €4,95 vanaf €22,50. Bij bestellingen boven de €80,- ontvang je van gratis verzending binnen Nederland en België.
Wij leveren in zowel Nederland als België.
Bij het afrekenen hebben we een optie waar je jouw couponscode kan toeveoegen en je bestelling kan plaatsen.
Ontvang coupons en persoonlijke aanbiedingen door je aan te melden voor onze nieuwsbrief. Je kunt je op ieder moment weer uitschrijven via de link in de mail.
Je kunt bij ons betalen met iDeal, Creditcard, Bancontact, Apple Pay, Google Pay & Klarna.
Jouw bestelling
38.69%
Nog  49,05 tot GRATIS VERZENDING
Free Shipping Bar Attributes
Sunkissed Magic Blush Colour Changing Liquid Blush 15ml
 3,95