post_type) || $post->post_type !== 'product') { return; } if ($new_status === $old_status) { return; } if ($old_status === 'publish' && $new_status !== 'publish') { global $wpdb; if (self::table_exists()) { $wpdb->delete(self::table_name(), array('product_id' => (int) $post->ID), array('%d')); } } elseif ($new_status === 'publish' && $old_status !== 'publish' && class_exists('AIEO_Recompute_Queue')) { AIEO_Recompute_Queue::enqueue(AIEO_Recompute_Queue::SURFACE_PRICES, (int) $post->ID, ''); } } public static function on_before_delete($post_id) { $post_id = (int) $post_id; if ($post_id <= 0 || !self::table_exists()) { return; } $post = get_post($post_id); if (!$post || $post->post_type !== 'product') { return; } global $wpdb; $wpdb->delete(self::table_name(), array('product_id' => $post_id), array('%d')); } public static function is_enabled() { return (bool) get_option('aieo_use_effective_prices', 0); } public static function get_for($product_id, $audience_key) { global $wpdb; $product_id = (int) $product_id; if ($product_id <= 0 || !is_string($audience_key) || $audience_key === '') { return null; } $l1 = &self::$l1; $key = $audience_key . ':' . $product_id; if (array_key_exists($key, $l1)) { return $l1[$key]; } $table = self::table_name(); if (!self::table_exists()) { return $l1[$key] = null; } $row = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$table} WHERE product_id = %d AND audience_key = %s LIMIT 1", $product_id, $audience_key ), ARRAY_A); return $l1[$key] = (is_array($row) ? self::hydrate($row) : null); } private static $l1 = array(); public static function prime(array $product_ids, $audience_key) { if (!is_string($audience_key) || $audience_key === '') { return; } $missing = array(); foreach (array_unique(array_filter(array_map('intval', $product_ids))) as $id) { if (!array_key_exists($audience_key . ':' . $id, self::$l1)) { $missing[] = $id; } } if (!$missing) { return; } $rows = self::get_for_batch($missing, $audience_key); foreach ($missing as $id) { self::$l1[$audience_key . ':' . $id] = $rows[$id] ?? null; } } public static function get_for_batch(array $product_ids, $audience_key) { global $wpdb; $ids = array_unique(array_filter(array_map('intval', $product_ids))); if (empty($ids) || !is_string($audience_key) || $audience_key === '') { return array(); } $table = self::table_name(); if (!self::table_exists()) { return array(); } $placeholders = implode(',', array_fill(0, count($ids), '%d')); $args = array_merge(array_values($ids), array($audience_key)); $rows = $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$table} WHERE product_id IN ({$placeholders}) AND audience_key = %s", $args ), ARRAY_A); $out = array(); if (is_array($rows)) { foreach ($rows as $r) { $hydrated = self::hydrate($r); $out[(int) $hydrated['product_id']] = $hydrated; } } return $out; } public static function recompute($product_id, $audience_key) { $product_id = (int) $product_id; if ($product_id <= 0 || !is_string($audience_key) || $audience_key === '') { return null; } if (!class_exists('AIEO_DMM_Pricing_Engine') || !function_exists('wc_get_product')) { return null; } $post = get_post($product_id); if (!$post || $post->post_type !== 'product' || $post->post_status !== 'publish') { global $wpdb; $wpdb->delete(self::table_name(), array('product_id' => $product_id), array('%d')); return null; } $product = wc_get_product($product_id); if (!$product) { return null; } $base = (float) $product->get_regular_price(); if ($base <= 0) { $base = (float) $product->get_price(); } if ($base <= 0) { return null; } $rep_user = self::representative_user_for_audience($audience_key); $original = get_current_user_id(); if ($rep_user > 0 && $rep_user !== $original) { wp_set_current_user($rep_user); } try { $engine = AIEO_DMM_Pricing_Engine::instance(); $sale_price = (float) $engine->get_effective_price($product_id, $rep_user, $base); } finally { if ($rep_user > 0 && $rep_user !== $original) { wp_set_current_user($original); } } $sale_undercut = $sale_price > 0 && $sale_price < $base; $native_on_sale = method_exists($product, 'is_on_sale') ? (bool) $product->is_on_sale() : false; if (!$native_on_sale && method_exists($product, 'is_type') && $product->is_type('variable')) { foreach ((array) $product->get_children() as $vid) { $v = function_exists('wc_get_product') ? wc_get_product((int) $vid) : null; if ($v && method_exists($v, 'is_on_sale') && $v->is_on_sale()) { $native_on_sale = true; break; } } } $was_on_sale = $sale_undercut || $native_on_sale; $sale_discount_pct = null; $effective_sale = $sale_undercut ? $sale_price : null; if ($sale_undercut) { $sale_discount_pct = round((1.0 - ($sale_price / $base)) * 100.0, 2); } elseif ($native_on_sale) { if ($product->is_type('variable') && method_exists($product, 'get_variation_regular_price')) { $reg_min = (float) $product->get_variation_regular_price('min'); $sale_min = (float) $product->get_variation_sale_price('min'); } else { $reg_min = (float) $product->get_regular_price(); $sale_min = (float) $product->get_sale_price(); } if ($reg_min > 0 && $sale_min > 0 && $sale_min < $reg_min) { $sale_discount_pct = round((1.0 - ($sale_min / $reg_min)) * 100.0, 2); $effective_sale = $sale_min; } } if (class_exists('AIEO_DMM_Role_Pricing')) { $role_price_applied = AIEO_DMM_Role_Pricing::has_role_pricing($product_id); } else { global $wpdb; $rp_tbl = $wpdb->prefix . 'aieo_dmm_role_prices'; $role_price_applied = (bool) $wpdb->get_var($wpdb->prepare( "SELECT 1 FROM {$rp_tbl} WHERE product_id = %d OR parent_id = %d LIMIT 1", $product_id, $product_id )); } $monthly_offer_active = class_exists('AIEO_DMM_Brand_Discounts') ? (bool) AIEO_DMM_Brand_Discounts::is_monthly_offer_active($product_id) : false; $source_rule_type = null; if ($role_price_applied) { $source_rule_type = 'role'; } elseif ($monthly_offer_active) { $source_rule_type = 'monthly_offer'; } elseif ($native_on_sale) { $source_rule_type = 'native_sale'; } $row = array( 'product_id' => $product_id, 'audience_key' => $audience_key, 'base_price' => $base, 'sale_price' => $effective_sale, 'was_on_sale' => $was_on_sale ? 1 : 0, 'role_price_applied' => $role_price_applied ? 1 : 0, 'monthly_offer_active' => $monthly_offer_active ? 1 : 0, 'sale_discount_pct' => $sale_discount_pct, 'source_rule_id' => null, 'source_rule_type' => $source_rule_type, 'valid_from' => null, 'valid_until' => null, 'computed_at' => current_time('mysql', true), ); global $wpdb; $table = self::table_name(); $wpdb->replace($table, $row, array( '%d', '%s', '%f', '%s', '%d', '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s', )); return self::hydrate($row); } public static function recompute_all_audiences($product_id) { $count = 0; $audiences = class_exists('AIEO_Identity_Bridge') ? AIEO_Identity_Bridge::known_audience_keys() : array('guest'); foreach ($audiences as $key) { if (self::recompute($product_id, $key)) { $count++; } } return $count; } public static function maybe_filter_price_html($price_html, $product) { if (!is_object($product) || !method_exists($product, 'get_id')) { return $price_html; } $row = self::get_for( (int) $product->get_id(), class_exists('AIEO_Identity_Bridge') ? AIEO_Identity_Bridge::audience_key_for_current_user() : 'guest' ); if (!$row || self::is_stale($row)) { return $price_html; } $base = (float) $row['base_price']; $sale = isset($row['sale_price']) ? (float) $row['sale_price'] : null; if ($sale !== null && $sale > 0 && $sale < $base) { return wc_format_sale_price(wc_price($base), wc_price($sale)); } return wc_price($base); } public static function maybe_filter_get_price($price, $product) { if (!is_object($product) || !method_exists($product, 'get_id')) { return $price; } $row = self::get_for( (int) $product->get_id(), class_exists('AIEO_Identity_Bridge') ? AIEO_Identity_Bridge::audience_key_for_current_user() : 'guest' ); if (!$row || self::is_stale($row)) { return $price; } if (isset($row['sale_price']) && (float) $row['sale_price'] > 0) { return number_format((float) $row['sale_price'], wc_get_price_decimals(), '.', ''); } return $price; } private static function is_stale(array $row) { $ttl = (int) get_option('aieo_effective_prices_ttl_seconds', 0); if ($ttl <= 0) { return false; } $computed = isset($row['computed_at']) ? strtotime($row['computed_at'] . ' UTC') : 0; if ($computed <= 0) { return true; } return (time() - $computed) > $ttl; } public static function representative_user_for_audience($audience_key) { if ($audience_key === 'guest' || $audience_key === '') { return 0; } if (strpos($audience_key, 'role:') === 0) { $role = substr($audience_key, 5); $users = get_users(array( 'role' => $role, 'number' => 1, 'fields' => 'ID', 'orderby' => 'ID', 'order' => 'ASC', )); return !empty($users) ? (int) $users[0] : 0; } if (strpos($audience_key, 'tier:') === 0) { global $wpdb; $tier_id = (int) substr($audience_key, 5); $table = $wpdb->prefix . 'aieo_dmm_loyalty_assignments'; $exists = (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s", $table )); if (!$exists) { return 0; } $uid = $wpdb->get_var($wpdb->prepare( "SELECT user_id FROM {$table} WHERE class_id = %d ORDER BY user_id ASC LIMIT 1", $tier_id )); return (int) $uid; } return 0; } public static function table_name() { global $wpdb; return $wpdb->prefix . self::TABLE_NAME; } public static function table_exists() { static $cached = null; if ($cached !== null) { return $cached; } global $wpdb; $found = (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s", self::table_name() )); return $cached = ($found > 0); } public static function maybe_install() { $current = (int) get_option(self::SCHEMA_OPTION, 0); if ($current >= self::SCHEMA_VERSION) { if (!is_admin() && !wp_doing_cron()) { return; } if (self::table_exists()) { return; } } if (!AIEO_Schema_Lock::acquire(self::SCHEMA_OPTION)) { return; } global $wpdb; $table = self::table_name(); $charset = aieo_get_charset_collate(); $sql = "CREATE TABLE {$table} ( product_id BIGINT(20) UNSIGNED NOT NULL, audience_key VARCHAR(64) NOT NULL, base_price DECIMAL(12,2) NOT NULL, sale_price DECIMAL(12,2) DEFAULT NULL, was_on_sale TINYINT(1) NOT NULL DEFAULT 0, role_price_applied TINYINT(1) NOT NULL DEFAULT 0, monthly_offer_active TINYINT(1) NOT NULL DEFAULT 0, sale_discount_pct DECIMAL(5,2) DEFAULT NULL, source_rule_id BIGINT(20) UNSIGNED DEFAULT NULL, source_rule_type VARCHAR(32) DEFAULT NULL, valid_from DATETIME DEFAULT NULL, valid_until DATETIME DEFAULT NULL, computed_at DATETIME NOT NULL, PRIMARY KEY (product_id, audience_key), KEY ix_audience (audience_key), KEY ix_source (source_rule_id, source_rule_type), KEY ix_valid (valid_until), KEY ix_was_on_sale (was_on_sale), KEY ix_role_price (role_price_applied), KEY ix_monthly (monthly_offer_active) ) {$charset};"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta($sql); self::ensure_v2_columns(); update_option(self::SCHEMA_OPTION, self::SCHEMA_VERSION, 'no'); } private static function ensure_v2_columns() { global $wpdb; $table = self::table_name(); $cols = $wpdb->get_col("SHOW COLUMNS FROM {$table}", 0); if (!is_array($cols)) { return; } $cols = array_flip($cols); $alters = array(); if (!isset($cols['was_on_sale'])) { $alters[] = "ADD COLUMN was_on_sale TINYINT(1) NOT NULL DEFAULT 0 AFTER sale_price"; } if (!isset($cols['role_price_applied'])) { $alters[] = "ADD COLUMN role_price_applied TINYINT(1) NOT NULL DEFAULT 0 AFTER was_on_sale"; } if (!isset($cols['monthly_offer_active'])) { $alters[] = "ADD COLUMN monthly_offer_active TINYINT(1) NOT NULL DEFAULT 0 AFTER role_price_applied"; } if (!isset($cols['sale_discount_pct'])) { $alters[] = "ADD COLUMN sale_discount_pct DECIMAL(5,2) DEFAULT NULL AFTER monthly_offer_active"; } if (!empty($alters)) { $wpdb->query("ALTER TABLE {$table} " . implode(', ', $alters)); } $existing_idx = $wpdb->get_col("SHOW INDEX FROM {$table}", 2); $existing_idx = is_array($existing_idx) ? array_flip(array_unique($existing_idx)) : array(); $idx_alters = array(); if (!isset($existing_idx['ix_was_on_sale'])) { $idx_alters[] = "ADD KEY ix_was_on_sale (was_on_sale)"; } if (!isset($existing_idx['ix_role_price'])) { $idx_alters[] = "ADD KEY ix_role_price (role_price_applied)"; } if (!isset($existing_idx['ix_monthly'])) { $idx_alters[] = "ADD KEY ix_monthly (monthly_offer_active)"; } if (!empty($idx_alters)) { @$wpdb->query("ALTER TABLE {$table} " . implode(', ', $idx_alters)); } } private static function hydrate(array $row) { $row['product_id'] = (int) ($row['product_id'] ?? 0); $row['audience_key'] = (string) ($row['audience_key'] ?? ''); $row['base_price'] = (float) ($row['base_price'] ?? 0); $row['sale_price'] = isset($row['sale_price']) && $row['sale_price'] !== null && $row['sale_price'] !== '' ? (float) $row['sale_price'] : null; $row['source_rule_id'] = isset($row['source_rule_id']) ? (int) $row['source_rule_id'] : null; return $row; } }