(int) $o['id'], 'label' => (string) $o['label'], 'price' => (float) $o['price_delta'], 'premium' => ! empty( $o['exclude_from_allowance'] ), 'default' => ! empty( $o['default_selected'] ), 'available' => ! empty( $o['available'] ), ); } $groups[] = array( 'id' => (int) $g['id'], 'name' => (string) $g['name'], 'select_type' => (string) $g['select_type'], 'min' => (int) $g['min_select'], 'max' => (int) $g['max_select'], 'included_free' => (int) $g['included_free'], 'free_policy' => (string) $g['free_policy'], 'description' => (string) $g['description'], 'assignment_id' => (int) $g['assignment_id'], 'options' => $options, ); } $product = wc_get_product( (int) $product_id ); $base_price = $product ? (float) wc_get_price_to_display( $product, array( 'price' => $product->get_price() ) ) : 0.0; return array( 'product_id' => (int) $product_id, 'base_price' => $base_price, 'currency' => function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '', 'groups' => $groups, 'rules' => isset( $cfg['rules'] ) ? $cfg['rules'] : array(), ); } public function enqueue_assets() { if ( ! function_exists( 'is_product' ) || ! is_product() ) { return; } global $product; $pid = $product instanceof WC_Product ? $product->get_id() : ( function_exists( 'get_the_ID' ) ? (int) get_the_ID() : 0 ); if ( ! self::config_for_product( $pid ) ) { return; } $base = defined( 'AIEO_DIR' ) ? AIEO_DIR : ''; $jrel = 'assets/js/aieo-option-groups-frontend.js'; $crel = 'assets/css/aieo-option-groups.css'; $jver = ( $base && file_exists( $base . $jrel ) ) ? (string) filemtime( $base . $jrel ) : '1.0'; $cver = ( $base && file_exists( $base . $crel ) ) ? (string) filemtime( $base . $crel ) : '1.0'; wp_enqueue_style( 'aieo-option-groups', plugins_url( $crel, AIEO_FILE ), array(), $cver ); wp_enqueue_script( 'aieo-option-groups-frontend', plugins_url( $jrel, AIEO_FILE ), array( 'jquery' ), $jver, true ); wp_localize_script( 'aieo-option-groups-frontend', 'aieoOG', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'i18n' => array( 'chooseMin' => __( 'Choose at least %1$d for “%2$s”.', 'ai-eshop-optimizer' ), 'chooseMax' => __( 'Choose at most %1$d for “%2$s”.', 'ai-eshop-optimizer' ), 'soldOut' => __( 'Sold out', 'ai-eshop-optimizer' ), 'free' => __( 'free', 'ai-eshop-optimizer' ), ), ) ); } public function render_picker() { global $product; $pid = $product instanceof WC_Product ? $product->get_id() : 0; $cfg = $pid ? self::config_for_product( $pid ) : null; if ( ! $cfg ) { return; } $cur = $cfg['currency']; echo '
'; foreach ( $cfg['groups'] as $g ) { $type = 'radio' === $g['select_type'] ? 'radio' : 'checkbox'; $req = $g['min'] > 0; $hint = self::group_hint( $g ); echo '
'; echo '' . esc_html( $g['name'] ); if ( $req ) { echo ' *'; } if ( $hint !== '' ) { echo ' ' . esc_html( $hint ) . ''; } echo ''; if ( $g['description'] !== '' ) { echo '

' . esc_html( $g['description'] ) . '

'; } foreach ( $g['options'] as $o ) { $oid = (int) $o['id']; $avail = ! empty( $o['available'] ); $priceTx = self::price_label( (float) $o['price'], $cur ); $id_attr = 'aieo-og-o-' . $g['id'] . '-' . $oid; echo ''; } echo '
'; } echo '
' . esc_html__( 'Total', 'ai-eshop-optimizer' ) . ' ' . '' . '
'; echo '
'; } private static function group_hint( $g ) { $bits = array(); if ( $g['min'] > 0 && $g['min'] === $g['max'] ) { $bits[] = sprintf( _n( 'choose %d option', 'choose %d options', $g['min'], 'ai-eshop-optimizer' ), $g['min'] ); } else { if ( $g['min'] > 0 ) { $bits[] = sprintf( __( 'min %d', 'ai-eshop-optimizer' ), $g['min'] ); } if ( $g['max'] > 0 ) { $bits[] = sprintf( __( 'up to %d', 'ai-eshop-optimizer' ), $g['max'] ); } } if ( $g['included_free'] > 0 ) { $bits[] = sprintf( __( '%d free', 'ai-eshop-optimizer' ), $g['included_free'] ); } return $bits ? '(' . implode( ', ', $bits ) . ')' : ''; } private static function price_label( $delta, $cur ) { if ( abs( $delta ) < 0.00001 ) { return ''; } $sign = $delta > 0 ? '+' : '−'; return $sign . $cur . number_format_i18n( abs( $delta ), 2 ); } private static function posted_selection() { if ( empty( $_POST[ self::FORM_FIELD ] ) ) { return array(); } $raw = (array) wp_unslash( $_POST[ self::FORM_FIELD ] ); return array_values( array_unique( array_filter( array_map( 'intval', $raw ) ) ) ); } public function validate_add( $passed, $product_id, $qty ) { $cfg = self::config_for_product( $product_id ); if ( ! $cfg ) { return $passed; } $res = AIEO_Option_Groups_Pricing::resolve( AIEO_Option_Groups_Store::get_product_config( $product_id ), self::posted_selection() ); if ( ! $res['valid'] ) { foreach ( $res['errors'] as $err ) { wc_add_notice( $err, 'error' ); } return false; } return $passed; } public function add_cart_item_data( $data, $product_id, $variation_id ) { $cfg = self::config_for_product( $product_id ); if ( ! $cfg ) { return $data; } $selected = self::posted_selection(); $store = AIEO_Option_Groups_Store::get_product_config( $product_id ); $res = AIEO_Option_Groups_Pricing::resolve( $store, $selected ); if ( ! $res['valid'] ) { return $data; } $data['aieo_og'] = array( 'options' => $selected, 'total_delta' => (float) $res['total_delta'], 'lines' => $res['lines'], ); $data['aieo_og_sig'] = md5( wp_json_encode( $selected ) ); return $data; } public function get_cart_item_from_session( $item, $values ) { if ( isset( $values['aieo_og'] ) ) { $item['aieo_og'] = $values['aieo_og']; } return $item; } public function before_calculate_totals( $cart ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { return; } if ( ! $cart || ! method_exists( $cart, 'get_cart' ) ) { return; } foreach ( $cart->get_cart() as $item ) { if ( empty( $item['aieo_og'] ) || empty( $item['data'] ) ) { continue; } $product = $item['data']; $base = (float) $product->get_price( 'edit' ); $product->set_price( $base + (float) $item['aieo_og']['total_delta'] ); } } public function get_item_data( $item_data, $cart_item ) { if ( empty( $cart_item['aieo_og']['lines'] ) ) { return $item_data; } $cur = function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : ''; foreach ( $cart_item['aieo_og']['lines'] as $line ) { $val = $line['label']; if ( ! empty( $line['free'] ) ) { $val .= ' (' . __( 'free', 'ai-eshop-optimizer' ) . ')'; } elseif ( abs( (float) $line['charged_delta'] ) > 0.00001 ) { $val .= ' (+' . $cur . number_format_i18n( (float) $line['charged_delta'], 2 ) . ')'; } $item_data[] = array( 'key' => __( 'Option', 'ai-eshop-optimizer' ), 'value' => $val, 'display' => $val, ); } return $item_data; } public function add_order_line_meta( $item, $cart_item_key, $values, $order ) { if ( empty( $values['aieo_og']['lines'] ) ) { return; } $cur = function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : ''; foreach ( $values['aieo_og']['lines'] as $line ) { $val = $line['label']; if ( empty( $line['free'] ) && abs( (float) $line['charged_delta'] ) > 0.00001 ) { $val .= ' (+' . $cur . number_format_i18n( (float) $line['charged_delta'], 2 ) . ')'; } elseif ( ! empty( $line['free'] ) ) { $val .= ' (' . __( 'free', 'ai-eshop-optimizer' ) . ')'; } $item->add_meta_data( $line['label'] , $val, false ); } $item->add_meta_data( '_aieo_og_options', $values['aieo_og']['options'], true ); } public function ajax_resolve() { $pid = isset( $_POST['product_id'] ) ? (int) $_POST['product_id'] : 0; $sel = isset( $_POST['options'] ) ? array_map( 'intval', (array) wp_unslash( $_POST['options'] ) ) : array(); $cfg = $pid ? self::config_for_product( $pid ) : null; if ( ! $cfg ) { wp_send_json_error(); } $res = AIEO_Option_Groups_Pricing::resolve( AIEO_Option_Groups_Store::get_product_config( $pid ), $sel ); wp_send_json_success( array( 'base_price' => $cfg['base_price'], 'total_delta' => $res['total_delta'], 'total' => $cfg['base_price'] + $res['total_delta'], 'valid' => $res['valid'], 'errors' => $res['errors'], 'lines' => $res['lines'], ) ); } }