class POSProductVisibilitySync {
/**
* Register hooks for syncing POS visibility.
*
* @since 10.5.0
*
* @return void
*/
public function register_hooks(): void {
add_action( 'woocommerce_new_product_variation', array( $this, 'inherit_parent_pos_visibility' ), 10, 2 );
}
/**
* Whether POS visibility can be managed for the given product.
*
* Only simple and variable products that are not downloadable support the
* "Available for POS" option; this is the single definition used by both the
* product data meta box UI and the save logic.
*
* @since 11.0.0
*
* @param \WC_Product $product The product to check.
* @return bool True if the product type supports POS visibility.
*/
public function is_product_supported( \WC_Product $product ): bool {
return $product->is_type( array( ProductType::SIMPLE, ProductType::VARIABLE ) ) && ! $product->is_downloadable();
}
/**
* Set POS visibility for a product and its variations.
*
* This method sets or removes the pos-hidden term on the product,
* and if it's a variable product, syncs the visibility to all variations.
*
* @since 10.5.0
*
* @param int $product_id The product ID.
* @param bool $visible_in_pos Whether the product should be visible in POS.
* @return void
*/
public function set_product_pos_visibility( int $product_id, bool $visible_in_pos ): void {
$is_currently_visible = ! has_term( 'pos-hidden', 'pos_product_visibility', $product_id );
if ( $is_currently_visible === $visible_in_pos ) {
return; // No change detected.
}
if ( $visible_in_pos ) {
wp_remove_object_terms( $product_id, 'pos-hidden', 'pos_product_visibility' );
} else {
wp_set_object_terms( $product_id, 'pos-hidden', 'pos_product_visibility' );
}
$product = wc_get_product( $product_id );
if ( $product && $product->is_type( 'variable' ) ) {
$this->sync_pos_visibility_to_variations( $product, $visible_in_pos );
}
}
/**
* Sync POS visibility to all variations of a variable product.
*
* @since 10.5.0
*
* @param \WC_Product $product The variable product.
* @param bool $visible_in_pos Whether the product should be visible in POS.
* @return void
*/
private function sync_pos_visibility_to_variations( \WC_Product $product, bool $visible_in_pos ): void {
$variation_ids = $product->get_children();
foreach ( $variation_ids as $variation_id ) {
if ( $visible_in_pos ) {
wp_remove_object_terms( $variation_id, 'pos-hidden', 'pos_product_visibility' );
} else {
wp_set_object_terms( $variation_id, 'pos-hidden', 'pos_product_visibility' );
}
// Save variation to update date_modified.
$variation = wc_get_product( $variation_id );
if ( $variation ) {
$variation->save();
}
}
}
/**
* Inherit POS visibility from parent when a new variation is created.
*
* When a new variation is created, check if the parent product has the
* pos-hidden term and apply it to the variation if so.
*
* @since 10.5.0
*
* @param int $variation_id The variation ID.
* @param \WC_Product_Variation|null $variation The variation object.
* @return void
*/
public function inherit_parent_pos_visibility( $variation_id, $variation ): void {
if ( ! $variation instanceof \WC_Product_Variation ) {
return;
}
$parent_id = $variation->get_parent_id();
if ( has_term( 'pos-hidden', 'pos_product_visibility', $parent_id ) ) {
wp_set_object_terms( $variation_id, 'pos-hidden', 'pos_product_visibility' );
}
}
}