$from, 'percent' => $pct);
}
ksort($out);
return array_values($out);
}
public static function resolve_tier(array $tiers, $qty) {
$qty = (int) $qty;
if ($qty < 1 || empty($tiers)) {
return null;
}
$match = null;
foreach ($tiers as $tier) {
if ($tier['from_qty'] <= $qty) {
$match = $tier;
} else {
break;
}
}
return $match;
}
public static function compute_discounted_price($base_price, array $tiers, $qty) {
$base = (float) $base_price;
if ($base <= 0.0 || empty($tiers)) {
return $base;
}
$tier = self::resolve_tier($tiers, $qty);
if (!$tier || $tier['percent'] <= 0.0) {
return $base;
}
$discounted = $base * (1.0 - ($tier['percent'] / 100.0));
return round($discounted, wc_get_price_decimals());
}
// ============================================================
// Admin UI: variation editor
// ============================================================
public function render_variation_tier_editor($loop, $variation_data, $variation) {
$parent_id = (int) $variation->post_parent;
if (!Studiou_WC_FPP_Product::is_fpp_product($parent_id)) {
return;
}
$tiers = self::get_tiers($variation->ID);
?>
$from, 'percent' => $pct);
}
$sanitized = self::normalize_tiers($sanitized);
if (empty($sanitized)) {
delete_post_meta($variation_id, self::META_KEY);
} else {
update_post_meta($variation_id, self::META_KEY, $sanitized);
}
}
// ============================================================
// Frontend: expose tiers on variation data + placeholder container
// ============================================================
public function inject_variation_tier_data($variation_data, $product, $variation) {
$parent_id = $variation->get_parent_id();
if (!Studiou_WC_FPP_Product::is_fpp_product($parent_id)) {
return $variation_data;
}
$tiers = self::get_tiers($variation->get_id());
$base_price = get_post_meta($variation->get_id(), '_price', true);
$variation_data['studiou_fpp_qty_tiers'] = $tiers;
$variation_data['studiou_fpp_base_price'] = ($base_price !== '' && $base_price !== false) ? (float) $base_price : 0.0;
return $variation_data;
}
public function render_tier_display_placeholder() {
global $product;
if (!$product || !Studiou_WC_FPP_Product::is_fpp_product($product->get_id())) {
return;
}
echo '';
}
/**
* Render a persistent notice above the variations form summarising tier availability.
* Only shown if at least one variation of the current product has tiers configured.
*/
public function render_preform_notice() {
global $product;
if (!$product || !Studiou_WC_FPP_Product::is_fpp_product($product->get_id())) {
return;
}
if (!$product->is_type('variable')) {
return;
}
$has_tiers = false;
$max_discount = 0.0;
foreach ($product->get_children() as $variation_id) {
$tiers = self::get_tiers($variation_id);
if (!empty($tiers)) {
$has_tiers = true;
foreach ($tiers as $tier) {
if ($tier['percent'] > $max_discount) {
$max_discount = $tier['percent'];
}
}
}
}
if (!$has_tiers) {
return;
}
$label = ($max_discount > 0)
? sprintf(
/* translators: %s: maximum discount percentage */
__('Quantity discount available — save up to %s%%. Select a variant to see the full tier table.', 'studiou-wc-free-photo-product'),
rtrim(rtrim(number_format($max_discount, 2, '.', ''), '0'), '.')
)
: __('Quantity discount available — select a variant to see the full tier table.', 'studiou-wc-free-photo-product');
?>
get_id())) {
return;
}
if (!$product->is_type('variable')) {
return;
}
$data = array();
foreach ($product->get_children() as $variation_id) {
$tiers = self::get_tiers($variation_id);
if (empty($tiers)) {
continue;
}
$base_price = get_post_meta($variation_id, '_price', true);
$data[(string) $variation_id] = array(
'tiers' => $tiers,
'base_price' => ($base_price !== '' && $base_price !== false) ? (float) $base_price : 0.0,
);
}
if (empty($data)) {
return;
}
echo '';
}
// ============================================================
// Cart runtime
// ============================================================
public function apply_cart_discount($cart) {
if (is_admin() && !defined('DOING_AJAX')) {
return;
}
if (!$cart || !is_a($cart, 'WC_Cart')) {
return;
}
foreach ($cart->get_cart() as $cart_item) {
if (empty($cart_item['variation_id'])) {
continue;
}
$product = isset($cart_item['data']) ? $cart_item['data'] : null;
if (!$product) {
continue;
}
$parent_id = $product->get_parent_id();
if (!Studiou_WC_FPP_Product::is_fpp_product($parent_id)) {
continue;
}
$variation_id = (int) $cart_item['variation_id'];
$tiers = self::get_tiers($variation_id);
if (empty($tiers)) {
continue;
}
// Read base price from meta to avoid compounding if the hook fires twice per request
$base_price = get_post_meta($variation_id, '_price', true);
if ($base_price === '' || $base_price === false) {
continue;
}
$qty = (int) $cart_item['quantity'];
$discounted = self::compute_discounted_price((float) $base_price, $tiers, $qty);
if ($discounted > 0.0) {
$product->set_price($discounted);
}
}
}
}