purchase_price_allowed = TRUE;
// Add the purchase price to WC products.
add_action( 'woocommerce_product_options_pricing', array( $this, 'add_purchase_price_field' ) );
add_action( 'woocommerce_variation_options_pricing', array( $this, 'add_purchase_price_field' ), 10, 3 );
}
/**
* Filters the Product data tabs settings to add ATUM settings
*
* @since 1.4.1
*
* @param array $data_tabs
*
* @return array
*/
public function add_product_data_tab( $data_tabs ) {
// Add the ATUM tab to Simple and BOM products.
$bom_tab = (array) apply_filters( 'atum/product_data/tab', array(
'atum' => array(
'label' => __( 'ATUM Inventory', ATUM_TEXT_DOMAIN ),
'target' => 'atum_product_data',
'class' => array_merge( array( 'show_if_simple', 'show_if_variable' ), Helpers::get_option_group_hidden_classes() ),
'priority' => 21,
),
) );
// Insert the ATUM tab under Inventory tab.
$data_tabs = array_merge( array_slice( $data_tabs, 0, 2 ), $bom_tab, array_slice( $data_tabs, 2 ) );
return $data_tabs;
}
/**
* Add the fields to ATUM Inventory tab within WC's Product Data meta box
*
* @since 1.4.1
*/
public function add_product_data_tab_panel() {
$product_id = get_the_ID();
$product = Helpers::get_atum_product( $product_id );
$product_status = get_post_status( $product_id );
$checkbox_wrapper_classes = (array) apply_filters( 'atum/product_data/atum_switch/classes', [ 'show_if_simple' ] );
$control_button_classes = (array) apply_filters( 'atum/product_data/control_button/classes', [ 'show_if_variable' ] );
Helpers::load_view( 'meta-boxes/product-data/atum-tab-panel', compact( 'product', 'product_status', 'checkbox_wrapper_classes', 'control_button_classes' ) );
}
/**
* Add the Product Levels meta boxes to the Product variations
*
* @since 0.0.3
*
* @param int $loop The current item in the loop of variations.
* @param array $variation_data The current variation data.
* @param \WP_Post $variation The variation post.
*/
public function add_product_variation_data_panel( $loop, $variation_data, $variation ) {
// Get the variation product.
$variation = Helpers::get_atum_product( $variation->ID );
Helpers::load_view( 'meta-boxes/product-data/atum-variation-panel', compact( 'loop', 'variation_data', 'variation' ) );
}
/**
* Save all the fields within the Product Data's ATUM Inventory tab
*
* @since 1.4.1
*/
private function save_product_data_panel() {
$tab_data_key = $this->is_variation ? 'variation_atum_tab' : 'atum_product_tab';
$atum_tab_values = isset( $_POST[ $tab_data_key ] ) ? $_POST[ $tab_data_key ] : array();
$product_tab_fields = Globals::get_product_tab_fields();
$is_inheritable_product = ! $this->is_variation && Helpers::is_inheritable_type( esc_attr( $_POST['product-type'] ) );
$this->product_data['inheritable'] = $is_inheritable_product ? 'yes' : 'no';
foreach ( $product_tab_fields as $field_name => $field_type ) {
// The ATUM's stock control must be always 'yes' for inheritable products.
if ( Globals::ATUM_CONTROL_STOCK_KEY === $field_name && $is_inheritable_product ) {
$this->product_data['atum_controlled'] = 'yes';
continue;
}
if ( $this->is_variation ) {
$field_value = isset( $atum_tab_values[ $field_name ][ $this->loop ] ) ? $atum_tab_values[ $field_name ][ $this->loop ] : NULL;
}
else {
$field_value = isset( $atum_tab_values[ $field_name ] ) ? $atum_tab_values[ $field_name ] : NULL;
}
// Sanitize the fields.
switch ( $field_type ) {
case 'checkbox':
$field_value = 'yes' === $field_value ? 'yes' : 'no';
break;
case 'number_int':
$field_value = absint( $field_value );
break;
case 'number_float':
$field_value = floatval( $field_value );
break;
case 'text':
default:
$field_value = wc_clean( $field_value );
break;
}
// The ATUM control key doesn't match to the new table column name.
if ( Globals::ATUM_CONTROL_STOCK_KEY === $field_name ) {
$field_name = 'atum_controlled';
}
$this->product_data[ $field_name ] = $field_value;
}
}
/**
* Add the individual out stock threshold field to WC's WC's product data meta box
*
* @since 1.4.10
*
* @param int $loop Only for variations. The loop item number.
* @param array $variation_data Only for variations. The variation item data.
* @param \WP_Post $variation Only for variations. The variation product.
*/
public function add_out_stock_threshold_field( $loop = NULL, $variation_data = array(), $variation = NULL ) {
global $post;
$meta_key = Globals::OUT_STOCK_THRESHOLD_KEY;
$woocommerce_notify_no_stock_amount = get_option( 'woocommerce_notify_no_stock_amount' );
$product_id = empty( $variation ) ? $post->ID : $variation->ID;
$product = Helpers::get_atum_product( $product_id );
/* @noinspection PhpUndefinedMethodInspection */
$out_stock_threshold = $product->get_out_stock_threshold();
$product_type = empty( $variation ) ? $product->get_type() : '';
$out_stock_threshold_field_name = empty( $variation ) ? $meta_key : "variation{$meta_key}[$loop]";
$out_stock_threshold_field_id = empty( $variation ) ? $meta_key : $meta_key . $loop;
// If the user is not allowed to edit "Out of stock threshold", add a hidden input.
if ( ! AtumCapabilities::current_user_can( 'edit_out_stock_threshold' ) ) : ?>
product->get_type(), Globals::get_product_types_with_stock(), TRUE ) ) {
return;
}
$out_stock_threshold = $this->is_variation ? $_POST[ 'variation' . Globals::OUT_STOCK_THRESHOLD_KEY ][ $this->loop ] : $_POST[ Globals::OUT_STOCK_THRESHOLD_KEY ];
// Force product validate and save to rebuild stock_status (probably _out_stock_threshold has been disabled for this product).
if ( $out_stock_threshold ) {
Helpers::force_rebuild_stock_status( $this->product );
}
$this->product_data['out_stock_threshold'] = $out_stock_threshold;
}
/**
* Add the purchase price field to WC's product data meta box
*
* @since 1.2.0
*
* @param int $loop Only for variations. The loop item number.
* @param array $variation_data Only for variations. The variation item data.
* @param \WP_Post $variation Only for variations. The variation product.
*/
public function add_purchase_price_field( $loop = NULL, $variation_data = array(), $variation = NULL ) {
if ( ! current_user_can( ATUM_PREFIX . 'edit_purchase_price' ) ) {
return;
}
$field_title = __( 'Purchase price', ATUM_TEXT_DOMAIN ) . ' (' . get_woocommerce_currency_symbol() . ')';
if ( empty( $variation ) ) {
$product_id = get_the_ID();
$wrapper_class = '_purchase_price_field ' . Helpers::get_option_group_hidden_classes( FALSE );
$field_id = $field_name = Globals::PURCHASE_PRICE_KEY;
}
else {
$product_id = $variation->ID;
$field_name = "variation_purchase_price[$loop]";
$field_id = "variation_purchase_price{$loop}";
$wrapper_class = "$field_name form-row form-row-first";
}
$product = Helpers::get_atum_product( $product_id );
/* @noinspection PhpUndefinedMethodInspection */
$purchase_price = $product->get_purchase_price();
$field_value = '' === $purchase_price ? '' : (float) $purchase_price;
$price = (float) $product->get_price();
Helpers::load_view( 'meta-boxes/product-data/purchase-price-field', compact( 'wrapper_class', 'field_title', 'field_name', 'field_id', 'field_value', 'price', 'variation', 'loop' ) );
}
/**
* Save the purchase price meta on product post savings
*
* @since 1.2.0
*
* @return array
*/
private function save_purchase_price() {
$product_type = empty( $_POST['product-type'] ) ? 'simple' : sanitize_title( stripslashes( $_POST['product-type'] ) );
/* @noinspection PhpUndefinedMethodInspection */
$old_purchase_price = $this->product->get_purchase_price();
$new_purchase_price = $old_purchase_price ?: '';
// Variables, grouped and variations.
if ( Helpers::is_inheritable_type( $product_type ) && 'bundle' !== $product_type ) {
if ( $this->is_variation && isset( $_POST[ 'variation' . Globals::PURCHASE_PRICE_KEY ][ $this->loop ] ) ) {
$purchase_price = wc_clean( $_POST[ 'variation' . Globals::PURCHASE_PRICE_KEY ][ $this->loop ] );
$new_purchase_price = '' === $purchase_price ? '' : $purchase_price;
}
}
// Rest of product types (Bypass if "_puchase_price" meta is not coming).
elseif ( ! $this->is_variation && isset( $_POST[ Globals::PURCHASE_PRICE_KEY ] ) ) {
$purchase_price = wc_clean( $_POST[ Globals::PURCHASE_PRICE_KEY ] );
$new_purchase_price = '' === $purchase_price ? '' : $purchase_price;
}
$this->product_data['purchase_price'] = $new_purchase_price;
return array(
'new_purchase_price' => $new_purchase_price,
'old_purchase_price' => $old_purchase_price,
);
}
/**
* Adds the Supplier fields in WC's product data meta box
*
* @since 1.3.0
*
* @param int $loop Only for variations. The loop item number.
* @param array $variation_data Only for variations. The variation item data.
* @param \WP_Post $variation Only for variations. The variation product.
*/
public function add_product_supplier_fields( $loop = NULL, $variation_data = array(), $variation = NULL ) {
global $post;
$product_id = empty( $variation ) ? $post->ID : $variation->ID;
$product = Helpers::get_atum_product( $product_id );
if ( empty( $variation ) ) {
// Do not add the field to variable products (every variation will have its own).
if ( in_array( $product->get_type(), array_diff( Globals::get_inheritable_product_types(), [ 'grouped', 'bundle' ] ) ) ) {
return;
}
}
// Save the meta keys on a variable (some sites were experiencing weird issues when accessing to these constants directly).
$supplier_meta = Suppliers::SUPPLIER_META_KEY;
$supplier_sku_meta = Suppliers::SUPPLIER_SKU_META_KEY;
/* @noinspection PhpUndefinedMethodInspection */
$supplier_id = $product->get_supplier_id();
/* @noinspection PhpUndefinedMethodInspection */
$supplier_sku = $product->get_supplier_sku();
$supplier = $supplier_id ? get_post( $supplier_id ) : NULL;
$supplier_field_name = empty( $variation ) ? $supplier_meta : "variation{$supplier_meta}[$loop]";
$supplier_field_id = empty( $variation ) ? $supplier_meta : $supplier_meta . $loop;
$supplier_sku_field_name = empty( $variation ) ? $supplier_sku_meta : "variation{$supplier_sku_meta}[$loop]";
$supplier_sku_field_id = empty( $variation ) ? $supplier_sku_meta : $supplier_sku_meta . $loop;
// If the user is not allowed to edit Suppliers, add a hidden input.
if ( ! AtumCapabilities::current_user_can( 'edit_supplier' ) ) : ?>
product->get_type(), array_diff( Globals::get_inheritable_product_types(), [ 'grouped', 'bundle' ] ) ) ) {
return;
}
if ( $this->is_variation ) {
$this->product_data['supplier_id'] = isset( $_POST[ 'variation' . Suppliers::SUPPLIER_META_KEY ][ $this->loop ] ) ? $_POST[ 'variation' . Suppliers::SUPPLIER_META_KEY ][ $this->loop ] : NULL;
$this->product_data['supplier_sku'] = isset( $_POST[ 'variation' . Suppliers::SUPPLIER_SKU_META_KEY ][ $this->loop ] ) ? $_POST[ 'variation' . Suppliers::SUPPLIER_SKU_META_KEY ][ $this->loop ] : NULL;
}
else {
$this->product_data['supplier_id'] = isset( $_POST[ Suppliers::SUPPLIER_META_KEY ] ) ? $_POST[ Suppliers::SUPPLIER_META_KEY ] : NULL;
$this->product_data['supplier_sku'] = isset( $_POST[ Suppliers::SUPPLIER_SKU_META_KEY ] ) ? $_POST[ Suppliers::SUPPLIER_SKU_META_KEY ] : NULL;
}
}
/**
* Save extra product data.
*
* @since 1.5.8
*/
private function save_extra_data() {
// If it's out of stock, how many days?
$this->product_data['out_stock_days'] = Helpers::get_product_out_stock_days( $this->product );
// Has location terms assigned?
$location_terms = wp_get_post_terms( $this->product->get_id(), Globals::PRODUCT_LOCATION_TAXONOMY );
$this->product_data['has_location'] = ! empty( $location_terms );
}
/**
* Hook callback after saving a product
*
* @since 1.5.0
*
* @param int $product_id The saved product's ID.
* @param \WP_Post $post The saved post.
*/
public function save_product_meta_boxes( $product_id, $post ) {
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! isset( $_POST['product-type'] ) ) {
return;
}
// Disable cache to avoid saving the wrong data.
$was_cache_disabled = AtumCache::is_cache_disabled();
if ( ! $was_cache_disabled ) {
AtumCache::disable_cache();
}
do_action( 'atum/product_data/before_save_product_meta_boxes', $product_id, $post );
$this->product = Helpers::get_atum_product( $product_id );
$this->is_variation = FALSE;
$this->loop = NULL;
$this->save_atum_meta_boxes();
do_action( 'atum/product_data/after_save_product_meta_boxes', $product_id, $post );
if ( ! $was_cache_disabled ) {
AtumCache::enable_cache();
}
}
/**
* Hook callback after saving a variation product
*
* @since 1.5.0
*
* @param int $variation_id
* @param int $loop
*/
public function save_product_variation_meta_boxes( $variation_id, $loop ) {
// Disable cache to avoid saving the wrong data.
$was_cache_disabled = AtumCache::is_cache_disabled();
if ( ! $was_cache_disabled ) {
AtumCache::disable_cache();
}
do_action( 'atum/product_data/before_save_product_variation_meta_boxes', $variation_id, $loop );
$this->product = Helpers::get_atum_product( $variation_id );
$this->product->set_object_read( TRUE );
$this->is_variation = TRUE;
$this->loop = $loop;
$this->save_atum_meta_boxes();
do_action( 'atum/product_data/after_save_product_variation_meta_boxes', $variation_id, $loop );
if ( ! $was_cache_disabled ) {
AtumCache::enable_cache();
}
}
/**
* Save all the collected data for the ATUM meta boxes at once
*
* @since 1.5.0
*/
public function save_atum_meta_boxes() {
$this->save_product_data_panel();
if ( 'yes' === Helpers::get_option( 'out_stock_threshold', 'no' ) ) {
$this->save_out_stock_threshold_field();
}
if ( AtumCapabilities::current_user_can( 'read_supplier' ) ) {
$this->save_product_supplier_fields();
}
if ( $this->purchase_price_allowed ) {
$purchase_price = $this->save_purchase_price();
}
// Save extra data (out of stock date, has_location, etc).
$this->save_extra_data();
$this->product_data = (array) apply_filters( 'atum/product_data/data_to_save', $this->product_data );
if ( ! empty( $this->product_data ) ) {
$errors = $this->product->set_props( $this->product_data );
if ( is_wp_error( $errors ) ) {
\WC_Admin_Meta_Boxes::add_error( $errors->get_error_message() );
}
/* @noinspection PhpUndefinedMethodInspection */
$this->product->save_atum_data();
if ( isset( $purchase_price ) && $purchase_price['new_purchase_price'] !== $purchase_price['old_purchase_price'] ) {
do_action( 'atum/product_data/after_save_purchase_price', $this->product->get_id(), $purchase_price['new_purchase_price'], $purchase_price['old_purchase_price'] );
}
do_action( 'atum/product_data/after_save_data', $this->product_data, $this->product );
}
}
/********************
* Instance methods
********************/
/**
* Cannot be cloned
*/
public function __clone() {
_doing_it_wrong( __FUNCTION__, esc_attr__( 'Cheatin’ huh?', ATUM_TEXT_DOMAIN ), '1.0.0' );
}
/**
* Cannot be serialized
*/
public function __sleep() {
_doing_it_wrong( __FUNCTION__, esc_attr__( 'Cheatin’ huh?', ATUM_TEXT_DOMAIN ), '1.0.0' );
}
/**
* Get Singleton instance
*
* @return ProductDataMetaBoxes instance
*/
public static function get_instance() {
if ( ! ( self::$instance && is_a( self::$instance, __CLASS__ ) ) ) {
self::$instance = new self();
}
return self::$instance;
}
}