diff --git a/woo-business-central/admin/class-wbc-admin.php b/woo-business-central/admin/class-wbc-admin.php index a12097d..e166f04 100644 --- a/woo-business-central/admin/class-wbc-admin.php +++ b/woo-business-central/admin/class-wbc-admin.php @@ -89,6 +89,12 @@ class WBC_Admin { register_setting( 'wbc_sync', 'wbc_category_parent_map', array( 'sanitize_callback' => 'sanitize_textarea_field', ) ); + register_setting( 'wbc_sync', 'wbc_create_missing_products', array( + 'sanitize_callback' => array( $this, 'sanitize_checkbox' ), + ) ); + register_setting( 'wbc_sync', 'wbc_new_product_status', array( + 'sanitize_callback' => array( $this, 'sanitize_new_product_status' ), + ) ); // Order settings (own group) register_setting( 'wbc_orders', 'wbc_enable_order_sync', array( @@ -173,6 +179,16 @@ class WBC_Admin { return $value === 'yes' ? 'yes' : 'no'; } + /** + * Sanitize the new-product status setting + * + * @param string $value Input value. + * @return string 'publish' or 'draft'. + */ + public function sanitize_new_product_status( $value ) { + return $value === 'publish' ? 'publish' : 'draft'; + } + /** * Enqueue admin scripts * diff --git a/woo-business-central/admin/partials/wbc-admin-display.php b/woo-business-central/admin/partials/wbc-admin-display.php index 27b87d5..1dcbbd9 100644 --- a/woo-business-central/admin/partials/wbc-admin-display.php +++ b/woo-business-central/admin/partials/wbc-admin-display.php @@ -225,6 +225,40 @@ $tabs = array(

+

+ +

+ + + + + + +

+ +

+ + + + + + + + +

+ +

diff --git a/woo-business-central/includes/class-wbc-product-sync.php b/woo-business-central/includes/class-wbc-product-sync.php index ee1886b..43f9983 100644 --- a/woo-business-central/includes/class-wbc-product-sync.php +++ b/woo-business-central/includes/class-wbc-product-sync.php @@ -285,6 +285,12 @@ class WBC_Product_Sync { $this->results['total']++; $product = $this->find_wc_product_for_item( $item ); + $is_new = false; + + if ( ! $product && get_option( 'wbc_create_missing_products', 'no' ) === 'yes' ) { + $product = $this->create_wc_product_from_item( $item ); + $is_new = (bool) $product; + } if ( ! $product ) { $this->results['skipped']++; @@ -308,11 +314,54 @@ class WBC_Product_Sync { if ( $updated ) { $this->results['success']++; + if ( $is_new ) { + WBC_Logger::info( 'ProductSync', 'Created new WooCommerce product from BC item', array( + 'product_id' => $product->get_id(), + 'item_number' => $item['number'] ?? '', + 'sku' => $product->get_sku(), + ) ); + } } else { $this->results['failed']++; } } + /** + * Create a new WooCommerce product from a BC item + * + * Bare creation only (name, SKU, status) - stock/price/brand are filled + * in right after by the normal update_product() call, so that logic + * isn't duplicated here. + * + * @param array $item BC item data. + * @return WC_Product|false + */ + private function create_wc_product_from_item( $item ) { + $sku = ! empty( $item['gtin'] ) ? $item['gtin'] : ( $item['number'] ?? '' ); + + if ( empty( $sku ) ) { + return false; + } + + $product = new WC_Product_Simple(); + $product->set_name( $item['displayName'] ?? $sku ); + + try { + $product->set_sku( $sku ); + $product->set_status( get_option( 'wbc_new_product_status', 'draft' ) === 'publish' ? 'publish' : 'draft' ); + $product->save(); + } catch ( Exception $e ) { + WBC_Logger::error( 'ProductSync', 'Failed to create WooCommerce product from BC item', array( + 'item_number' => $item['number'] ?? '', + 'sku' => $sku, + 'error' => $e->getMessage(), + ) ); + return false; + } + + return $product; + } + /** * Find the WooCommerce product matching a BC item, via a local SKU lookup * (no BC round trip) - tries GTIN, then item number, then Item Reference