diff --git a/admin/class-informatiq-sp-admin.php b/admin/class-informatiq-sp-admin.php index 062b74e8..fc4e8f78 100644 --- a/admin/class-informatiq-sp-admin.php +++ b/admin/class-informatiq-sp-admin.php @@ -51,6 +51,7 @@ class Informatiq_SP_Admin { add_action( 'wp_ajax_informatiq_sp_manual_sync', array( $this, 'handle_manual_sync' ) ); add_action( 'wp_ajax_informatiq_sp_test_connection', array( $this, 'handle_test_connection' ) ); add_action( 'wp_ajax_informatiq_sp_revoke_auth', array( $this, 'handle_revoke_auth' ) ); + add_action( 'wp_ajax_informatiq_sp_compare_products', array( $this, 'handle_compare_products' ) ); // Enqueue admin assets. add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) ); @@ -333,6 +334,10 @@ class Informatiq_SP_Admin {
+ render_product_comparison(); ?> + +
+ render_logs_section(); ?> @@ -575,6 +580,50 @@ class Informatiq_SP_Admin { +
+

+

+ +

+ + is_authorized() ) : ?> +

+ + +

+ + + +

+ +

+ +
+ __( 'Authorization revoked.', 'informatiq-smart-pricing' ) ) ); } + /** + * Handle product comparison AJAX request. + */ + public function handle_compare_products() { + check_ajax_referer( 'informatiq_sp_admin', 'nonce' ); + + if ( ! current_user_can( 'manage_woocommerce' ) ) { + wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'informatiq-smart-pricing' ) ) ); + } + + $merchant_id = get_option( 'informatiq_sp_merchant_id' ); + $client_id = get_option( 'informatiq_sp_client_id' ); + $client_secret = get_option( 'informatiq_sp_client_secret' ); + $refresh_token = get_option( 'informatiq_sp_refresh_token' ); + + if ( empty( $merchant_id ) || empty( $refresh_token ) ) { + wp_send_json_error( array( 'message' => __( 'Please configure settings and authorize first.', 'informatiq-smart-pricing' ) ) ); + } + + try { + $google_api = new Informatiq_SP_Google_API( + $merchant_id, + $client_id, + $client_secret, + $refresh_token, + $this->logger + ); + + // Get all Google products indexed by various identifiers. + $google_products_raw = $google_api->get_all_products(); + $google_products = array(); + + foreach ( $google_products_raw as $gp ) { + // Index by offerId. + if ( ! empty( $gp['offerId'] ) ) { + $google_products['offer_' . $gp['offerId']] = $gp; + } + // Index by gtin. + if ( ! empty( $gp['gtin'] ) ) { + $google_products['gtin_' . $gp['gtin']] = $gp; + } + } + + // Get WooCommerce in-stock products (limit to 50 for performance). + $wc_products = wc_get_products( array( + 'status' => 'publish', + 'stock_status' => 'instock', + 'limit' => 50, + 'return' => 'objects', + 'type' => array( 'simple', 'variable' ), + ) ); + + $comparison = array(); + + foreach ( $wc_products as $product ) { + $sku = $product->get_sku(); + if ( empty( $sku ) ) { + continue; + } + + // Get local price (sale price priority, then regular). + $sale_price = $product->get_sale_price(); + $regular_price = $product->get_regular_price(); + $local_price = ! empty( $sale_price ) ? $sale_price : $regular_price; + $price_type = ! empty( $sale_price ) ? 'sale' : 'regular'; + + // Try to find matching Google product. + $google_product = null; + $match_type = ''; + + // Try offerId match. + if ( isset( $google_products['offer_' . $sku] ) ) { + $google_product = $google_products['offer_' . $sku]; + $match_type = 'offerId'; + } + // Try GTIN match (when SKU is a barcode). + elseif ( isset( $google_products['gtin_' . $sku] ) ) { + $google_product = $google_products['gtin_' . $sku]; + $match_type = 'gtin'; + } + + // Get Google price. + $google_price = null; + if ( $google_product ) { + if ( isset( $google_product['price']['amountMicros'] ) ) { + $google_price = (float) $google_product['price']['amountMicros'] / 1000000; + } + } + + $comparison[] = array( + 'id' => $product->get_id(), + 'name' => $product->get_name(), + 'sku' => $sku, + 'local_price' => $local_price ? (float) $local_price : null, + 'price_type' => $price_type, + 'google_price' => $google_price, + 'match_type' => $match_type, + 'found' => ! empty( $google_product ), + 'google_offer' => $google_product ? ( $google_product['offerId'] ?? '' ) : '', + ); + } + + wp_send_json_success( array( + 'products' => $comparison, + 'google_count' => count( $google_products_raw ), + 'wc_count' => count( $wc_products ), + 'currency' => get_woocommerce_currency_symbol(), + ) ); + + } catch ( Exception $e ) { + wp_send_json_error( array( 'message' => $e->getMessage() ) ); + } + } + /** * Enqueue admin assets. * diff --git a/assets/js/admin.js b/assets/js/admin.js index 26e4580d..24884022 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -20,6 +20,7 @@ $('#informatiq-sp-manual-sync').on('click', this.handleManualSync); $('#informatiq-sp-test-connection').on('click', this.handleTestConnection); $('#informatiq-sp-revoke-auth').on('click', this.handleRevokeAuth); + $('#informatiq-sp-compare-products').on('click', this.handleCompareProducts); }, /** @@ -179,6 +180,82 @@ $button.prop('disabled', false).text('Revoke Authorization'); } }); + }, + + /** + * Handle compare products button click + */ + handleCompareProducts: function(e) { + e.preventDefault(); + + var $button = $(this); + var $spinner = $button.next('.spinner'); + var $results = $('#informatiq-sp-comparison-results'); + var $tbody = $('#informatiq-sp-comparison-tbody'); + + // Disable button and show spinner + $button.prop('disabled', true); + $spinner.addClass('is-active'); + + // Make AJAX request + $.ajax({ + url: informatiqSP.ajaxUrl, + type: 'POST', + data: { + action: 'informatiq_sp_compare_products', + nonce: informatiqSP.nonce + }, + success: function(response) { + if (response.success) { + var data = response.data; + var html = ''; + + if (data.products.length === 0) { + html = 'No in-stock products with SKU found.'; + } else { + $.each(data.products, function(i, product) { + var localPrice = product.local_price ? data.currency + parseFloat(product.local_price).toFixed(2) : '-'; + var googlePrice = product.google_price ? data.currency + parseFloat(product.google_price).toFixed(2) : '-'; + var priceLabel = product.price_type === 'sale' ? ' (sale)' : ' (regular)'; + var matchLabel = product.match_type ? product.match_type : '-'; + var statusClass = product.found ? 'color: #00a32a;' : 'color: #d63638;'; + var statusText = product.found ? 'Found' : 'Not Found'; + + if (product.found && product.google_offer) { + matchLabel += ' (' + product.google_offer + ')'; + } + + html += ''; + html += '' + product.name + ''; + html += '' + product.sku + ''; + html += '' + localPrice + priceLabel + ''; + html += '' + googlePrice + ''; + html += '' + matchLabel + ''; + html += '' + statusText + ''; + html += ''; + }); + } + + $tbody.html(html); + $results.show(); + + // Show summary + var summary = 'Showing ' + data.products.length + ' WooCommerce products. '; + summary += data.google_count + ' products found in Google Merchant Center.'; + $button.after('

' + summary + '

'); + + } else { + alert('Error: ' + (response.data.message || 'Unknown error')); + } + }, + error: function(jqXHR, textStatus, errorThrown) { + alert('Error: ' + errorThrown); + }, + complete: function() { + $button.prop('disabled', false); + $spinner.removeClass('is-active'); + } + }); } };