Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5ad44d6
manual-sync-deduplication
rithikb24 Jul 23, 2025
ba3cb90
modify sync working
rithikb24 Jul 29, 2025
e1c3dc8
Merge branch 'facebook:main' into feat/modify-sync-button
rithikb24 Jul 29, 2025
cea237c
chore: update copywriting of modify button
rithikb24 Jul 30, 2025
a8ecc4a
chore: update sync all button label
rithikb24 Jul 30, 2025
b5d6f73
chore: fixing comments
rithikb24 Aug 1, 2025
121c2f4
Merge branch 'facebook:main' into feat/modify-sync-button
rithikb24 Aug 1, 2025
f8a1aae
chore: fixing quotes to remove PHPCS error
rithikb24 Aug 1, 2025
fd49de8
fix: remove field added for testing
rithikb24 Aug 4, 2025
ae6543e
chore: fix indentation
rithikb24 Aug 4, 2025
dc5bb01
feat: moved timestamp update after succesfuly api call
rithikb24 Aug 4, 2025
7988bc5
feat: error handling & using current logger
rithikb24 Aug 4, 2025
a413e1d
chore: fix liniting errors
rithikb24 Aug 4, 2025
0376c51
feat: adding unit tests
rithikb24 Aug 4, 2025
915941e
feat: toggle sync modify button
rithikb24 Aug 5, 2025
90dff8c
chore: fix versions
rithikb24 Aug 6, 2025
d19f13b
feat: add help tips
rithikb24 Aug 6, 2025
089c328
Feat/sync changes (#1)
rithikb24 Aug 7, 2025
683860c
chore: remove help tip
rithikb24 Aug 7, 2025
b5742e3
chore: remove extra stuff
rithikb24 Aug 7, 2025
5e4b52a
fix: make product id timestamp updated safer
rithikb24 Aug 11, 2025
c2ccab7
fix: better regex handling
rithikb24 Aug 12, 2025
448f621
fix: tests for timestamps
rithikb24 Aug 12, 2025
a184298
fix: tests retry
rithikb24 Aug 12, 2025
bc8a964
fix: retrying tests
rithikb24 Aug 12, 2025
b75fd01
fix: seperate try/catch
rithikb24 Aug 12, 2025
ff79d2b
feat: keeping update function seperate for maintainability
rithikb24 Aug 14, 2025
994cb7c
Merge branch 'facebook:main' into feat/modify-sync-button
rithikb24 Sep 1, 2025
c415bec
Merge branch 'facebook:main' into feat/modify-sync-button
rithikb24 Sep 3, 2025
3dc29f6
fix: remove extra logging
rithikb24 Sep 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions includes/AJAX.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use WooCommerce\Facebook\Admin\Settings_Screens\Shops;
use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
use WooCommerce\Facebook\Handlers\WhatsAppUtilityConnection;
use WooCommerce\Facebook\Framework\Logger;

defined( 'ABSPATH' ) || exit;

Expand Down Expand Up @@ -128,7 +129,7 @@ function ( $name, $slug ) use ( $term ) {
}

/**
* Syncs all products via AJAX.
* Syncs all products via AJAX (modified products first, then all products).
*
* @internal
*
Expand All @@ -141,16 +142,70 @@ public function sync_products() {
return;
}

check_admin_referer( Product_Sync::ACTION_SYNC_PRODUCTS, 'nonce' );

try {
facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_all_products();
check_admin_referer( Product_Sync::ACTION_SYNC_PRODUCTS, 'nonce' );

// Step 1: Queue modified products for sync
try {
$sync_handler = facebook_for_woocommerce()->get_products_sync_handler();
$sync_handler->create_or_update_modified_products();
} catch ( \Exception $exception ) {
Logger::log(
'Error during modified products sync',
[
'event' => 'ajax_product_sync_step1_error',
'error_message' => $exception->getMessage(),
],
[
'should_save_log_in_woocommerce' => true,
'woocommerce_log_level' => \WC_Log_Levels::ERROR,
],
$exception
);
// Continue with full sync even if modified products sync fails
}

// Step 2: Queue all products for sync
try {
// Create a new sync handler instance to avoid request array conflicts
$sync_handler_all = new \WooCommerce\Facebook\Products\Sync();
$sync_handler_all->create_or_update_all_products();
} catch ( \Exception $exception ) {
Logger::log(
'Error during all products sync',
[
'event' => 'ajax_product_sync_step2_error',
'error_message' => $exception->getMessage(),
],
[
'should_save_log_in_woocommerce' => true,
'woocommerce_log_level' => \WC_Log_Levels::ERROR,
],
$exception
);
wp_send_json_error( $exception->getMessage() );
return;
}

wp_send_json_success();
} catch ( \Exception $exception ) {
Logger::log(
'Error during product sync initialization',
[
'event' => 'ajax_product_sync_init_error',
'error_message' => $exception->getMessage(),
],
[
'should_save_log_in_woocommerce' => true,
'woocommerce_log_level' => \WC_Log_Levels::ERROR,
],
$exception
);
wp_send_json_error( $exception->getMessage() );
}
}


/**
* Syncs all coupons via AJAX.
*
Expand Down
53 changes: 53 additions & 0 deletions includes/Products/Sync.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

namespace WooCommerce\Facebook\Products;

use WooCommerce\Facebook\Framework\Logger;

defined( 'ABSPATH' ) || exit;

/**
Expand Down Expand Up @@ -81,6 +83,57 @@ public function create_or_update_all_products() {
$profiling_logger->stop( 'create_or_update_all_products' );
}

/**
* Adds all eligible product IDs to the requests array that were updated/modified.
*
* Uses the same logic that the feed handler uses to get a list of product IDs to sync.
*
* @see \WC_Facebook_Product_Feed::get_product_ids()
* @see \WC_Facebook_Product_Feed::write_product_feed_file()
*
* @since 3.5.8
*/
public function create_or_update_modified_products() {
try {
// Get all product IDs that are eligible for sync
$all_product_ids = \WC_Facebookcommerce_Utils::get_all_product_ids_for_sync();

// Filter to only get products modified since last sync
$products_to_sync = array();

foreach ( $all_product_ids as $product_id ) {
$product = wc_get_product( $product_id );
if ( ! $product ) {
continue;
}

$last_sync_time = get_post_meta( $product_id, '_fb_sync_last_time', true );
$modified_time = $product->get_date_modified() ? $product->get_date_modified()->getTimestamp() : 0;

// If never synced or modified since last sync (using >= to catch same-second modifications), add to sync queue
if ( ! $last_sync_time || $modified_time >= $last_sync_time ) {
$products_to_sync[] = $product_id;
}
}

// Queue up filtered IDs for sync
$this->create_or_update_products( $products_to_sync );
} catch ( \Exception $e ) {
// Log the error but don't interrupt the sync process
Logger::log(
'Error syncing modified products',
[
'event' => 'product_sync_modified_products_error',
'error_message' => $e->getMessage(),
],
[
'should_save_log_in_woocommerce' => true,
'woocommerce_log_level' => \WC_Log_Levels::ERROR,
],
$e
);
}
}

/**
* Adds all eligible product IDs to the requests array to be created or updated.
Expand Down
46 changes: 46 additions & 0 deletions includes/Products/Sync/Background.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
defined( 'ABSPATH' ) || exit;

use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
use WooCommerce\Facebook\Framework\Logger;
use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
use WooCommerce\Facebook\Framework\Utilities\BackgroundJobHandler;
use WooCommerce\Facebook\Products;
Expand Down Expand Up @@ -134,6 +135,9 @@ public function process_items( $job, $data, $items_per_batch = null ) {
$handles = $this->send_item_updates( $requests );
$job->handles = ! isset( $job->handles ) || ! is_array( $job->handles ) ? $handles : array_merge( $job->handles, $handles );
$this->update_job( $job );

// Update timestamps after successful API call - only if we got handles back
$this->update_sync_timestamps( $requests, $handles );
} catch ( ApiException $e ) {
/* translators: Placeholders: %1$s - <string job ID, %2$s - <strong> error message */
$message = sprintf( __( 'There was an error trying sync products using the Catalog Batch API for job %1$s: %2$s', 'facebook-for-woocommerce' ), $job->id, $e->getMessage() );
Expand Down Expand Up @@ -254,4 +258,46 @@ private function send_item_updates( array $requests ): array {
$handles = ( isset( $response_handles ) && is_array( $response_handles ) ) ? $response_handles : array();
return $handles;
}

/**
* Updates sync timestamps for products after successful API call.
*
* @since 3.5.5
*
* @param array $requests Array of sync requests.
* @param array $handles Array of handles returned from API call.
*/
protected function update_sync_timestamps( array $requests, array $handles ) {
if ( ! empty( $handles ) ) {
try {
$current_time = time();
foreach ( $requests as $request ) {
if ( Sync::ACTION_UPDATE === $request['method'] && isset( $request['data']['id'] ) && ! empty( $request['data']['id'] ) ) {
// Extract product ID from retailer ID by taking digits after the last underscore
// Works with any format: wc_post_id_123, pagination-571_1167, simple-sku_456, etc.
if ( preg_match( '/_(\d+)$/', $request['data']['id'], $matches ) ) {
$product_id = (int) $matches[1];
if ( $product_id > 0 ) {
update_post_meta( $product_id, '_fb_sync_last_time', $current_time );
}
}
}
}
} catch ( \Exception $e ) {
// Log the error but don't interrupt the sync process
Logger::log(
'Error updating product sync timestamps',
[
'event' => 'product_sync_timestamp_update_error',
'error_message' => $e->getMessage(),
],
[
'should_save_log_in_woocommerce' => true,
'woocommerce_log_level' => \WC_Log_Levels::ERROR,
],
$e
);
}
}
}
}
Loading
Loading