[ 'min' => 1, 'max' => 2, ], 'suppliers' => [], ]; $options = get_option(self::OPTION_KEY, []); if (!is_array($options)) { $options = []; } $options = wp_parse_args($options, $defaults); $options['default_range'] = self::sanitize_range($options['default_range']); if (!is_array($options['suppliers'])) { $options['suppliers'] = []; } else { foreach ($options['suppliers'] as $key => $range) { $options['suppliers'][$key] = self::sanitize_range($range); } } $options['suppliers'] = self::ensure_known_suppliers($options['suppliers'], $options['default_range']); return $options; } public static function register_settings_page(): void { add_submenu_page( 'woocommerce', __('Levertijden', SITI_DELIVERY_TIME_NOTICES_SLUG), __('Levertijden', SITI_DELIVERY_TIME_NOTICES_SLUG), 'manage_woocommerce', 'lb-levertijden', [__CLASS__, 'render_settings_page'] ); } public static function maybe_save_settings(): void { if (!isset($_POST['lb_levertijden_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['lb_levertijden_nonce'])), 'lb_save_levertijden')) { return; } if (!current_user_can('manage_woocommerce')) { return; } $default_min = isset($_POST['lb_default_min']) ? absint($_POST['lb_default_min']) : 1; $default_max = isset($_POST['lb_default_max']) ? absint($_POST['lb_default_max']) : $default_min; $default_range = self::sanitize_range([ 'min' => $default_min, 'max' => $default_max, ]); $suppliers = []; if (!empty($_POST['lb_suppliers']) && is_array($_POST['lb_suppliers'])) { $data = wp_unslash($_POST['lb_suppliers']); $keys = $data['key'] ?? []; $labels = $data['label'] ?? []; $mins = $data['min'] ?? []; $maxes = $data['max'] ?? []; $count = max(count($keys), count($labels), count($mins), count($maxes)); for ($i = 0; $i < $count; $i++) { $key = sanitize_key($keys[$i] ?? ''); $label = sanitize_text_field($labels[$i] ?? ''); $min = absint($mins[$i] ?? 0); $max = absint($maxes[$i] ?? 0); if ($key === '' || $label === '') { continue; } $range = self::sanitize_range([ 'min' => $min, 'max' => $max, 'label' => $label, ]); $range['label'] = $label; $suppliers[$key] = $range; } } update_option( self::OPTION_KEY, [ 'default_range' => $default_range, 'suppliers' => $suppliers, ] ); add_settings_error('lb_levertijden', 'lb_levertijden_saved', __('Levertijden opgeslagen.', SITI_DELIVERY_TIME_NOTICES_SLUG), 'updated'); } public static function render_settings_page(): void { $settings = self::get_settings(); $suppliers = $settings['suppliers']; $select_options = self::get_supplier_dropdown_options(array_keys($suppliers)); settings_errors('lb_levertijden'); ?>

$range) : ?>

$label, 'min' => $range['min'], 'max' => $range['max']], $select_options); ?> '', 'min' => $default_range['min'] ?? 1, 'max' => $default_range['max'] ?? 1, ], $select_options); return $content; } private static function sanitize_range(array $range): array { $min = isset($range['min']) ? absint($range['min']) : 0; $max = isset($range['max']) ? absint($range['max']) : 0; if ($min === 0 && $max === 0) { $min = 1; $max = 1; } if ($max === 0) { $max = $min; } if ($min === 0) { $min = $max; } if ($max < $min) { $temp = $min; $min = $max; $max = $temp; } return [ 'min' => max(1, $min), 'max' => max(1, $max), 'label' => $range['label'] ?? '', ]; } private static function ensure_known_suppliers(array $current, array $default_range): array { $known = self::get_distinct_supplier_sources(); foreach ($known as $key => $label) { if (!isset($current[$key])) { $current[$key] = [ 'min' => $default_range['min'], 'max' => $default_range['max'], 'label' => $label ?: $key, ]; continue; } if (empty($current[$key]['label']) && $label) { $current[$key]['label'] = $label; } } ksort($current); return $current; } private static function get_supplier_dropdown_options(array $current_keys = []): array { $options = [ '' => __('Selecteer leverancier', SITI_DELIVERY_TIME_NOTICES_SLUG), ]; foreach (self::get_distinct_supplier_sources() as $key => $label) { $options[$key] = $label ?: $key; } foreach ($current_keys as $key) { if ($key !== '' && !isset($options[$key])) { $options[$key] = $key; } } return $options; } private static function render_supplier_select(string $selected, array $options): string { if ($selected !== '' && !array_key_exists($selected, $options)) { $options[$selected] = $selected; } $html = ''; return $html; } private static function get_distinct_supplier_sources(): array { static $cache = null; if ($cache !== null) { return $cache; } global $wpdb; $table = $wpdb->postmeta; $rows = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT meta_value FROM {$table} WHERE meta_key = %s AND meta_value <> ''", '_wpci_supplier' ) ); $cache = []; if ($rows) { foreach ($rows as $raw) { foreach (self::normalize_supplier_values($raw) as $value) { $key = sanitize_key($value); if ($key === '' || isset($cache[$key])) { continue; } $cache[$key] = $value; } } } return $cache; } private static function normalize_supplier_values($raw): array { $result = []; $value = maybe_unserialize($raw); if (is_array($value)) { array_walk_recursive($value, function ($item) use (&$result) { if (is_scalar($item)) { $item = trim((string) $item); if ($item !== '') { $result[] = $item; } } }); } elseif (is_scalar($value)) { $value = trim((string) $value); if ($value !== '') { $result[] = $value; } } return $result; } public static function render_product_override_field(): void { $settings = self::get_settings(); $choices = [ '' => __('Automatisch (voorraad / leverancier)', SITI_DELIVERY_TIME_NOTICES_SLUG), 'default' => __('Altijd standaard levertijd', SITI_DELIVERY_TIME_NOTICES_SLUG), ]; foreach ($settings['suppliers'] as $key => $range) { $choices[$key] = sprintf( '%s (%s)', $range['label'] ?: $key, self::format_range_text($range) ); } global $post; $value = get_post_meta($post->ID, self::META_SUPPLIER_OVERRIDE, true); echo '
'; woocommerce_wp_select([ 'id' => self::META_SUPPLIER_OVERRIDE, 'label' => __('Levertijd leverancier', SITI_DELIVERY_TIME_NOTICES_SLUG), 'options' => $choices, 'value' => $value, 'desc_tip' => false, 'description' => __('Kies een leverancier om de levertijd van dit product te forceren.', SITI_DELIVERY_TIME_NOTICES_SLUG), ]); echo '
'; } public static function save_product_override_field(int $post_id): void { if (!isset($_POST[self::META_SUPPLIER_OVERRIDE])) { return; } $value = sanitize_text_field(wp_unslash($_POST[self::META_SUPPLIER_OVERRIDE])); update_post_meta($post_id, self::META_SUPPLIER_OVERRIDE, $value); } public static function filter_availability_text(string $availability, WC_Product $product): string { $text = self::get_levertijd_text($product, false); if ($text) { return sprintf(__('Verwachte levertijd: %s', SITI_DELIVERY_TIME_NOTICES_SLUG), $text); } return $availability; } public static function render_card_below_cart(): void { if (!is_product()) { return; } global $product; if (!$product instanceof WC_Product) { return; } if ($product->is_type('variable')) { $text = ''; } else { $text = self::get_levertijd_text($product); if (!$text) { return; } } echo '
'; echo '
' . esc_html($text) . '
'; echo '
'; } public static function add_variation_levertijd_data($variation_data, $product = null, $variation = null) { if ($variation instanceof WC_Product_Variation) { $text = self::get_levertijd_text($variation); $variation_data['lb_levertijd'] = $text; } return $variation_data; } private static function get_product_meta_with_parent(WC_Product $product, string $key, bool $allow_array = false) { $value = get_post_meta($product->get_id(), $key, true); if (($value === '' || ($allow_array && empty($value))) && $product->get_parent_id()) { $value = get_post_meta($product->get_parent_id(), $key, true); } if (!$allow_array && is_array($value)) { return ''; } return $value; } private static function get_levertijd_text(WC_Product $product, bool $with_label = true): string { $range = self::resolve_range_for_product($product); if (!$range) { return ''; } $text = self::format_range_text($range); if ($text === '') { return ''; } return $with_label ? sprintf(__('Verwachte levertijd: %s', SITI_DELIVERY_TIME_NOTICES_SLUG), $text) : $text; } private static function resolve_range_for_product(WC_Product $product): ?array { $settings = self::get_settings(); $override = self::get_product_meta_with_parent($product, self::META_SUPPLIER_OVERRIDE); if ($override && $override !== 'auto') { if ($override === 'default') { return $settings['default_range']; } if (isset($settings['suppliers'][$override])) { return $settings['suppliers'][$override]; } } if (self::has_local_stock($product)) { return $settings['default_range']; } $supplier = self::get_supplier_key($product); if ($supplier && isset($settings['suppliers'][$supplier])) { return $settings['suppliers'][$supplier]; } return $settings['default_range']; } private static function has_local_stock(WC_Product $product): bool { $explicit_stock = self::get_product_meta_with_parent($product, '_stock'); if ($explicit_stock !== '') { $qty = function_exists('wc_stock_amount') ? wc_stock_amount($explicit_stock) : (int) $explicit_stock; return $qty > 0; } if ($product->managing_stock()) { $qty = $product->get_stock_quantity(); if ($qty !== null) { return $qty > 0; } } return $product->is_in_stock(); } private static function get_supplier_key(WC_Product $product): string { $supplier_meta = self::get_product_meta_with_parent($product, '_wpci_supplier', true); if ($supplier_meta === '' || $supplier_meta === null) { return ''; } $supplier = $supplier_meta; if (is_string($supplier) && is_serialized($supplier)) { $maybe = maybe_unserialize($supplier); if (is_array($maybe)) { $supplier = $maybe; } } if (is_array($supplier)) { $supplier = reset($supplier); } return sanitize_key((string) $supplier); } private static function format_range_text(array $range): string { $min = absint($range['min'] ?? 0); $max = absint($range['max'] ?? 0); if ($min === 0 && $max === 0) { return ''; } if ($max === 0) { $max = $min; } if ($min === $max) { $text = sprintf(_n('%d werkdag', '%d werkdagen', $min, SITI_DELIVERY_TIME_NOTICES_SLUG), $min); } else { $text = sprintf(__('van %d tot %d werkdagen', SITI_DELIVERY_TIME_NOTICES_SLUG), $min, $max); } return $text; } }