I'm looking for a way to target product variations and modify their prices based on the attributes each variation has. The below code is somewhat heading in the right direction, but instead of using product attributes to target variations, I'm relying on matching the variation's current price to a variable in order to target it. It works, but it leaves a ton of room for error with how vague it is.
add_filter('woocommerce_get_price','change_price', 10, 2);
function change_price($price, $productd){
if ( $productd->product_type == 'variation' || $productd->product_type == 'variable' ) {
$basicmp3 = 25;
$vprice = get_post_meta( $productd->variation_id, '_regular_price',true);
if ($vprice == $basicmp3) {
$vprice2 = $vprice * .5;
return $vprice2;
}
else{
return $vprice;
}
}
}
UPDATE Posted a working solution below
When you run a woocommerce class in a function, there's a need to call some global.
global $woocommerce; $product;
$product_variation = new WC_Product_Variation($_POST['variation_id']);
$regular_price = $product_variation->regular_price;
In other Word, it's not because you have $product as parameter that you can use the class functions (it's just a 'flatten' object like an array can be). You need to initialize a new object of a class to use $newproductobject->regular_price().
It's possible, if your code is part of a plugin that the woocommerce class are not yet load, then you'll need to include the file containing the class WC_Product _Variation. (Don't think it's the case)
Hope it helps !
Found a working solution. It's probably a little bloated since I'm no code master, but I does it's job! Below is a basic version, but this kind of flexibility will allow for more useful functions, such as automatically bulk discounting variation prices by user role for products who share certain attributes.
add_filter('woocommerce_get_price','change_price', 10, 2);
function change_price($price, $productd){
global $post, $woocommerce;
$post_id = $variation->ID;
$args = array(
'post_type' => 'product_variation',
'post_status' => array( 'private', 'publish' ),
'numberposts' => -1,
'orderby' => 'menu_order',
'order' => 'asc',
'post_parent' => $post->ID
);
$variations = get_posts( $args );
//declare variables for later use
$demomp3 = '';
$basicmp3 = '';
$basicwav = '';
$premiummp3 = '';
$premiumwav = '';
$syncmp3 = '';
$syncwav = '';
foreach ( $variations as $variation ) {
$variation_id = absint( $variation->ID );$variable_id = $this['variation_id'];
$variation_post_status = esc_attr( $variation->post_status );
$variation_data = get_post_meta( $variation_id );
$variation_data['variation_post_id'] = $variation_id;
//find attributes
$option1 = 'pa_license-options';
$option2 = 'pa_delivery-format';
$getmeta1 = get_post_meta($variation_id , 'attribute_'.$option1, true);
$getmeta2 = get_post_meta($variation_id , 'attribute_'.$option2, true);
$attribute1 = get_term_by('slug', $getmeta1, $option1);
$attribute2 = get_term_by('slug', $getmeta2, $option2);
$license = $attribute1->name;
$format = $attribute2->name;
//get the prices of each variation by the attribute combinations they have
if ($format === "mp3" && $license === "Demo License" ){
//retrieve variation price and assign it to the previously created variable
$demomp3 = get_post_meta($variation_id, '_regular_price', true );
}
if ($format === "mp3" && $license === "Basic License" ){
$basicmp3 = get_post_meta($variation_id, '_regular_price', true );
}
if ($format === "WAV" && $license === "Basic License" ){
$basicwav = get_post_meta($variation_id, '_regular_price', true );
}
if ($format === "mp3" && $license === "Premium License" ){
$premiummp3 = get_post_meta($variation_id, '_regular_price', true );
}
if ($format === "WAV" && $license === "Premium License" ){
$premiumwav = get_post_meta($variation_id, '_regular_price', true );
}
if ($format === "mp3" && $license === "Sync License" ){
$syncmp3 = get_post_meta($variation_id, '_regular_price', true );
}
if ($format === "WAV" && $license === "Sync License" ){
$syncwav = get_post_meta($variation_id, '_regular_price', true );
}
}
//Simple product doesn't need to be modified
if($productd->product_type == 'simple'){
return $price;
}
//adjust prices of variable products
if ( $productd->product_type == 'variation' || $productd->product_type == 'variable' ) {
$regprice = get_post_meta( $productd->variation_id, '_regular_price',true);
//target the current variation by matching its price to the one stored in the variable
if ($regprice == $demomp3) {
//apply price adjustment and return the new value
$price = $regprice * .5;
return $price;
}
else if ($regprice == $basicmp3) {
$price = $regprice * .5;
return $price;
}
else if ($regprice == $basicwav) {
$price = $regprice * .5;
return $price;
}
else if ($regprice == $premiummp3) {
$price = $regprice * .5;
return $price;
}
else if ($regprice == $premiumwav) {
$price = $regprice * .5;
return $price;
}
else if ($regprice == $syncmp3) {
$price = $regprice * .5;
return $price;
}
else if ($regprice == $syncwav) {
$price = $regprice * .5;
return $price;
}
else{
return $price;
}
}
}
Use the following script to update product variation. click here for detailed explanation.
https://www.pearlbells.co.uk/bulk-update-product-variation-price-woocommerce/
function getExistingProducts($updatedPrices,$skuArray) {
$loop = new WP_Query(array('post_type' => array('product', 'product_variation'), 'posts_per_page' => -1));
while ($loop->have_posts()) : $loop->the_post();
$id = get_the_ID();
$product = wc_get_product( $id );
$sku = get_post_meta($id, '_sku', true);
if( in_array( $sku, $skuArray ) ) {
$attributes = $product->get_attributes();
$attributes['medium-quantity-price']['value'] = $updatedPrices[$sku][4];
$attributes['low-quantity-price']['value'] = $updatedPrices[$sku][3];
// $attributes['v-low-quantity-price']['value'] = $updatedPrices[$sku][2];
update_post_meta( $id,'_product_attributes',$attributes);
echo ' Update Sku : '.$sku.' '.PHP_EOL;
}
endwhile;
}
Related
I'm developing a WordPress plugin that extends the popular WooCommerce plugin. It adds a new product type "Metal" and adding custom fields that must be filled in when product type "metal" is selected. Anyway, I'm using the woocommerce_process_product_meta hook to set the new meta values:
add_action( 'woocommerce_process_product_meta', 'wm_save_fields' );
function wm_save_fields( $id ){
if(isset($_POST['product-type']) && $_POST['product-type'] == 'metal') {
$weight = $_POST['_metal_weight'];
$margin = $_POST['_metal_margin'];
if(
!isset($weight) ||
!isset($margin)
) {
return;
}
$weight = ($weight == "") ? 0 : sanitize_text_field($_POST['_metal_weight']);
$margin = ($margin == "") ? 0 : sanitize_text_field($_POST['_metal_margin']);
$gold_price = get_option('wm_gold_price');
$price = $weight * $gold_price;
$margin_price = $price + (($margin / 100) * $price);
$margin_price = number_format($margin_price, 2);
update_post_meta( $id, 'wm_weight', $weight );
update_post_meta( $id, 'wm_margin', $margin );
if(get_post_meta( $id, '_price' )) {
update_post_meta( $id, '_price', $margin_price );
} else {
add_post_meta( $id, '_price', $margin_price );
}
}
}
The problem comes only when adding a new product. It's not setting the price of the product. It sets an empty string. But on product update it's working perfectly. I tried different approaches of the update_post_meta code but nothing worked. Any ideas why is this happening? I'm struggling my head for 2 hours now.
Solution to this issue could be to use the woocommerce_before_product_object_save action, which is run before the product is saved.
Please try this :
add_action( 'woocommerce_before_product_object_save', 'wm_set_price' );
function wm_set_price( $product ){
if(isset($_POST['product-type']) && $_POST['product-type'] == 'metal') {
$weight = $_POST['_metal_weight'];
$margin = $_POST['_metal_margin'];
if(
!isset($weight) ||
!isset($margin)
) {
return;
}
$weight = ($weight == "") ? 0 : sanitize_text_field($_POST['_metal_weight']);
$margin = ($margin == "") ? 0 : sanitize_text_field($_POST['_metal_margin']);
$gold_price = get_option('wm_gold_price');
$price = $weight * $gold_price;
$margin_price = $price + (($margin / 100) * $price);
$margin_price = number_format($margin_price, 2);
update_post_meta( $product->get_id(), 'wm_weight', $weight );
update_post_meta( $product->get_id(), 'wm_margin', $margin );
$product->set_price( $margin_price );
}
}
I add two filter to add extra information in cart items with filters. When click add to cart button, product not added in cart and Not showing any errors.
add_filter('woocommerce_add_cart_item_data', array($this, 'addCartItemData'), 10, 3);
add_filter('woocommerce_get_item_data', array($this, 'addItemData'), 10, 2);
public function addCartItemData($cart_item, $product_id, $variation_id)
{
global $woocommerce;
$product_id = $variation_id > 0 ? $variation_id : $product_id;
$eventtype=get_post_meta($product_id, "WooCommerceEventsType", true);
print_r($eventtype); exit;
if($eventtype == 'sequential'){
if(isset($_POST['fooevent_class_date']) && !empty($_POST['fooevent_class_date'])) { $cart_item['fooevent_class_date'] = $_POST['fooevent_class_date']; }
if(isset($_POST['fooevent_class_timing']) && !empty($_POST['fooevent_class_timing'])) { $cart_item['fooevent_class_timing'] = $_POST['fooevent_class_timing']; }
}
return $cart_item;
}
/**
* Add item data.
*/
public function addItemData($item_data, $cart_item)
{
$product_id = $cart_item['variation_id'] > 0 ? $cart_item['variation_id'] : $cart_item['product_id'];
$eventtype=get_post_meta($product_id, "WooCommerceEventsType", true);
if($eventtype == 'sequential'){
$class_date = $cart_item['fooevent_class_date'];
$class_timing = $cart_item['fooevent_class_timing'];
$item_data['fooevent_class_date'] = array('key' => __('Class Date','fooevents-multiday-events'),'value' => $class_date);
$item_data['fooevent_class_timing'] = array('key' => __('Class Timing','fooevents-multiday-events'),'value' => $class_timing);
}
return $item_data;
}
A filter should be passed a callback. Maybe try this
add_filter('woocommerce_add_cart_item_data', 'addCartItemData', 10, 3);
function addCartItemData($cart_item, $product_id, $variation_id) {
global $woocommerce;
$product_id = $variation_id > 0 ? $variation_id : $product_id;
$eventtype = get_post_meta($product_id, "WooCommerceEventsType", true);
print_r($eventtype);
exit;
if ($eventtype == 'sequential') {
if (isset($_POST['fooevent_class_date']) && !empty($_POST['fooevent_class_date'])) {
$cart_item['fooevent_class_date'] = $_POST['fooevent_class_date'];
}
if (isset($_POST['fooevent_class_timing']) && !empty($_POST['fooevent_class_timing'])) {
$cart_item['fooevent_class_timing'] = $_POST['fooevent_class_timing'];
}
}
return $cart_item;
}
// Add item data
add_filter('woocommerce_get_item_data', 'addItemData', 10, 2);
function addItemData($item_data, $cart_item) {
$product_id = $cart_item['variation_id'] > 0 ? $cart_item['variation_id'] : $cart_item['product_id'];
$eventtype = get_post_meta($product_id, "WooCommerceEventsType", true);
if ($eventtype == 'sequential') {
$class_date = $cart_item['fooevent_class_date'];
$class_timing = $cart_item['fooevent_class_timing'];
$item_data['fooevent_class_date'] = array('key' => __('Class Date', 'fooevents-multiday-events'), 'value' => $class_date);
$item_data['fooevent_class_timing'] = array('key' => __('Class Timing', 'fooevents-multiday-events'), 'value' => $class_timing);
}
return $item_data;
}
I Hope are you okay.
Today I have problems with I open order invoice pdf in admin after products list have components.
I Remove all components as well
I am using woocommerce-pdf-invoices-packing-slips plugin.
Below is my product URL :
https://www.pcguru.lt/produktas/amd-home-guru/
My Invoice PDF screenshot as well.
How can I remove it?
I Searching Google but couldn't Found Anything.
After Code Debugging Step by Step, That's are Found Successfully.
File Path:
/wp-content/plugins/woocommerce-pdf-invoices-packing-slips/includes/documents/abstract-wcpdf-order-document-methods.php
public function get_order_items() {
$items = $this->order->get_items();
$data_list = array();
if( sizeof( $items ) > 0 ) {
foreach ( $items as $item_id => $item ) {
//Find Child Product Of Composite Product
global $wpdb;
$post_id = $wpdb->get_row("SELECT meta_value FROM pcg_woocommerce_order_itemmeta WHERE (meta_key = 'wooco_ids' AND order_item_id = '". $item_id ."')");
if($post_id->meta_value !=''){
$main_id = str_replace('/1/', '', $post_id->meta_value);
$data_ids = explode(",",$main_id);
}
//END Custom Code
$data = array();
// Set the item_id
$data['item_id'] = $item_id;
// Set the id
$data['product_id'] = $item['product_id'];
$data['variation_id'] = $item['variation_id'];
// Compatibility: WooCommerce Composit Products uses a workaround for
// setting the order before the item name filter, so we run this first
if ( class_exists('WC_Composite_Products') ) {
$order_item_class = apply_filters( 'woocommerce_order_item_class', '', $item, $this->order );
}
// Set item name
$data['name'] = apply_filters( 'woocommerce_order_item_name', $item['name'], $item, false );
// Set item quantity
$data['quantity'] = $item['qty'];
//$data['product_desc'] = $item->get_product(); // Get the WC_Product objec
//echo '<pre>'; print_r($product); echo '</pre>'; die;
// Set the line total (=after discount)
$data['line_total'] = $this->format_price( $item['line_total'] );
$data['single_line_total'] = $this->format_price( $item['line_total'] / max( 1, abs( $item['qty'] ) ) );
$data['line_tax'] = $this->format_price( $item['line_tax'] );
$data['single_line_tax'] = $this->format_price( $item['line_tax'] / max( 1, abs( $item['qty'] ) ) );
$data['tax_rates'] = $this->get_tax_rate( $item, $this->order, false );
$data['calculated_tax_rates'] = $this->get_tax_rate( $item, $this->order, true );
// Set the line subtotal (=before discount)
$data['line_subtotal'] = $this->format_price( $item['line_subtotal'] );
$data['line_subtotal_tax'] = $this->format_price( $item['line_subtotal_tax'] );
$data['ex_price'] = $this->get_formatted_item_price( $item, 'total', 'excl' );
$data['price'] = $this->get_formatted_item_price( $item, 'total' );
$data['order_price'] = $this->order->get_formatted_line_subtotal( $item ); // formatted according to WC settings
// Calculate the single price with the same rules as the formatted line subtotal (!)
// = before discount
$data['ex_single_price'] = $this->get_formatted_item_price( $item, 'single', 'excl' );
$data['single_price'] = $this->get_formatted_item_price( $item, 'single' );
// Pass complete item array
$data['item'] = $item;
// Get the product to add more info
if ( is_callable( array( $item, 'get_product' ) ) ) {
$product = $item->get_product();
} else {
$product = $this->order->get_product_from_item( $item );
}
// Checking fo existance, thanks to MDesigner0
if( !empty( $product ) ) {
// Thumbnail (full img tag)
$data['thumbnail'] = $this->get_thumbnail( $product );
$data['get_parent_id'] = is_callable( array( $product, 'get_parent_id' ) ) ? $product->get_parent_id() : '';
// Set item SKU
$data['sku'] = is_callable( array( $product, 'get_sku' ) ) ? $product->get_sku() : '';
// Set item weight
$data['weight'] = is_callable( array( $product, 'get_weight' ) ) ? $product->get_weight() : '';
// Set item dimensions
$data['dimensions'] = $product instanceof \WC_Product ? WCX_Product::get_dimensions( $product ) : '';
// Pass complete product object
$data['product'] = $product;
} else {
$data['product'] = null;
}
// Set item meta
if (function_exists('wc_display_item_meta')) { // WC3.0+
$data['meta'] = wc_display_item_meta( $item, array(
'echo' => false,
) );
} else {
if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '<' ) ) {
$meta = new \WC_Order_Item_Meta( $item['item_meta'], $product );
} else { // pass complete item for WC2.4+
$meta = new \WC_Order_Item_Meta( $item, $product );
}
$data['meta'] = $meta->display( false, true );
}
if (!in_array($data['product_id'], $data_ids)) {
$data_list[$item_id] = apply_filters( 'wpo_wcpdf_order_item_data', $data, $this->order, $this->get_type());
}
}
}
//echo '<pre>'; print_r($data_list); echo '</pre>'; die;
return apply_filters( 'wpo_wcpdf_order_items_data', $data_list, $this->order, $this->get_type() );
}
Solution Screenshot:
I am very happy because solution are completed.
Also Thanks to all helping guys
I am trying to create product attribute on user input, but whenever i send the request the attribute gets created but it overrides the previous attributes linked to the product.
public static function create_variable_product(){
$variant_data = wp_clean(isset($_POST['data']) ? wp_unslash($_POST['data']) : '');
$product_id = wp_clean(isset($_POST['product_id']) ? wp_unslash($_POST['product_id']) : '');
$variant_data = json_decode($variant_data);
$product = wc_get_product($product_id);
if($product->has_child() == false){
$product = new WC_Product_Variable($product_id);
}
$attribute = self::create_product_attribute($product,$variant_data);
wp_send_json($attribute);
}
public static function create_product_attribute($product,$variant_data){
$id = [];
for($i=0; $i<count($variant_data); $i++){
$attribute = new WC_Product_Attribute();
$attribute->set_id(0);
foreach($variant_data[$i] as $key => $value){
if($key == 'attribute_name'){
$attribute->set_name($value);
}
if($key == 'options'){
$attribute->set_options($value);
}
}
$attribute->set_position( 1 );
$attribute->set_visible( 1 );
$attribute->set_variation( 1 );
$attribute->is_taxonomy(0);
$product->set_attributes(array($attribute));
array_push($id,$product->save());
}
return $id;
}
The data being passed to $_POST['data'] is:
[{
attribute_name: 'Color',
options: ['red','yellow','blue']
}]
There are some mistakes in your code. Try the following instead (untested):
public static function create_variable_product(){
$data = wp_clean(isset($_POST['data'] ? wp_unslash($_POST['data']) : '');
$product_id = wp_clean(isset($_POST['product_id']) ? wp_clean( wp_unslash($_POST['product_id']) ) : '');
$product = wc_get_product($product_id);
if( is_a($product, 'WC_Product') && ! $product->is_type('variable') && isset($_POST['data']) ){
$product = new WC_Product_Variable($product_id);
}
$attribute = self::create_product_attribute($product, $data);
$product->save()
wp_send_json($attribute);
}
public static function create_product_attribute( $product, $data ) {
$attributes = $product->get_attributes();
$attribute = new WC_Product_Attribute();
foreach($variant_data[$i] as $key => $values){
if( $key === 'attribute_name'){
$attribute->set_name($values);
} elseif( $key === 'options' ){
$attribute->set_options($values);
}
}
$attribute->set_position( 1 );
$attribute->set_visible( 1 );
$attribute->set_variation( 1 );
$attribute->is_taxonomy(0);
$attributes[$attribute->get_name()] = $attribute;
$product->set_attributes($attributes);
}
It should better work.
I have code which prints text after the price when on sale and I am now getting an error using this code, which I did not get before. It says: "PHP Warning: date() expects parameter 2 to be integer" and it refers to this line:
$sales_price_date_to = date( "j.M.Y", $sales_price_to );
Any ideas on how to fix it?
add_filter( 'woocommerce_get_price_html', 'custom_price_html', 100, 2 );
function custom_price_html( $price, $product ){
global $post;
$sales_price_to = get_post_meta( $product->id, '_sale_price_dates_to', true );
$sales_price_date_to = date( "j.M.Y", $sales_price_to );
if ( is_single() ) {
if($product->is_type( 'simple' ) && $sales_price_to != "" ) {
$sales_price_date_to = date("j.m.Y", $sales_price_to);
return str_replace( '</ins>', ' </ins> <span class="notice-price">(on offer until '.$sales_price_date_to.')</span>', $price );
} else if ( $product->is_type( 'variable' ) ){
$handle = new WC_Product_Variable( $product->id);
$variations1 = $handle->get_children();
$content_variations = "";
$count_each_var = 0;
foreach ($variations1 as $value) {
$count_each_var++;
$single_variation = new WC_Product_Variation($value);
$var_id = $single_variation->variation_id;
$sales_price_to_variable = get_post_meta($var_id, '_sale_price_dates_to', true);
if( $sales_price_to_variable != '' ){
$sales_price_date_to = date("j.m.Y", $sales_price_to_variable);
if($count_each_var == 1) {
$class_val = 'class="active"';
} else {
$class_val = "";
}
$content_variations .= "<i data-id='". $var_id ."' data-order='".$count_each_var."' $class_val >".$sales_price_date_to."</i>";
} else {
$content_variations .= "";
}
}
if( $content_variations != ''){
return str_replace( '</ins>', ' </ins> <span class="notice-price">(on offer until '.$content_variations.')</span>', $price );
} else {
return $price;
}
} else {
return apply_filters( 'woocommerce_get_price', $price );
}
}
}
Been trying to fix it, but I can't.
Your code is really outdated ans more since Woocommerce 3 as a lot of things have changed.
I have completely revisited your code making it up to date for Woocommerce 3+ versions:
add_filter( 'woocommerce_get_price_html', 'custom_price_html', 100, 2 );
function custom_price_html( $price, $product ){
if ( is_product() ) {
// Simple products and variations
if( $product->is_type( 'simple' ) || $product->is_type( 'variation' ) )
{
$sales_price_to = $product->get_date_on_sale_to();
if( ! empty($sales_price_to) ){
$replacement = ' </ins> <span class="notice-price">(on offer until ';
return str_replace( '</ins>', $replacement . date( 'j.M.Y', $sales_price_to->getTimestamp() ) . ')</span>', $price );
}
}
// Variable products
else if ( $product->is_type( 'variable' ) )
{
$content = '';
// Loop through variations
foreach ( $product->get_children() as $key => $variation_id ) {
$variation = wc_get_product($variation_id);
$sales_price_to = $variation->get_date_on_sale_to();
if( ! empty($sales_price_to) ){
$date_to = date( 'j.M.Y', $sales_price_to->getTimestamp() );
$class = $key == 0 ? 'class="active"' : '';
$content .= '<i data-id="'.$variation_id.'" data-order="'.($key + 1).'" '.$class.'>'. $date_to .'</i>';
}
}
if( ! empty($content) ){
return $price . ' <span class="notice-price">(on offer until ' . $content .')</span>';
}
}
}
return $price;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.