From 263540b8d2046a81d4253436e8d097a8721d9f78 Mon Sep 17 00:00:00 2001 From: Karthik Thayyil Date: Wed, 26 Nov 2025 14:31:26 +0530 Subject: [PATCH 1/5] Sync issue --- includes/Handlers/Products.php | 240 +++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/includes/Handlers/Products.php b/includes/Handlers/Products.php index b2cb3a5f..8f29e793 100644 --- a/includes/Handlers/Products.php +++ b/includes/Handlers/Products.php @@ -123,6 +123,10 @@ private function add_products_edit_screen_hooks() { add_action( 'woocommerce_product_bulk_edit_save', array( $this, 'set_synced_with_square' ) ); add_action( 'woocommerce_admin_process_product_object', array( __CLASS__, 'process_post_data' ) ); + // add custom columns to product list page + add_filter( 'manage_edit-product_columns', array( $this, 'add_product_list_columns' ), 10 ); + add_action( 'manage_product_posts_custom_column', array( $this, 'render_product_list_columns' ), 10, 2 ); + // export product sync status. add_filter( 'woocommerce_product_export_column_names', array( $this, 'add_sync_status_to_column' ) ); add_filter( 'woocommerce_product_export_product_default_columns', array( $this, 'add_sync_status_to_column' ) ); @@ -294,6 +298,242 @@ public function filter_products_synced_with_square( $query_vars ) { } + /** + * Adds custom columns to the product list page. + * + * @internal + * + * @since 2.0.0 + * + * @param array $columns Array of column names + * @return array Modified array of column names + */ + public function add_product_list_columns( $columns ) { + // Insert the new columns before the "date" column + $new_columns = array(); + + foreach ( $columns as $key => $value ) { + if ( 'date' === $key ) { + $new_columns['wc_square_sync_status'] = __( 'Sync Status', 'woocommerce-square' ); + $new_columns['wc_square_sync_time'] = __( 'Last Sync Time', 'woocommerce-square' ); + } + $new_columns[ $key ] = $value; + } + + return $new_columns; + } + + + /** + * Renders the content for custom columns in the product list page. + * + * @internal + * + * @since x.x.x + * + * @param string $column_name The name of the column to display + * @param int $post_id The post ID + */ + public function render_product_list_columns( $column_name, $post_id ) { + if ( 'wc_square_sync_status' !== $column_name && 'wc_square_sync_time' !== $column_name ) { + return; + } + + $product = wc_get_product( $post_id ); + if ( ! $product ) { + return; + } + + $is_synced_with_square = Product::is_synced_with_square( $product ); + $can_sync_with_square = Product::can_sync_with_square( $product ); + $record = $this->get_product_sync_record( $product->get_id(), $is_synced_with_square, $can_sync_with_square ); + + $has_any_records = $this->has_product_sync_records( $product->get_id() ); + + if ( 'wc_square_sync_status' === $column_name ) { + $this->render_sync_status_column( $record, $is_synced_with_square, $can_sync_with_square, $has_any_records ); + } elseif ( 'wc_square_sync_time' === $column_name ) { + $this->render_sync_time_column( $record, $is_synced_with_square ); + } + } + + + /** + * Renders the sync status column content using early returns. + * + * @internal + * + * @since x.x.x + * + * @param \WooCommerce\Square\Sync\Records\Record|null $record The sync record + * @param bool $is_synced_with_square Whether the product is synced with Square + * @param bool $can_sync_with_square Whether the product can sync with Square + * @param bool $has_any_records Whether the product has any sync records + */ + private function render_sync_status_column( $record, $is_synced_with_square, $can_sync_with_square, $has_any_records ) { + // if ( ! $can_sync_with_square ){ + // return; + // } + + if ( $is_synced_with_square ) { + printf( + '%s', + esc_attr( sanitize_html_class( 'info' ) ), + esc_html( __( 'Synced', 'woocommerce-square' ) ) + ); + return; + } + + if ( $record ) { + printf( + '%s', + esc_attr( sanitize_html_class( $record->get_type() ) ), + esc_html( $record->get_label() ) + ); + return; + } + + if ( $can_sync_with_square && ! $has_any_records ) { + printf( + '%s', + esc_attr( sanitize_html_class( 'notice' ) ), + esc_html( __( 'Ready to Sync', 'woocommerce-square' ) ) + ); + return; + } + + return; + } + + + /** + * Renders the sync time column content using early returns. + * + * @internal + * + * @since 2.0.0 + * + * @param \WooCommerce\Square\Sync\Records\Record|null $record The sync record + * @param bool $is_synced_with_square Whether the product is synced with Square + */ + private function render_sync_time_column( $record, $is_synced_with_square ) { + if ( $record ) { + echo esc_html( $record->get_local_date() ); + return; + } + + // Product is synced but no product-specific record - use global last sync time + if ( $is_synced_with_square ) { + $global_sync_time = $this->plugin->get_sync_handler()->get_last_synced_at(); + if ( $global_sync_time ) { + $date_time_format = wc_date_format() . ' ' . wc_time_format(); + $sync_date = new \DateTime(); + $sync_date->setTimestamp( $global_sync_time ); + $sync_date->setTimezone( new \DateTimeZone( wc_timezone_string() ) ); + echo esc_html( $sync_date->format( $date_time_format ) ); + return; + } + } + + return; + } + + + /** + * Gets the sync record to display for a product. + * Prioritizes unresolved alerts/notices, then latest record of any type. + * + * @internal + * + * @since 2.0.0 + * + * @param int $product_id The product ID + * @param bool $is_synced_with_square Whether the product is synced with Square + * @param bool $can_sync_with_square Whether the product can sync with Square + * @return \WooCommerce\Square\Sync\Records\Record|null The record to display or null + */ + private function get_product_sync_record( $product_id, $is_synced_with_square, $can_sync_with_square = false ) { + // Only check records if product can sync with Square + if ( ! $can_sync_with_square ) { + return null; + } + + // First, check for unresolved issues (alerts and notices) - these take priority + $unresolved_records = Records::get_records( + array( + 'product' => $product_id, + 'type' => array( 'alert', 'notice' ), + 'orderby' => 'date', + 'sort' => 'DESC', + 'limit' => 1, + ) + ); + + if ( ! empty( $unresolved_records ) ) { + return reset( $unresolved_records ); + } + + // If product is synced, get the latest record of any type + if ( $is_synced_with_square ) { + $all_records = Records::get_records( + array( + 'product' => $product_id, + 'orderby' => 'date', + 'sort' => 'DESC', + 'limit' => 1, + ) + ); + + if ( ! empty( $all_records ) ) { + return reset( $all_records ); + } + } + + // If product is not synced, check for any records (success or failure) + if ( ! $is_synced_with_square ) { + $all_records = Records::get_records( + array( + 'product' => $product_id, + 'orderby' => 'date', + 'sort' => 'DESC', + 'limit' => 1, + ) + ); + + if ( ! empty( $all_records ) ) { + return reset( $all_records ); + } + } + + return null; + } + + + /** + * Checks if a product has any sync records (of any type). + * Used to determine if a product is truly "ready to sync" (has no records yet). + * + * @internal + * + * @since 2.0.0 + * + * @param int $product_id The product ID + * @return bool True if product has any sync records, false otherwise + */ + private function has_product_sync_records( $product_id ) { + $all_records = Records::get_records( + array( + 'product' => $product_id, + 'orderby' => 'date', + 'sort' => 'DESC', + 'limit' => 1, + ) + ); + + return ! empty( $all_records ); + } + + /** * Adds general product data options to a product metabox. * From 6fc191fd4eabb178d1a3d7ed787255048ec75da2 Mon Sep 17 00:00:00 2001 From: Karthik Thayyil Date: Wed, 26 Nov 2025 19:40:39 +0530 Subject: [PATCH 2/5] Improve the codebase --- includes/Handlers/Products.php | 216 ++++++--------------------------- 1 file changed, 36 insertions(+), 180 deletions(-) diff --git a/includes/Handlers/Products.php b/includes/Handlers/Products.php index 8f29e793..aad437a6 100644 --- a/includes/Handlers/Products.php +++ b/includes/Handlers/Products.php @@ -303,7 +303,7 @@ public function filter_products_synced_with_square( $query_vars ) { * * @internal * - * @since 2.0.0 + * @since x.x.x * * @param array $columns Array of column names * @return array Modified array of column names @@ -323,7 +323,6 @@ public function add_product_list_columns( $columns ) { return $new_columns; } - /** * Renders the content for custom columns in the product list page. * @@ -346,194 +345,51 @@ public function render_product_list_columns( $column_name, $post_id ) { $is_synced_with_square = Product::is_synced_with_square( $product ); $can_sync_with_square = Product::can_sync_with_square( $product ); - $record = $this->get_product_sync_record( $product->get_id(), $is_synced_with_square, $can_sync_with_square ); - - $has_any_records = $this->has_product_sync_records( $product->get_id() ); - - if ( 'wc_square_sync_status' === $column_name ) { - $this->render_sync_status_column( $record, $is_synced_with_square, $can_sync_with_square, $has_any_records ); - } elseif ( 'wc_square_sync_time' === $column_name ) { - $this->render_sync_time_column( $record, $is_synced_with_square ); - } - } - - - /** - * Renders the sync status column content using early returns. - * - * @internal - * - * @since x.x.x - * - * @param \WooCommerce\Square\Sync\Records\Record|null $record The sync record - * @param bool $is_synced_with_square Whether the product is synced with Square - * @param bool $can_sync_with_square Whether the product can sync with Square - * @param bool $has_any_records Whether the product has any sync records - */ - private function render_sync_status_column( $record, $is_synced_with_square, $can_sync_with_square, $has_any_records ) { - // if ( ! $can_sync_with_square ){ - // return; - // } - - if ( $is_synced_with_square ) { - printf( - '%s', - esc_attr( sanitize_html_class( 'info' ) ), - esc_html( __( 'Synced', 'woocommerce-square' ) ) - ); - return; - } - - if ( $record ) { - printf( - '%s', - esc_attr( sanitize_html_class( $record->get_type() ) ), - esc_html( $record->get_label() ) - ); - return; - } - - if ( $can_sync_with_square && ! $has_any_records ) { - printf( - '%s', - esc_attr( sanitize_html_class( 'notice' ) ), - esc_html( __( 'Ready to Sync', 'woocommerce-square' ) ) - ); - return; - } - - return; - } - - - /** - * Renders the sync time column content using early returns. - * - * @internal - * - * @since 2.0.0 - * - * @param \WooCommerce\Square\Sync\Records\Record|null $record The sync record - * @param bool $is_synced_with_square Whether the product is synced with Square - */ - private function render_sync_time_column( $record, $is_synced_with_square ) { - if ( $record ) { - echo esc_html( $record->get_local_date() ); - return; - } - - // Product is synced but no product-specific record - use global last sync time - if ( $is_synced_with_square ) { - $global_sync_time = $this->plugin->get_sync_handler()->get_last_synced_at(); - if ( $global_sync_time ) { - $date_time_format = wc_date_format() . ' ' . wc_time_format(); - $sync_date = new \DateTime(); - $sync_date->setTimestamp( $global_sync_time ); - $sync_date->setTimezone( new \DateTimeZone( wc_timezone_string() ) ); - echo esc_html( $sync_date->format( $date_time_format ) ); - return; - } - } - - return; - } - - /** - * Gets the sync record to display for a product. - * Prioritizes unresolved alerts/notices, then latest record of any type. - * - * @internal - * - * @since 2.0.0 - * - * @param int $product_id The product ID - * @param bool $is_synced_with_square Whether the product is synced with Square - * @param bool $can_sync_with_square Whether the product can sync with Square - * @return \WooCommerce\Square\Sync\Records\Record|null The record to display or null - */ - private function get_product_sync_record( $product_id, $is_synced_with_square, $can_sync_with_square = false ) { - // Only check records if product can sync with Square - if ( ! $can_sync_with_square ) { - return null; - } - - // First, check for unresolved issues (alerts and notices) - these take priority - $unresolved_records = Records::get_records( - array( - 'product' => $product_id, - 'type' => array( 'alert', 'notice' ), + // Get the failed records for the product. + $unresolved_records = Records::get_records( + array( + 'product' => $post_id, + 'type' => array( 'alert', 'notice' ), 'orderby' => 'date', 'sort' => 'DESC', - 'limit' => 1, - ) + 'limit' => 1 + ) ); + $unresolved_records = reset( $unresolved_records ); + + if ( 'wc_square_sync_status' === $column_name ) { + if( $can_sync_with_square || $is_synced_with_square){ + if( $is_synced_with_square ){ + printf( + '%s', + esc_attr( sanitize_html_class( 'synced' ) ), + esc_html( __( 'Synced', 'woocommerce-square' ) ) + ); + return; + } - if ( ! empty( $unresolved_records ) ) { - return reset( $unresolved_records ); - } - - // If product is synced, get the latest record of any type - if ( $is_synced_with_square ) { - $all_records = Records::get_records( - array( - 'product' => $product_id, - 'orderby' => 'date', - 'sort' => 'DESC', - 'limit' => 1, - ) - ); - - if ( ! empty( $all_records ) ) { - return reset( $all_records ); - } - } - - // If product is not synced, check for any records (success or failure) - if ( ! $is_synced_with_square ) { - $all_records = Records::get_records( - array( - 'product' => $product_id, - 'orderby' => 'date', - 'sort' => 'DESC', - 'limit' => 1, - ) - ); + if( !empty( $unresolved_records ) ){ + printf( + '%s', + esc_attr( sanitize_html_class( 'failed-to-sync' ) ), + esc_html( __( 'Failed to Sync', 'woocommerce-square' ) ) + ); + return; + } - if ( ! empty( $all_records ) ) { - return reset( $all_records ); + if( empty( $unresolved_records ) && $can_sync_with_square && !$is_synced_with_square ){ + printf( + '%s', + esc_attr( sanitize_html_class( 'to-sync' ) ), + esc_html( __( 'Ready to Sync', 'woocommerce-square' ) ) + ); + return; + } } } - - return null; - } - - - /** - * Checks if a product has any sync records (of any type). - * Used to determine if a product is truly "ready to sync" (has no records yet). - * - * @internal - * - * @since 2.0.0 - * - * @param int $product_id The product ID - * @return bool True if product has any sync records, false otherwise - */ - private function has_product_sync_records( $product_id ) { - $all_records = Records::get_records( - array( - 'product' => $product_id, - 'orderby' => 'date', - 'sort' => 'DESC', - 'limit' => 1, - ) - ); - - return ! empty( $all_records ); } - /** * Adds general product data options to a product metabox. * From f2392d8253be7e9e8801ddaa5412c8361c7de641 Mon Sep 17 00:00:00 2001 From: Karthik Thayyil Date: Wed, 26 Nov 2025 20:11:07 +0530 Subject: [PATCH 3/5] removing sync date as the correct date is not always available --- includes/Handlers/Products.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/Handlers/Products.php b/includes/Handlers/Products.php index aad437a6..3c965f25 100644 --- a/includes/Handlers/Products.php +++ b/includes/Handlers/Products.php @@ -315,7 +315,6 @@ public function add_product_list_columns( $columns ) { foreach ( $columns as $key => $value ) { if ( 'date' === $key ) { $new_columns['wc_square_sync_status'] = __( 'Sync Status', 'woocommerce-square' ); - $new_columns['wc_square_sync_time'] = __( 'Last Sync Time', 'woocommerce-square' ); } $new_columns[ $key ] = $value; } @@ -359,7 +358,7 @@ public function render_product_list_columns( $column_name, $post_id ) { $unresolved_records = reset( $unresolved_records ); if ( 'wc_square_sync_status' === $column_name ) { - if( $can_sync_with_square || $is_synced_with_square){ + if( $can_sync_with_square || $is_synced_with_square ){ if( $is_synced_with_square ){ printf( '%s', From 27e0b2f40afc344b7a3175af8e76d8402c228df4 Mon Sep 17 00:00:00 2001 From: Karthik Thayyil Date: Wed, 26 Nov 2025 20:14:56 +0530 Subject: [PATCH 4/5] remove time --- includes/Handlers/Products.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/Handlers/Products.php b/includes/Handlers/Products.php index 3c965f25..2445c238 100644 --- a/includes/Handlers/Products.php +++ b/includes/Handlers/Products.php @@ -333,7 +333,7 @@ public function add_product_list_columns( $columns ) { * @param int $post_id The post ID */ public function render_product_list_columns( $column_name, $post_id ) { - if ( 'wc_square_sync_status' !== $column_name && 'wc_square_sync_time' !== $column_name ) { + if ( 'wc_square_sync_status' !== $column_name ) { return; } From 234159b514f88641327e5634239af988938a4f03 Mon Sep 17 00:00:00 2001 From: Karthik Thayyil Date: Sun, 30 Nov 2025 08:53:30 +0530 Subject: [PATCH 5/5] cs fixes --- includes/Handlers/Products.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/includes/Handlers/Products.php b/includes/Handlers/Products.php index 2445c238..4dbf936a 100644 --- a/includes/Handlers/Products.php +++ b/includes/Handlers/Products.php @@ -311,14 +311,14 @@ public function filter_products_synced_with_square( $query_vars ) { public function add_product_list_columns( $columns ) { // Insert the new columns before the "date" column $new_columns = array(); - + foreach ( $columns as $key => $value ) { if ( 'date' === $key ) { $new_columns['wc_square_sync_status'] = __( 'Sync Status', 'woocommerce-square' ); } $new_columns[ $key ] = $value; } - + return $new_columns; } @@ -346,20 +346,20 @@ public function render_product_list_columns( $column_name, $post_id ) { $can_sync_with_square = Product::can_sync_with_square( $product ); // Get the failed records for the product. - $unresolved_records = Records::get_records( - array( - 'product' => $post_id, - 'type' => array( 'alert', 'notice' ), + $unresolved_records = Records::get_records( + array( + 'product' => $post_id, + 'type' => array( 'alert', 'notice' ), 'orderby' => 'date', 'sort' => 'DESC', - 'limit' => 1 - ) + 'limit' => 1, + ) ); $unresolved_records = reset( $unresolved_records ); - if ( 'wc_square_sync_status' === $column_name ) { - if( $can_sync_with_square || $is_synced_with_square ){ - if( $is_synced_with_square ){ + if ( 'wc_square_sync_status' === $column_name ) { + if ( $can_sync_with_square || $is_synced_with_square ) { + if ( $is_synced_with_square ) { printf( '%s', esc_attr( sanitize_html_class( 'synced' ) ), @@ -368,7 +368,7 @@ public function render_product_list_columns( $column_name, $post_id ) { return; } - if( !empty( $unresolved_records ) ){ + if ( ! empty( $unresolved_records ) ) { printf( '%s', esc_attr( sanitize_html_class( 'failed-to-sync' ) ), @@ -377,7 +377,7 @@ public function render_product_list_columns( $column_name, $post_id ) { return; } - if( empty( $unresolved_records ) && $can_sync_with_square && !$is_synced_with_square ){ + if ( empty( $unresolved_records ) && $can_sync_with_square && ! $is_synced_with_square ) { printf( '%s', esc_attr( sanitize_html_class( 'to-sync' ) ),