admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wc_tpp_variation_pricing'), 'currency_symbol' => esc_js(get_woocommerce_currency_symbol()), 'currency_position' => esc_js(get_option('woocommerce_currency_pos', 'left')), 'price_decimals' => absint(wc_get_price_decimals()), 'price_decimal_separator' => esc_js(wc_get_price_decimal_separator()), 'price_thousand_separator' => esc_js(wc_get_price_thousand_separator()) )); } } public function display_pricing_table_before() { if (get_option('wc_tpp_display_position') === 'before_add_to_cart') { $this->display_pricing_table(); } } public function display_pricing_table_after() { if (get_option('wc_tpp_display_position') === 'after_add_to_cart') { $this->display_pricing_table(); } } public function display_pricing_table_after_price() { if (get_option('wc_tpp_display_position') === 'after_price') { $this->display_pricing_table(); } } public function maybe_hide_quantity_input() { global $product; if (!$product || !is_a($product, 'WC_Product')) { return; } // For variable products, quantity hiding is handled per-variation via JS if ($product->is_type('variable')) { return; } $product_id = $product->get_id(); $global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; $product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes'; $packages = get_post_meta($product_id, '_wc_tpp_packages', true); // Hide quantity input if restriction is enabled and packages exist if (($global_restrict || $product_restrict) && !empty($packages)) { echo ''; } } public function display_pricing_table() { global $product; if (!$product || !is_a($product, 'WC_Product') || get_option('wc_tpp_display_table') !== 'yes') { return; } // For variable products, show a placeholder that will be populated by JS when variation is selected if ($product->is_type('variable')) { echo ''; return; } // For simple products, display pricing table directly $product_id = $product->get_id(); $tiers = get_post_meta($product_id, '_wc_tpp_tiers', true); $packages = get_post_meta($product_id, '_wc_tpp_packages', true); $global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; $product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes'; if (empty($tiers) && empty($packages)) { return; } WC_TPP_Template_Loader::get_instance()->display('frontend/pricing-table.twig', array( 'product' => $product, 'tiers' => $tiers, 'packages' => $packages, 'restrict_to_packages' => $global_restrict || $product_restrict )); } /** * Get tier price for a product or variation * * @param int $product_id Product ID (parent for simple, parent for variable) * @param int $quantity Quantity * @param int $variation_id Variation ID (0 for simple products) * @return float|null Tier price or null if not applicable */ public static function get_tier_price($product_id, $quantity, $variation_id = 0) { $effective_id = $variation_id > 0 ? $variation_id : $product_id; $tiers = get_post_meta($effective_id, '_wc_tpp_tiers', true); if (empty($tiers) || !is_array($tiers)) { return null; } $applicable_price = null; foreach ($tiers as $tier) { if ($quantity >= $tier['min_qty']) { $applicable_price = $tier['price']; } } return $applicable_price; } /** * Get package price for a product or variation * * @param int $product_id Product ID (parent for simple, parent for variable) * @param int $quantity Quantity * @param int $variation_id Variation ID (0 for simple products) * @return float|null Package price or null if not applicable */ public static function get_package_price($product_id, $quantity, $variation_id = 0) { $effective_id = $variation_id > 0 ? $variation_id : $product_id; $packages = get_post_meta($effective_id, '_wc_tpp_packages', true); if (empty($packages) || !is_array($packages)) { return null; } foreach ($packages as $package) { if ($quantity == $package['qty']) { return $package['price']; } } return null; } /** * Check if a product has quantity restrictions enabled * * @param int $product_id Product ID * @return bool */ public static function has_quantity_restriction($product_id) { $global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; $product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes'; $packages = get_post_meta($product_id, '_wc_tpp_packages', true); return ($global_restrict || $product_restrict) && !empty($packages); } /** * Modify catalog add to cart button for products with quantity restrictions * * @param string $html Add to cart button HTML * @param WC_Product $product Product object * @return string Modified HTML */ public function modify_catalog_add_to_cart_button($html, $product) { if (!$product || !is_a($product, 'WC_Product')) { return $html; } $product_id = $product->get_id(); // For variable products, check if ANY variation has restrictions // For simple products, check the product itself $has_restriction = false; if ($product->is_type('variable')) { // Check if any variation has package restrictions $variations = $product->get_available_variations(); foreach ($variations as $variation_data) { if (self::has_quantity_restriction($variation_data['variation_id'])) { $has_restriction = true; break; } } } else { $has_restriction = self::has_quantity_restriction($product_id); } if (!$has_restriction) { return $html; } // Replace add to cart button with "View Options" link $product_url = esc_url($product->get_permalink()); $button_text = esc_html__('View Options', 'wc-tier-package-prices'); // Use correct product type class $product_type_class = $product->is_type('variable') ? 'product_type_variable' : 'product_type_simple'; $new_html = sprintf( '%s', $product_url, esc_attr($product_type_class), esc_attr(sprintf(__('View options for %s', 'wc-tier-package-prices'), $product->get_name())), $button_text ); return $new_html; } /** * AJAX handler to get variation pricing data */ public function ajax_get_variation_pricing() { // Verify nonce check_ajax_referer('wc_tpp_variation_pricing', 'nonce'); $variation_id = isset($_POST['variation_id']) ? absint($_POST['variation_id']) : 0; if (!$variation_id) { wp_send_json_error(array('message' => __('Invalid variation ID', 'wc-tier-package-prices'))); } // Get variation data $variation = wc_get_product($variation_id); if (!$variation || !$variation->is_type('variation')) { wp_send_json_error(array('message' => __('Variation not found', 'wc-tier-package-prices'))); } // Get tier and package pricing $tiers = get_post_meta($variation_id, '_wc_tpp_tiers', true); $packages = get_post_meta($variation_id, '_wc_tpp_packages', true); $global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; $product_restrict = get_post_meta($variation_id, '_wc_tpp_restrict_to_packages', true) === 'yes'; if (empty($tiers) && empty($packages)) { // No pricing data for this variation wp_send_json_success(array( 'has_pricing' => false, 'html' => '' )); } // Render the pricing table HTML ob_start(); WC_TPP_Template_Loader::get_instance()->display('frontend/pricing-table.twig', array( 'product' => $variation, 'tiers' => $tiers, 'packages' => $packages, 'restrict_to_packages' => $global_restrict || $product_restrict )); $html = ob_get_clean(); wp_send_json_success(array( 'has_pricing' => true, 'html' => $html, 'tiers' => $tiers ? $tiers : array(), 'packages' => $packages ? $packages : array(), 'restrict_to_packages' => $global_restrict || $product_restrict )); } } new WC_TPP_Frontend(); }