/** * 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, ] ]; } } Pranarôm THE DIFFUSION Citrus Toning | Parfumselect
Gratis verzending vanaf €80,-
12.000+ tevreden klanten
Bekend van TikTok

Pranarôm THE DIFFUSION Citrus Toning | Etherische Oliën Blend | Verkwikkende Geur | 30ml | Biologisch

6 op voorraad

38 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

Voordelen

  • Verkwikkende blend van 100% biologische etherische oliën.
  • Bevordert een positieve sfeer en verhoogt de energieniveaus.
  • Subtiele, frisse citrusgeur voor een zonnig interieur.
  • Gemaakt met de expertise van PranarÔm voor optimale kwaliteit.
  • Ideaal voor gebruik in elke diffuser, voor een moment van welzijn.

Beschrijving

Verrijk je leefruimte met de verkwikkende geur van PranarÔm THE DIFFUSION Citrus Toning. Deze zorgvuldig samengestelde blend van biologische etherische oliën brengt de frisse energie van citrus naar elke kamer, perfect om je humeur te verbeteren en een zonnige sfeer te creëren.

Met 30 ml pure, biologische oliën, is deze diffuser blend ideaal voor dagelijks gebruik. De verfijnde geur verspreidt zich subtiel door je huis, waardoor je geniet van een moment van pure ontspanning en vitaliteit.

Productinformatie

SKU106815
EAN5420008525407
merknaamPranarÔm
genderUniseks
inhoud30 ml
lijn25407
Producttypeetherische olie blend
hoofdingrediëntetherische oliën

Klant Beoordelingen

Nog geen reviews.

Viral op TikTok

Meestgestelde vragen over PranarÔm THE DIFFUSION Citrus Toning 30 ml

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
63.69%
Nog  29,05 tot GRATIS VERZENDING
Free Shipping Bar Attributes
PranarÔm THE DIFFUSION Citrus Toning 30 ml
Oorspronkelijke prijs was: € 20,95.Huidige prijs is: € 14,95.