I am not that proficient at coding so please bear with me.
I have this custom add to cart code:
public static function add_to_cart( $params, $uid ) {
$data = self::get_calc_data($uid);
foreach ($data as $calc_id => $calc_data) {
$product_id = isset($calc_data['woo_info']['product_id']) ? $calc_data['woo_info']['product_id'] : null;
if ( $product_id && intval($product_id) === intval($params['woo_info']['product_id']) ) {
$meta = [
'product_id' => $product_id,
'item_name' => isset($calc_data['item_name']) ? $calc_data['item_name'] : '',
];
if(!empty($calc_data['descriptions']) && is_array($calc_data['descriptions'])) {
foreach ($calc_data['descriptions'] as $calc_item) {
if ( $calc_item['hidden'] === true ){
continue;
}
if ( strpos($calc_item['alias'], 'datePicker_field_id_') !== false ) {
$val = $calc_item['converted'] ? ' ('. $calc_item['converted'] . ') ' . $calc_item['value'] : $calc_item['value'];
}else{
$labels = isset($calc_item['extra']) ? $calc_item['extra']: '';
if ( (strpos($calc_item['alias'], 'radio_field_id_') !== false
|| strpos($calc_item['alias'], 'dropDown_field_id_') !== false)
&& key_exists('options', $calc_item) ) {
$labels = CCBWooCheckout::getLabels($calc_item['options']);
}
if ( strpos($calc_item['alias'], 'multi_range_field_id_') !== false
&& key_exists('options', $calc_item) && count($calc_item['options']) > 0 ) {
$labels = key_exists('label', $calc_item['options'][0]) ?
$calc_item['options'][0]['label']: '';
}
$val = isset($labels) ? $labels . ' ' . $calc_item['converted'] : $calc_item['converted'];
}
$meta['calc_data'][$calc_item['label']] = $val;
}
}
/** add totals data */
if( !empty($calc_data['ccb_total_and_label']) && is_array($calc_data['ccb_total_and_label']) ) {
$meta['ccb_total'] = $calc_data['ccb_total_and_label']['total'];
}
WC()->cart->add_to_cart($product_id, 1, '', array(), array('ccb_calculator' => $meta));
}
}
}
It adds simple item from a plugin into Woocommerce Cart. I am trying to add a unique ID for every added item so that I can add multiples of the same item and they would show as unique items in the cart.
I was trying to create the unique ID with
public static function add_custom_cart_item_data( $cart_item_data, $cart_item_key ) {
$cart_item_data[custom_data]['unique_key'] = md5( microtime().rand() );
return $cart_item_data;
}
and then use the $cart_item_data; changing the 'product_id' => $product_id, to 'product_id' => $cart_item_data, but sadly this doesn't seem to work. What am I doing wrong?
Really appreciate all your help!
Related
I want to exclude WooCommerce revenue from the Google Analytics purchase event when the Product ID is 224112 or 159324.
I tried to find solutions to exclude sending the data to GA4 through GMT exclusion triggers or filters directly in the GA4 interface, but it seems the best way to accomplish this is to exclude the data from arriving into the datalayer in the first place.
I use the GTM4WP plugin to send the website data (WordPress, WooCommerce) to the GTM datalayer.
In the plugin, I found the part of the code that sends the purchase data to the datalayer. I'm not very familiar with this code and I dont want to mess things up on my site. Since the Google Analytics give the results only after 24 hours I want to make sure that I do the right thing right away.
This is the original plugin code:
function gtm4wp_get_purchase_datalayer( $order, $order_items ) {
global $gtm4wp_options, $gtm4wp_is_woocommerce3_7;
$dataLayer = array();
if ( $order instanceof WC_Order ) {
$woo = WC();
// variable for Google Smart Shopping campaign new customer reporting
// https://support.google.com/google-ads/answer/9917012?hl=en-AU#zippy=%2Cinstall-with-google-tag-manager
if ( $woo->customer instanceof WC_Customer ) {
// we need to use this instead of $woo->customer as this will load proper total order number and value from the database instead of the session
$woo_customer = new WC_Customer( $woo->customer->get_id() );
$dataLayer['new_customer'] = $woo_customer->get_order_count() === 1;
}
if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEXCLUDETAX ] ) {
$order_revenue = (float)( $order->get_total() - $order->get_total_tax() );
} else {
$order_revenue = (float) $order->get_total();
}
$order_shipping_cost = (float) $order->get_shipping_total();
if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCEXCLUDESHIPPING ] ) {
$order_revenue -= $order_shipping_cost;
}
$order_currency = $order->get_currency();
if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) {
$dataLayer['event'] = 'gtm4wp.orderCompleted';
$dataLayer['transactionId'] = $order->get_order_number();
$dataLayer['transactionAffiliation'] = '';
$dataLayer['transactionTotal'] = $order_revenue;
$dataLayer['transactionShipping'] = $order_shipping_cost;
$dataLayer['transactionTax'] = (float) $order->get_total_tax();
$dataLayer['transactionCurrency'] = $order_currency;
}
if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
$dataLayer['event'] = 'gtm4wp.orderCompletedEEC';
$dataLayer['ecommerce'] = array(
'currencyCode' => $order_currency,
'purchase' => array(
'actionField' => array(
'id' => $order->get_order_number(),
'affiliation' => '',
'revenue' => $order_revenue,
'tax' => (float) $order->get_total_tax(),
'shipping' => (float)( $order->get_shipping_total() ),
'coupon' => implode( ', ', ( $gtm4wp_is_woocommerce3_7 ? $order->get_coupon_codes() : $order->get_used_coupons() ) ),
)
)
);
}
if ( isset( $order_items ) ) {
$_order_items = $order_items;
} else {
$_order_items = gtm4wp_process_order_items( $order );
}
if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC ] ) {
$dataLayer['transactionProducts'] = $_order_items['products'];
}
if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
$dataLayer['ecommerce']['purchase']['products'] = $_order_items['products'];
}
if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
$dataLayer['ecomm_prodid'] = $_order_items['product_ids'];
$dataLayer['ecomm_pagetype'] = 'purchase';
$dataLayer['ecomm_totalvalue'] = (float) $_order_items['sumprice'];
}
}
return $dataLayer;
}
I think I could add an if statement just before the excludeTAX option. It would be something like this:
If ($productID = array (224112, 159324)) {
$order_revenue = 0;
} else {
$order_revenue = (float) $order->get_total();
}
I don't know what would be the correct variable for $productID and I am not sure that this would work.
Any help is appreciated.
$order_product_ids = array();
foreach ($order->get_items() as $item_key => $item) {
$product = $item->get_product();
$order_product_ids[] = $product->get_id();
}
$checklist_product_ids = array(224112, 159324);
$is_product_in_order = (count(array_intersect($order_product_ids, $checklist_product_ids))) ? true : false;
If ($is_product_in_order) {
$order_revenue = 0;
} else {
$order_revenue = (float) $order->get_total();
}
I have a different kind of scenario then the typical custom fields (I suppose). I am not getting custom values (fields) from user in the form rather I have an implementation which adds:
ColorName
Size
City
These are from a custom product flow which adds custom attributes to the cart, here is how I am doing that:
add_action('wp_ajax_wdm_add_user_custom_data_options', 'wdm_add_user_custom_data_options_callback');
add_action('wp_ajax_nopriv_wdm_add_user_custom_data_options', 'wdm_add_user_custom_data_options_callback');
function wdm_add_user_custom_data_options_callback()
{
// print_r($_POST);
$productIDM = $_POST['product_id'];
// case swith
switch ($productIDM) {
case "Ace Dura Silk":
$productID = 3254;
break;
case "Ace Matt Finish":
$productID = 3232;
break;
case "Ace Plastic Emulsion":
$productID = 3276;
break;
case "Ace Weather Defender":
$productID = 2991;
break;
case "Overall Plasticoat":
$productID = 3112;
break;
}
$colorname = $_POST['colorname'];
$cityname = $_POST['cityname'];
$size = $_POST['size'];
$price = $_POST['price'];
global $woocommerce;
$woocommerce->cart->add_to_cart( $productID, 1 );
// die();
// echo 'I am in...';
$result = array(
'status' => true
);
echo json_encode($result);
}
add_filter('woocommerce_add_cart_item_data','wdm_add_item_data',1,10);
function wdm_add_item_data($cart_item_data, $product_id) {
global $woocommerce;
$new_value = array();
$new_value['_custom_options'] = $_POST['custom_options'];
if(empty($cart_item_data)) {
return $new_value;
} else {
return array_merge($cart_item_data, $new_value);
}
}
add_filter('woocommerce_get_cart_item_from_session', 'wdm_get_cart_items_from_session', 1, 3 );
function wdm_get_cart_items_from_session($item,$values,$key) {
if (array_key_exists( '_custom_options', $values ) ) {
$item['_custom_options'] = $values['_custom_options'];
}
return $item;
}
add_filter('woocommerce_cart_item_name','add_usr_custom_session',1,3);
function add_usr_custom_session($product_name, $values, $cart_item_key ) {
$return_string = $product_name . "<div class='cart_subitems_custom'><br /><small>".$values['_custom_options']['colorname']."</small><br /><small>".$values['_custom_options']['cityname']."</small><br /><small>".$values['_custom_options']['size']."</small></div>" ; //. "<br />" . print_r($values['_custom_options']);
return $return_string;
}
add_action('woocommerce_add_order_item_meta','wdm_add_values_to_order_item_meta',1,2);
function wdm_add_values_to_order_item_meta($item_id, $values) {
global $woocommerce,$wpdb;
wc_add_order_item_meta($item_id,'_colorname',$values['_custom_options']['colorname']);
wc_add_order_item_meta($item_id,'_cityname',$values['_custom_options']['cityname']);
wc_add_order_item_meta($item_id,'_size',$values['_custom_options']['size']);
}
add_action( 'woocommerce_before_calculate_totals', 'update_custom_price', 1, 1 );
function update_custom_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $cart_item_key => $value ) {
// Version 2.x
//$value['data']->price = $value['_custom_options']['custom_price'];
// Version 3.x / 4.x
if($value['_custom_options']['price'] == null){
echo"";
}else{
$value['data']->set_price($value['_custom_options']['price']);
}
}
}
I am getting these custom values almost everywhere except Email Notification.
Here is what normal product order edit shows:
Here is how I am getting the custom product in order edit page:
I have tried all the solution I can possibly find (filter & action hooks) but nothing works for me.
I have tried first answer from this:
add_action( 'woocommerce_checkout_create_order_line_item', 'custom_checkout_create_order_line_item', 20, 4 );
function custom_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
if( isset( $values['colorname'] ) )
$item->add_meta_data( __('DCM Shade'), $values['_colorname'] );
}
Also the common method I found everywhere:
function custom_woocommerce_email_order_meta_fields( $fields, $sent_to_admin, $order ) {
// Get meta
$color = $order->get_meta( 'colorname', true );
// NOT empty
if( ! empty( $color ) ) {
$fields['colorname'] = array(
'label' => __( 'Shade' ),
'value' => $color,
);
}
// Get (other) meta
$shipping_email = $order->get_meta( '_cityname', true );
// NOT empty
if ( ! empty( $shipping_email ) ) {
$fields['_cityname'] = array(
'label' => __( 'City' ),
'value' => $shipping_email,
);
}
return $fields;
}
add_filter( 'woocommerce_email_order_meta_fields', 'custom_woocommerce_email_order_meta_fields', 10, 3 );
But I can't get the custom fields.
What am I doing wrong can please anyone please guide me.
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 added a custom <select> field to each product in cart page to be able to update variation, but when I change it's value and click "Update Cart", nothing updates unless quantity field has also been changed.
Is there a way to avoid this?
My code:
functions.php file:
add_filter( 'woocommerce_update_cart_action_cart_updated', 'on_action_cart_updated', 20, 1 );
function on_action_cart_updated( $cart_updated ){
if ($cart_updated) {
$cart_content = WC()->cart->get_cart_contents();
$update_cart = false;
$cart_totals = isset( $_POST['cart'] ) ? wp_unslash( $_POST['cart'] ) : '';
if ( ! empty( $cart_content ) && is_array( $cart_totals ) ) {
foreach ($cart_content as $key => $item) {
$lease_period = $cart_totals[$key]['lease'];
if ( ! empty( $lease_period )) {
$cart_content[$key]['variation']['attribute_pa_lease-period'] = $lease_period;
$update_cart = true;
}
}
if ($update_cart) {
WC()->cart->set_cart_contents($cart_content);
}
}
}
}
cart.php file:
<td class="product-lease-period" data-title="<?php esc_attr_e( 'Lease Period', 'woocommerce' ); ?>">
<div class="product-lease-period-select">
<select name="cart[<?php echo $cart_item_key ?>][lease]">
<?php
$lease_periods = ['6-months'=> '6 Months',
'12-months' => '12 Months',
'18-months' => '18 Months',
'24-months' => '24 Months'];
foreach ($lease_periods as $key => $period) {
$selected = '';
if ($cart_item['variation']['attribute_pa_lease-period'] == $key) {
$selected = 'selected="selected"';
}
echo "<option value=" . $key . " $selected>" . $period . "</option>";
}
?>
</select>
</div>
</td>
My conclusions so far:
I believe it's because of these pieces of code inside the class-wc-form-handler.php :
// Skip product if no updated quantity was posted.
if ( ! isset( $cart_totals[ $cart_item_key ] ) || ! isset( $cart_totals[ $cart_item_key ]['qty'] ) ) {
continue;
}
and a bit below:
if ( '' === $quantity || $quantity === $values['quantity'] ) {
continue;
}
I extended the WC_Form_Handler class in my functions.php file, copied a method I needed and edited it, and gave it higher hook priority in my extended class than it's in the original class:
class WC_Form_Handler_Ext extends WC_Form_Handler {
/**
* Hook in method.
*/
public static function init() {
add_action( 'wp_loaded', array( __CLASS__, 'update_cart_action' ), 30 );
}
/**
* Remove from cart/update.
*/
public static function update_cart_action() {
// method content edited
}
}
WC_Form_Handler_Ext::init();
UPDATE:
To make price change after variation value in cart is updated, this function needs to be added to the functions.php
function find_matching_product_variation_id($product_id, $attributes)
{
return (new WC_Product_Data_Store_CPT())->find_matching_product_variation(
new WC_Product($product_id),
$attributes
);
}
And it should be called from the loop in functions.php I mentioned in my question this way:
$attributes = $cart_content[$key]['variation'];
$variation_id = find_matching_product_variation_id($product_id, $attributes);
$price = get_post_meta($variation_id, '_price', true);
$item['data']->set_price($price);
I'm using the code below to modify the WooCommerce Is_Purchasable option so that, item Y is purchasable if item X is added to the cart.
But it gives ajax error when trying to add item Y to the cart.
Here's the code:
function aelia_get_cart_contents() {
$cart_contents = array();
/**
* Load the cart object. This defaults to the persistant cart if null.
*/
$cart = WC()->session->get( 'cart', null );
if ( is_null( $cart ) && ( $saved_cart = get_user_meta( get_current_user_id(), '_woocommerce_persistent_cart', true ) ) ) {
$cart = $saved_cart['cart'];
} elseif ( is_null( $cart ) ) {
$cart = array();
}
if ( is_array( $cart ) ) {
foreach ( $cart as $key => $values ) {
$_product = wc_get_product( $values['variation_id'] ? $values['variation_id'] : $values['product_id'] );
if ( ! empty( $_product ) && $_product->exists() && $values['quantity'] > 0 ) {
if ( $_product->is_purchasable() ) {
// Put session data into array. Run through filter so other plugins can load their own session data
$session_data = array_merge( $values, array( 'data' => $_product ) );
$cart_contents[ $key ] = apply_filters( 'woocommerce_get_cart_item_from_session', $session_data, $values, $key );
}
}
}
}
return $cart_contents;
}
// Step 1 - Keep track of cart contents
add_action('wp_loaded', function() {
// If there is no session, then we don't have a cart and we should not take
// any action
if(!is_object(WC()->session)) {
return;
}
// Product Y
global $y_cart_items;
$y_cart_items = 2986;
//Product X
global $x_cart_items;
$x_cart_items = array(
'297'
);
// Step 2
add_filter('woocommerce_is_purchasable', function($is_purchasable, $product) {
global $y_cart_items;
global $x_cart_items;
if( $product->id == $y_cart_items ) {
// make it false
$is_purchasable = false;
// get the cart items object
foreach ( aelia_get_cart_contents() as $key => $item ) {
// do your condition
if( in_array( $item['product_id'], $x_cart_items ) ) {
// Eligible product found on the cart
$is_purchasable = true;
break;
}
}
}
return $is_purchasable;
}, 10, 2);
}, 10);
// Step 3 - Explain customers why they can't add some products to the cart
add_filter('woocommerce_get_price_html', function($price_html, $product) {
if(!$product->is_purchasable() && is_product()) {
$price_html .= '<p>' . __('Add Product X to be able to purchase Product Y.', 'woocommerce') . '</p>';
}
return $price_html;
}, 10, 2);
How do I fix this? Thank you.