Add option to include non-public products in selections (v1.3.0)
All checks were successful
Create Release Package / build-release (push) Successful in 1m6s

Allow draft and private products to appear in composable product
selections. Useful when products should only be sold as part of a
composition, not individually. Includes global setting and per-product
override with translations in all 6 locales.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 12:23:01 +01:00
parent 9bc7a62f20
commit dd5965ae4c
12 changed files with 196 additions and 5 deletions

View File

@@ -95,6 +95,19 @@ class Product_Data {
<div id="composable_product_data" class="panel woocommerce_options_panel hidden">
<div class="options_group">
<?php
woocommerce_wp_select([
'id' => '_composable_include_unpublished',
'label' => __('Include Non-Public Products', 'wc-composable-product'),
'description' => __('Allow draft and private products in the selection. Useful when products should only be sold as part of a composition.', 'wc-composable-product'),
'desc_tip' => true,
'options' => [
'' => __('Use global default', 'wc-composable-product'),
'yes' => __('Yes', 'wc-composable-product'),
'no' => __('No', 'wc-composable-product'),
],
'value' => get_post_meta($post->ID, '_composable_include_unpublished', true) ?: '',
]);
woocommerce_wp_select([
'id' => '_composable_criteria_type',
'label' => __('Selection Criteria', 'wc-composable-product'),
@@ -181,6 +194,10 @@ class Product_Data {
$pricing_mode = isset($_POST['_composable_pricing_mode']) ? sanitize_text_field($_POST['_composable_pricing_mode']) : '';
update_post_meta($post_id, '_composable_pricing_mode', $pricing_mode);
// Save include unpublished
$include_unpublished = isset($_POST['_composable_include_unpublished']) ? sanitize_text_field($_POST['_composable_include_unpublished']) : '';
update_post_meta($post_id, '_composable_include_unpublished', $include_unpublished);
// Save criteria type
$criteria_type = isset($_POST['_composable_criteria_type']) ? sanitize_text_field($_POST['_composable_criteria_type']) : 'category';
update_post_meta($post_id, '_composable_criteria_type', $criteria_type);

View File

@@ -60,6 +60,13 @@ class Settings extends \WC_Settings_Page {
],
'desc_tip' => true,
],
[
'title' => __('Include Non-Public Products', 'wc-composable-product'),
'desc' => __('Allow draft and private products to appear in composable product selections. Useful when products should only be sold as part of a composition, not individually.', 'wc-composable-product'),
'id' => 'wc_composable_include_unpublished',
'type' => 'checkbox',
'default' => 'no',
],
[
'title' => __('Show Product Images', 'wc-composable-product'),
'desc' => __('Display product images in the selection interface.', 'wc-composable-product'),

View File

@@ -97,6 +97,22 @@ class Product_Type extends \WC_Product {
return true;
}
/**
* Check if non-public products should be included
*
* @return bool
*/
public function should_include_unpublished() {
$per_product = $this->get_meta('_composable_include_unpublished', true);
if ($per_product === 'yes') {
return true;
}
if ($per_product === 'no') {
return false;
}
return get_option('wc_composable_include_unpublished', 'no') === 'yes';
}
/**
* Get available products based on criteria
*
@@ -104,10 +120,11 @@ class Product_Type extends \WC_Product {
*/
public function get_available_products() {
$criteria = $this->get_selection_criteria();
$include_unpublished = $this->should_include_unpublished();
$args = [
'post_type' => 'product',
'posts_per_page' => -1,
'post_status' => 'publish',
'post_status' => $include_unpublished ? ['publish', 'draft', 'private'] : 'publish',
'orderby' => 'title',
'order' => 'ASC',
];
@@ -177,11 +194,11 @@ class Product_Type extends \WC_Product {
$variation_ids = $product->get_children();
foreach ($variation_ids as $variation_id) {
$variation = wc_get_product($variation_id);
if ($variation && $variation->is_purchasable()) {
if ($variation && ($include_unpublished || $variation->is_purchasable())) {
$products[] = $variation;
}
}
} elseif ($product->is_purchasable()) {
} elseif ($include_unpublished || $product->is_purchasable()) {
$products[] = $product;
}
}