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