From 76a0add8886359cb2f6b241ac7418839ee37a7c4 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 29 Jan 2025 17:38:06 +0530 Subject: [PATCH 1/4] Update stock status to "outofstock" in Woo side if product marked sold out at Square. --- includes/Handlers/Product.php | 16 ++++++++------ includes/Sync/Helper.php | 20 ++++++++++++++---- includes/Sync/Interval_Polling.php | 27 ++++++++++++++---------- includes/Sync/Manual_Synchronization.php | 6 ++++-- includes/Sync/Product_Import.php | 8 +++---- 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/includes/Handlers/Product.php b/includes/Handlers/Product.php index a3dc960b9..c1bb27baa 100644 --- a/includes/Handlers/Product.php +++ b/includes/Handlers/Product.php @@ -462,13 +462,15 @@ public static function update_stock_from_square( \WC_Product $product, $save = t } } - $is_inventory_tracking = isset( $inventory_tracking[ $square_id ] ) ? $inventory_tracking[ $square_id ] : true; + $inventory_tracking_data = $inventory_tracking[ $square_id ] ?? array(); + $is_inventory_tracking = $inventory_tracking_data['track_inventory'] ?? true; + $sold_out = $inventory_tracking_data['sold_out'] ?? false; if ( $is_inventory_tracking ) { $product->set_manage_stock( true ); $product->set_stock_quantity( $stock ); } else { - $product->set_stock_status( 'instock' ); + $product->set_stock_status( $sold_out ? 'outofstock' : 'instock' ); $product->set_manage_stock( false ); } @@ -512,9 +514,11 @@ public static function update_products_stock_from_square( $product_ids ) { $inventory_tracking = Helper::get_catalog_inventory_tracking( $response->get_data()->getObjects() ); foreach ( $response->get_data()->getObjects() as $catalog_object ) { - $square_id = $catalog_object->getId(); - $stock = $inventory_hash[ $square_id ] ?? 0; - $is_inventory_tracking = isset( $inventory_tracking[ $square_id ] ) ? $inventory_tracking[ $square_id ] : true; + $square_id = $catalog_object->getId(); + $stock = $inventory_hash[ $square_id ] ?? 0; + $inventory_tracking_data = $inventory_tracking[ $square_id ] ?? array(); + $is_inventory_tracking = $inventory_tracking_data['track_inventory'] ?? true; + $sold_out = $inventory_tracking_data['sold_out'] ?? false; $product_id = $products_map[ $square_id ]['product_id']; $product = wc_get_product( $product_id ); @@ -527,7 +531,7 @@ public static function update_products_stock_from_square( $product_ids ) { $product->set_manage_stock( true ); $product->set_stock_quantity( $stock ); } else { - $product->set_stock_status( 'instock' ); + $product->set_stock_status( $sold_out ? 'outofstock' : 'instock' ); $product->set_manage_stock( false ); } diff --git a/includes/Sync/Helper.php b/includes/Sync/Helper.php index 8a8a7c798..92d3b4c06 100644 --- a/includes/Sync/Helper.php +++ b/includes/Sync/Helper.php @@ -104,6 +104,11 @@ public static function get_catalog_inventory_tracking( $catalog_objects ) { $location_overrides = $variation_data->getLocationOverrides(); $configured_location = wc_square()->get_settings_handler()->get_location_id(); + $default_data = array( + 'track_inventory' => $variation_data->getTrackInventory(), + 'sold_out' => false, + ); + if ( ! empty( $location_overrides ) ) { $location_ids = array_map( function ( $location_override ) { @@ -113,7 +118,7 @@ function ( $location_override ) { ); if ( ! in_array( $configured_location, $location_ids, true ) ) { - $catalog_objects_tracking[ $catalog_object->getId() ] = $variation_data->getTrackInventory(); + $catalog_objects_tracking[ $catalog_object->getId() ] = $default_data; continue; } @@ -121,15 +126,22 @@ function ( $location_override ) { $location_id = $location_override->getLocationId(); if ( $configured_location === $location_id ) { + $sold_out = $location_override->getSoldOut() ?? false; if ( ! is_null( $location_override->getTrackInventory() ) ) { - $catalog_objects_tracking[ $catalog_object->getId() ] = $location_override->getTrackInventory(); + $catalog_objects_tracking[ $catalog_object->getId() ] = array( + 'track_inventory' => $location_override->getTrackInventory(), + 'sold_out' => $sold_out, + ); } else { - $catalog_objects_tracking[ $catalog_object->getId() ] = $variation_data->getTrackInventory(); + $catalog_objects_tracking[ $catalog_object->getId() ] = array( + 'track_inventory' => $variation_data->getTrackInventory(), + 'sold_out' => $sold_out, + ); } } } } else { - $catalog_objects_tracking[ $catalog_object->getId() ] = $variation_data->getTrackInventory(); + $catalog_objects_tracking[ $catalog_object->getId() ] = $default_data; } } diff --git a/includes/Sync/Interval_Polling.php b/includes/Sync/Interval_Polling.php index dc9f342c3..2a0e32851 100644 --- a/includes/Sync/Interval_Polling.php +++ b/includes/Sync/Interval_Polling.php @@ -278,12 +278,17 @@ protected function update_inventory_tracking() { $catalog_objects_tracking_stats = Helper::get_catalog_inventory_tracking( $objects ); $catalog_objects_to_update = array(); - foreach ( $catalog_objects_tracking_stats as $catalog_object_id => $is_tracking_inventory ) { + foreach ( $catalog_objects_tracking_stats as $catalog_object_id => $inventory_data ) { + $is_tracking_inventory = $inventory_data['track_inventory'] ?? true; + $sold_out = $inventory_data['sold_out'] ?? false; $product = Product::get_product_by_square_variation_id( $catalog_object_id ); if ( $product instanceof \WC_Product ) { $manage_stock = $product->get_manage_stock(); - // If Inventory tracking is the same as the product's manage stock setting, skip. - if ( (bool) $is_tracking_inventory === (bool) $manage_stock ) { + $stock_status = $product->get_stock_status(); + $out_of_stock = 'outofstock' === $stock_status; + + // If Inventory tracking is the same as the product's manage stock setting and sold_old value same, skip. + if ( (bool) $is_tracking_inventory === (bool) $manage_stock && (bool) $sold_out === (bool) $out_of_stock ) { continue; } $catalog_objects_to_update[] = $catalog_object_id; @@ -297,9 +302,9 @@ protected function update_inventory_tracking() { foreach ( $catalog_objects_to_update as $catalog_object_id ) { $product = Product::get_product_by_square_variation_id( $catalog_object_id ); if ( $product instanceof \WC_Product ) { - $is_tracking_inventory = isset( $catalog_objects_tracking_stats[ $catalog_object_id ] ) ? - $catalog_objects_tracking_stats[ $catalog_object_id ] : - true; + $inventory_data = $catalog_objects_tracking_stats[ $catalog_object_id ] ?? array(); + $is_tracking_inventory = $inventory_data['track_inventory'] ?? true; + $sold_out = $inventory_data['sold_out'] ?? false; /* If catalog object is tracked and has a quantity > 0 set in Square. */ if ( $is_tracking_inventory && isset( $inventory_hash[ $catalog_object_id ] ) ) { @@ -313,7 +318,7 @@ protected function update_inventory_tracking() { /* If the catalog object is not tracked in Square at all. */ } else { - $product->set_stock_status( 'instock' ); + $product->set_stock_status( status: $sold_out ? 'outofstock' : 'instock' ); $product->set_manage_stock( false ); } @@ -401,15 +406,15 @@ protected function update_inventory_counts() { // Square can return multiple "types" of counts, WooCommerce only distinguishes whether a product is in stock or not if ( $product instanceof \WC_Product ) { - $is_tracking_inventory = isset( $catalog_objects_tracking_stats[ $catalog_object_id ] ) ? - $catalog_objects_tracking_stats[ $catalog_object_id ] : - true; + $inventory_data = $catalog_objects_tracking_stats[ $catalog_object_id ] ?? array(); + $is_tracking_inventory = $inventory_data['track_inventory'] ?? true; + $sold_out = $inventory_data['sold_out'] ?? false; if ( $is_tracking_inventory ) { $product->set_manage_stock( true ); $product->set_stock_quantity( $stats['quantity'] ); } else { - $product->set_stock_status( 'instock' ); + $product->set_stock_status( $sold_out ? 'outofstock' : 'instock' ); $product->set_manage_stock( false ); } diff --git a/includes/Sync/Manual_Synchronization.php b/includes/Sync/Manual_Synchronization.php index 07e419887..adb3c9ea6 100644 --- a/includes/Sync/Manual_Synchronization.php +++ b/includes/Sync/Manual_Synchronization.php @@ -1523,7 +1523,9 @@ protected function pull_inventory() { $catalog_objects_tracking_stats = Helper::get_catalog_objects_tracking_stats( $catalog_object_ids ); - foreach ( $catalog_objects_tracking_stats as $catalog_object_id => $is_tracking_inventory ) { + foreach ( $catalog_objects_tracking_stats as $catalog_object_id => $inventory_data ) { + $is_tracking_inventory = $inventory_data['track_inventory'] ?? true; + $sold_out = $inventory_data['sold_out'] ?? false; if ( in_array( $catalog_object_id, $in_progress['processed_variation_ids'], false ) ) { // phpcs:disable WordPress.PHP.StrictInArray.FoundNonStrictFalse continue; @@ -1545,7 +1547,7 @@ protected function pull_inventory() { /* If the catalog object is not tracked in Square at all. */ } else { - $product->set_stock_status( 'instock' ); + $product->set_stock_status( $sold_out ? 'outofstock' : 'instock' ); $product->set_manage_stock( false ); } diff --git a/includes/Sync/Product_Import.php b/includes/Sync/Product_Import.php index d3ead42fc..1d2bcde15 100644 --- a/includes/Sync/Product_Import.php +++ b/includes/Sync/Product_Import.php @@ -285,9 +285,9 @@ static function( \Square\Models\CatalogObject $catalog_object ) { $product = Product::get_product_by_square_variation_id( $catalog_object->getId() ); if ( $product && $product instanceof \WC_Product ) { - $is_tracking_inventory = isset( $catalog_objects_hash[ $catalog_object->getId() ] ) ? - $catalog_objects_hash[ $catalog_object->getId() ] : - false; + $inventory_data = $catalog_objects_hash[ $catalog_object->getId() ] ?? array(); + $is_tracking_inventory = $inventory_data['track_inventory'] ?? true; + $sold_out = $inventory_data['sold_out'] ?? false; /* If catalog object is tracked and has a quantity > 0 set in Square. */ if ( $is_tracking_inventory && isset( $inventory_hash[ $catalog_object->getId() ] ) ) { @@ -301,7 +301,7 @@ static function( \Square\Models\CatalogObject $catalog_object ) { /* If the catalog object is not tracked in Square at all. */ } else { - $product->set_stock_status( 'instock' ); + $product->set_stock_status( $sold_out ? 'outofstock' : 'instock' ); $product->set_manage_stock( false ); } From 9978c8483dcfdc7d96a74ed0bf5aa89f4a63b8b9 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 29 Jan 2025 19:12:10 +0530 Subject: [PATCH 2/4] Set product sold out from woo to Square during initial sync. --- includes/Handlers/Product/Woo_SOR.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/includes/Handlers/Product/Woo_SOR.php b/includes/Handlers/Product/Woo_SOR.php index f9196fd29..0da84b5c0 100644 --- a/includes/Handlers/Product/Woo_SOR.php +++ b/includes/Handlers/Product/Woo_SOR.php @@ -229,14 +229,30 @@ public static function update_catalog_variation( CatalogObject $catalog_object, } if ( wc_square()->get_settings_handler()->is_inventory_sync_enabled() ) { + $track_inventory = $variation_data->getTrackInventory(); + $location_overrides = $variation_data->getLocationOverrides(); + /* * Only update track_inventory if it's not set. * This will only update inventory tracking on new variations. * inventory tracking will remain the same for existing variations. */ - $track_inventory = $variation_data->getTrackInventory(); - if ( is_null( $track_inventory ) ) { + if ( is_null( $track_inventory ) && is_null( $location_overrides ) ) { $variation_data->setTrackInventory( $product->get_manage_stock() ); + + // If the product is not managing stock and is out of stock, set it as sold out. + if ( ! $product->get_manage_stock() && 'outofstock' === $product->get_stock_status() ) { + $configured_location = wc_square()->get_settings_handler()->get_location_id(); + $location_override = new \Square\Models\ItemVariationLocationOverrides(); + $location_override->setLocationId( $configured_location ); + // We need to set track_inventory to true to be able to set sold_out to true, without it will be ignored. + $location_override->setTrackInventory( true ); + $location_override->setSoldOut( true ); + $location_overrides = array( $location_override ); + + // Set the location overrides. + $variation_data->setLocationOverrides( $location_overrides ); + } } } From 69685b4a203faa505d980225b855c4b3f2fb773e Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Mon, 3 Feb 2025 17:25:43 +0530 Subject: [PATCH 3/4] Disable stock status field. --- src/js/admin/wc-square-admin-products.js | 26 ++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/js/admin/wc-square-admin-products.js b/src/js/admin/wc-square-admin-products.js index 2a254c93c..b24393eb1 100644 --- a/src/js/admin/wc-square-admin-products.js +++ b/src/js/admin/wc-square-admin-products.js @@ -106,7 +106,10 @@ jQuery( document ).ready( ( $ ) => { } } else { $stockQty.prop( 'readonly', false ); - $stockStatus.prop( 'readonly', false ); + $stockStatus.off('mousedown keydown change'); + $stockStatus.css( { + opacity: 1, + } ); } } else { $manageStockInput.on( 'click', () => { @@ -129,7 +132,12 @@ jQuery( document ).ready( ( $ ) => { $( '.backorder_field' ).hide(); } else { $stockQty.prop( 'readonly', true ); - $stockStatus.prop( 'readonly', true ); + $stockStatus.on( 'mousedown keydown change', () => { + return false; + } ); + $stockStatus.css( { + opacity: 0.5, + } ); } } } ).trigger( 'change' ); @@ -328,6 +336,9 @@ jQuery( document ).ready( ( $ ) => { const $stockFields = $( '.stock_fields' ); const $stockInput = $stockFields.find( '#_stock' ); const $stockStatus = $( '.stock_status_field' ); + const $stockStatusInput = $stockStatus.find( + 'input[name="_stock_status"]' + ); const $manageField = $( '._manage_stock_field' ); const $manageInput = $manageField.find( '#_manage_stock' ); const $manageDesc = $manageField.find( '.description' ); @@ -372,6 +383,10 @@ jQuery( document ).ready( ( $ ) => { }); $manageInput.css({ opacity: '0.5' }); + // disable stock status radios. + $stockStatusInput.css({ opacity: 0.5 }); + $stockStatusInput.on('mousedown keydown click', () => { return false; }); + $stockInput.prop( 'readonly', true ); // WooCommerce SoR - note: for variable products, the stock can be fetched for individual variations. @@ -447,6 +462,8 @@ jQuery( document ).ready( ( $ ) => { $manageInput.off( 'click' ); $manageInput.css( { opacity: 1 } ); $manageInput.prop( 'checked', manageStockOriginal ); + $stockStatusInput.css({ opacity: 1 }); + $stockStatusInput.off('mousedown keydown click'); if ( ! variableProduct ) { if ( manageStockOriginal ) { @@ -467,6 +484,7 @@ jQuery( document ).ready( ( $ ) => { const $variationManageField = $variationManageInput.parent(); const $variationStockInput = $( e ).find( '.wc_input_stock' ); const $variationStockField = $variationStockInput.parent(); + const $variationStockStatusField = $( e ).find( '.variable_stock_status select' ); // Square manages variations stock if ( useSquare ) { @@ -476,6 +494,8 @@ jQuery( document ).ready( ( $ ) => { return false; }); $variationManageInput.css( { opacity: '0.5' } ); + $variationStockStatusField.css( { opacity: 0.5 } ); + $variationStockStatusField.on('mousedown keydown change', () => { return false; }); // add a note that the variation stock is managed by square, but check if it wasn't added already to avoid duplicates. if ( 0 === $variationManageField.find( '.description' ).length ) { @@ -564,6 +584,8 @@ jQuery( document ).ready( ( $ ) => { $variationManageInput.off( 'click' ); $variationManageInput.css( { opacity: 1 } ); $variationManageInput.next( '.description' ).remove(); + $variationStockStatusField.css( { opacity: 1 } ); + $variationStockStatusField.off( 'mousedown keydown change' ); } } ); // initial page load handling. From c59e2e168cfe71caf92f11df370208e42d716b7a Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Mon, 3 Feb 2025 18:31:56 +0530 Subject: [PATCH 4/4] Fixed "Equals sign not aligned with surrounding assignments" --- includes/Handlers/Product/Woo_SOR.php | 4 ++-- includes/Sync/Interval_Polling.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/Handlers/Product/Woo_SOR.php b/includes/Handlers/Product/Woo_SOR.php index 0da84b5c0..4409ab74d 100644 --- a/includes/Handlers/Product/Woo_SOR.php +++ b/includes/Handlers/Product/Woo_SOR.php @@ -229,8 +229,8 @@ public static function update_catalog_variation( CatalogObject $catalog_object, } if ( wc_square()->get_settings_handler()->is_inventory_sync_enabled() ) { - $track_inventory = $variation_data->getTrackInventory(); - $location_overrides = $variation_data->getLocationOverrides(); + $track_inventory = $variation_data->getTrackInventory(); + $location_overrides = $variation_data->getLocationOverrides(); /* * Only update track_inventory if it's not set. diff --git a/includes/Sync/Interval_Polling.php b/includes/Sync/Interval_Polling.php index 2a0e32851..e56cd70a1 100644 --- a/includes/Sync/Interval_Polling.php +++ b/includes/Sync/Interval_Polling.php @@ -281,7 +281,7 @@ protected function update_inventory_tracking() { foreach ( $catalog_objects_tracking_stats as $catalog_object_id => $inventory_data ) { $is_tracking_inventory = $inventory_data['track_inventory'] ?? true; $sold_out = $inventory_data['sold_out'] ?? false; - $product = Product::get_product_by_square_variation_id( $catalog_object_id ); + $product = Product::get_product_by_square_variation_id( $catalog_object_id ); if ( $product instanceof \WC_Product ) { $manage_stock = $product->get_manage_stock(); $stock_status = $product->get_stock_status(); @@ -318,7 +318,7 @@ protected function update_inventory_tracking() { /* If the catalog object is not tracked in Square at all. */ } else { - $product->set_stock_status( status: $sold_out ? 'outofstock' : 'instock' ); + $product->set_stock_status( $sold_out ? 'outofstock' : 'instock' ); $product->set_manage_stock( false ); }