WooCommerce get_name into ACF Repeater Field - woocommerce

I am facing the following challenge right now, if someone could help met out I would really appreciate it.
My goal is to save WooCommerce order data in an ACF Repeater field. The code I use is as followed:
function create_post_after_order( $order_id ) {
if ( $order_id instanceof WC_Order ){
return;
}
$order = wc_get_order( $order_id );
$order_items = $order->get_items();
foreach ( $order_items as $item_id => $item_data ) {
$product_name.= $item_data->get_name();
$product_id.= $item_data->get_product_id();
$product_quantity.= $item_data->get_quantity();
}
$new_post = array(
'post_title' => "Order {$order_id}",
'post_date' => date('Y-m-d H:i:s'),
'post_author' => $user_ID,
'post_type' => 'groeiproces',
'post_status' => 'publish',
);
$post_id = wp_insert_post($new_post);
// repeater field with one subfield
$classs_field_key = 'field_61645b866cbd6';
$classs_subfield_key = 'field_61645b916cbd7';
$classs_items = array($product_name, $product_name);
foreach ($classs_items as $classs_items_value) {
$classs_value[] = array(
$classs_subfield_key => $classs_items_value);
update_field($classs_field_key, $classs_value, $post_id);
}
}
add_action( 'woocommerce_thankyou', 'create_post_after_order', 10, 1 );
The problem is that this combines the order data en saves it to the repeater fields.
Current
Want I want is that every productname gets saved in a new repeater field.
Ideal
Can anyone point me in the right direction? Thanks in advance.
With the help of Naveen I was able to upgrade my code to the following:
//Creƫr post met orderdetails na bestelling
function create_post_after_order( $order_id ) {
if ( $order_id instanceof WC_Order ){
return;
}
//Zoek de order ID's met bijbehorende items
$order = wc_get_order( $order_id );
$order_items = $order->get_items();
//Loop door alle orders voor de onderstaande orderdata
foreach ( $order_items as $item_id => $item_data ) {
$product_ids[] = $item_data->get_product_id(); // ID's van de producten
$product_names[] = $item_data->get_name(); // Namen van de producten
$product_quantities[] = $item_data->get_quantity(); // Aantal van de producten
$product_prices[] = $product->get_price(); //Prijs per product
$ordeline_subtotals[] = $item_data->get_subtotal(); //Subtotaal van de orderregels
}
//Maak een nieuwe post aan
$new_post = array(
'post_title' => "Order {$order_id}", // Definieer titel van de post
'post_date' => date('Y-m-d H:i:s'), // Voeg publicatiedatum toe
'post_author' => $user_ID, // Definieer de klant als auteur
'post_type' => 'groeiproces', // Definieer CPT
'post_status' => 'publish', // Publiceer post
);
$post_id = wp_insert_post($new_post);
//Verbind de ACF velden
$orderdetails_key = 'field_61645b866cbd6';
$product_id_key = 'field_6166a67234fa3';
$product_name_key = 'field_61645b916cbd7';
$product_quantity_key = 'field_6165bd2101987';
$product_price_key = 'field_6166a68134fa4';
$ordeline_subtotal_key = 'field_6166a68934fa5';
$product_id = $product_ids;
$product_name = $product_names;
$product_quantity = $product_quantities;
$product_price = $product_prices;
$ordeline_subtotal = $ordeline_subtotals;
//Save de orderdata in het ACF repeater field
foreach ($product_id as $index => $product_id) {
$orderdetails_value[] = array(
$product_id_key => $product_id,
$product_name_key => $product_name[$index],
$product_quantity_key => $product_quantity[$index],
$product_price_key => $product_price[$index],
$ordeline_subtotal_key => $ordeline_subtotal[$index],
);
update_field( $orderdetails_key, $orderdetails_value, $post_id );
}
}
add_action( 'woocommerce_thankyou', 'create_post_after_order', 10, 1 );
This gives me the following situation which is great.
current situation
The only thing I am still looking for is the product price from the order. I am not totally sure if it is able to get this, as I only found the active price option online. Maybe it would be possible to just divide the $ordeline_subtotals by the $product_quantities.
Anyone who does know how to set this up?
Thanks in advance!

I find two issues in your code:
You are concatenating $product_name instead of putting them in an array.
You are updating the field inside a for loop instead of outside the loop.
These two issues are fixed in the below code... take note of $product_names as array and then we loop this array at the end to create values for your repeater field.
function create_post_after_order( $order_id ) {
if ( $order_id instanceof WC_Order ){
return;
}
$order = wc_get_order( $order_id );
$order_items = $order->get_items();
foreach ( $order_items as $item_id => $item_data ) {
$product_names[] = $item_data->get_name();
$product_ids[] = $item_data->get_product_id();
$product_quantities[] = $item_data->get_quantity();
}
$new_post = array(
'post_title' => "Order {$order_id}",
'post_date' => date('Y-m-d H:i:s'),
'post_author' => $user_ID,
'post_type' => 'groeiproces',
'post_status' => 'publish',
);
$post_id = wp_insert_post($new_post);
// repeater field with one subfield
$classs_field_key = 'field_61645b866cbd6';
$classs_subfield_key = 'field_61645b916cbd7';
$classs_items = $product_names;
foreach ($classs_items as $classs_items_value) {
$classs_value[] = array(
$classs_subfield_key => $classs_items_value);
}
update_field($classs_field_key, $classs_value, $post_id);
}
add_action( 'woocommerce_thankyou', 'create_post_after_order', 10, 1 );
Pls note that I have slightly altered the array variable names (example $product_name is now $product_names with 's' in it and same applies to other variables).
EDIT (answering to additional question asked by the OP):
To get the price of the product, we first need to get an instance of the corresponding product object from the order. This is done as follows inside the foreach loop:
$product_details = $item_data->get_product();
// Get the product price that customer paid
$product_price = $product_details->get_price();
//Get sale price (i.e discounted price, if it exists)
$product_regular_price = $product_details->get_sale_price();
//Regular price.
$product_sale_price = $product_details->get_regular_price();
Assuming you may need just the product price only, then your foreach loop may look like this:
foreach ( $order_items as $item_id => $item_data ) {
$product_ids[] = $item_data->get_product_id(); // ID's van de producten
$product_names[] = $item_data->get_name(); // Namen van de producten
$product_quantities[] = $item_data->get_quantity(); // Aantal van de producten
$product_details = $item_data->get_product();
// Get the product price that customer paid
$product_prices[] = $product_details->get_price();
$ordeline_subtotals[] = $item_data->get_subtotal(); //Subtotaal van de orderregels
}
You can then use the $product_price in your repeater field's foreach loop.

Related

Change the status of orders automatically

I need to change the status of my orders to (Cancelled) automatically after 7 days if it is not processed.
I need to change the status from: New Request to Cancelled.
Or
I need to change the status from: Processing to Cancelled.
Thanks
Adnan
I did that with the help of this code:
function autocancel_wc_orders(){
$query = ( array(
'limit' => 10,
'orderby' => 'date',
'order' => 'DESC',
'status' => array( 'wc-pending', 'wc-ywraq-new', 'wc-ywraq-
pending')
) );
$orders = wc_get_orders( $query );
foreach( $orders as $order ){
$date = new DateTime( $order->get_date_created() );
$today = new DateTime();
$interval = $date->diff($today);
$datediff = $interval->format('%a');
if( $datediff > 2 ){
$order->update_status('cancelled', 'Cancelled for missing
payment');
}
}
}
add_action( 'admin_init', 'autocancel_wc_orders' );
I found this answer online in the link:
https://samuelsilva.pt/cancel-woocommerce-not-paid-orders-automatically/
Helpful documentation - https://developer.wordpress.org/reference/classes/wp_query/#date-parameters
https://woocommerce.wp-a2z.org/oik_api/wc_get_order_statuses/
//My server cronjob is targeting wp-cron.php
function ss_cancel_failed_orders() {
$held_duration = 15; //minutes how often my cron job to run per day/hour
$data_store = WC_Data_Store::load('order');
//Change post_status for desired order statuses
//Change date_query before value to desired time compare with modified post date
$unpaid_orders = get_posts(array('posts_per_page' => -1, 'post_type' => 'shop_order', 'post_status' => array('wc-failed','wc-on-hold'), 'date_query' => array(array('before' => '15 minutes ago', 'column'=>'post_modified' ))));
if ( $unpaid_orders ) {
foreach ( $unpaid_orders as $unpaid_order ) {
$order = wc_get_order( $unpaid_order );
$order->update_status( 'cancelled', __( 'Order has expired.', 'woocommerce' ) ); // Status here is without wc- prefix
}
}
wp_clear_scheduled_hook( 'ssa_cancel_failed_orders' );
wp_schedule_single_event( time() + ( absint( $held_duration ) * 60 ), 'ssa_cancel_failed_orders' ); // you can remove wp cron and work with server cron only
}
add_action('ssa_cancel_failed_orders', 'ss_cancel_failed_orders');

WooCommerce apply coupon depends of cart line item quantity

After long looking, I was not able to find any proper code how would be possible to apply coupon for a cart line items. Lets say customer added some product quantity of 10, my selected coupon should be applied for that product. If he adds another product with quantity more than 10, again same coupon should to be applied for that product.
Any assistance here?
I was able to find something similar but this only works for specific products id, any assistance how to update this code to go through each cart products ,check their quantities and apply coupon for products which quantity is 10 or more?
Reference for similar code but only for specific products:
Conditionally apply coupons automatically for specific Product IDs and quantities
Image example:
tried to create a custom solution for my question below. And did it in some way, not sure if this is proper and good option, but it at least work for me as exactly I need. This creates a separate coupon for every product in a shop (if newly product added it creates an unique coupon for it as well). Coupons is applied automatically per cart line item, if product quantity is 10 or more in a cart. It gives a 10% discount for that product. Code as per below, maybe for someone will be useful as I couldn't find any plugins or codes to work like this anywhere...
$args = array(
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'asc',
'post_type' => 'shop_coupon',
'post_status' => 'publish',
);
$all_coupons = get_posts( $args );
// Loop through the available coupons
foreach ( $all_coupons as $coupon ) {
// Get the name for each coupon and add to the previously created array
$coupon_name = $coupon->post_title;
}
foreach ($all_coupons as $coupon) {
$coupons_array[] = $coupon->post_title;
}
$all_ids = get_posts( array(
'post_type' => 'product',
'numberposts' => -1,
'post_status' => 'publish',
'fields' => 'ids',
) );
foreach ( $all_ids as $id ) {
$product_id_array[] = $id;
}
// Get values from arr2 and arr1 unique
$output = array_merge(array_diff($coupons_array, $product_id_array), array_diff($product_id_array, $coupons_array));
function coupon_exists($coupon_code) {
global $wpdb;
$sql = $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_type = 'shop_coupon' AND post_name = '%s'", $coupon_code );
$coupon_codes = $wpdb->get_results($sql);
if (count($coupon_codes)> 0) {
return true;
}
else {
return false;
}
}
foreach ($output as $o) {
if (is_numeric($o)) {
if (!coupon_exists($o)) {
generate_coupon($o);
}
}
}
function generate_coupon($coupon_code){
$coupon = new WC_Coupon();
$coupon->set_code($coupon_code);
//the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
$coupon->set_discount_type('percent_product');
//the discount amount, defaults to zero
$coupon->set_amount(10);
$coupon->set_individual_use(false);
$coupon->set_product_ids(array($coupon_code));
//save the coupon
$coupon->save();
return $coupon_code;
}
add_action( 'woocommerce_before_cart', 'conditional_auto_add_coupons' );
function conditional_auto_add_coupons() {
$all_ids = get_posts( array(
'post_type' => 'product',
'numberposts' => -1,
'post_status' => 'publish',
'fields' => 'ids',
) );
if ( !WC()->cart->is_empty() ){
// First cart loop: Counting number of subactegory items in cart
foreach ( $all_ids as $id ){
foreach ( WC()->cart->get_cart() as $cart_item ){
if( $id == $cart_item['data']->id ){
if( 10 <= $cart_item['quantity'] ){
WC()->cart->add_discount( $id );
//wc_add_notice( __( 'Discount of <strong>10%</strong> for quantity.', 'theme_domain' ), 'success' );
}else{
WC()->cart->remove_coupon( $id );
//wc_add_notice( __( 'Discount of <strong>10%</strong> due to low quantity removed.', 'theme_domain' ), 'success' );}
}
}
}
}
}
}

Query to get Attributes based on Product Category selected in WooCommerce?

I need to implement custom functionality like "Product filter by Attributes" widget works in woocommerce.
For example in Product category page:
In Parent Category like Clothing, it loads all the attributes filter (pa_color , pa_size).
Now, when you check sub-category of that parent category i.e., Hoodies. It gets filtered and loads only related attributes (pa_color).
Please suggest the query to achieve this requirement.
This is how I am getting the data as per my requirement :
$filter_raw = array();
$attrs_raw = wc_get_attribute_taxonomy_names(); // Getting data of attributes assign in backend.
$cat_name = get_term($request['category'], 'product_cat', ARRAY_A ); //Category data by category ID.
$args = array(
'category' => array($cat_name['slug'] )
);
foreach( wc_get_products($args) as $product ){
foreach( $product->get_attributes() as $attr_name => $attr ){
$filter_raw[] = $attr_name;
if(is_array($attr->get_terms())){
foreach( $attr->get_terms() as $term ){
$terms_raw[] = $term->name;
}
}
}
}
$filters = array_unique(array_intersect((array)$filter_raw,(array)$attrs_raw)); //Filtering the attributes used by products in particular category
if(is_array($filters)){
foreach ( $filters as $filter ){
$terms = get_terms( $filter );
if ( ! empty( $terms ) ) {
$return['items'][ $filter ] = array(
'id' => $filter,
'type' => 'checkbox',
'label' => $this->decode_html( wc_attribute_label( $filter ) ),
);
foreach ( $terms as $term ) {
if(in_array($term->name,$terms_raw)){ //Filtering the terms from attribute used by the products in a category and showing required result.
$return['items'][ $filter ]['values'][] = array(
'label' => $this->decode_html( $term->name ),
'value' => $term->slug,
);
}
}
}
}
}
print_r($return);

Hide " Add to cart " button for 1 day after ordering a product, for that product which user has ordered

I have added a new custom field in checkout page named " Date of event ", It's working fine. But i want one thing to be done which is " When user order single/multiple products then hide "Add to cart" button and show unavailable message instead of button for that selected date of event. " Like if user selected date " 7/2/2019 " in " Date of event field " during checkout, then after he ordered that product, hide " Add to cart " button and show unavailable message instead of button for " 7/2/2019 " date of event. I don't know how to do this.
Which hooks and actions will do this. I have googled it a lot, but didn't get any answer.
Please help me.
Custom field Code:
add_action('woocommerce_after_checkout_billing_form', 'date_of_event_field');
function date_of_event_field($checkout){
echo '<div id="date_of_event_field" class="margin-top-20">';
woocommerce_form_field( 'date_of_event', array(
'type' => 'date',
'class' => array('my-field-class form-row-wide'),
'label' => __('Date Of Event'),
'required' => true,
), $checkout->get_value( 'date_of_event' ));
echo '</div>';
}
Code to hide Add to cart button and show message instead of button:
function make_product_unavailable( $_product, $order ) {
if( $order->id == $_product ){
remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart');
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart');
}
}
It's a try from my side, because i don't know how to do to this and i don't know which filter/action hook will be used for this.
Please help me.
Source : https://wisdmlabs.com/blog/the-right-way-to-hide-add-to-cart-button-in-woocommerce/
Check if a customer has purchased a specific products in WooCommerce
Note : The below code is not tested but it will work if some part is modified as per your requirement..
add_filter( 'woocommerce_is_purchasable', 'hidecart',10,1);
function hidecart(){
$found = has_bought_items();
if(!empty($found["order_date"])){
$current_date = date("d/m/Y");
if($found["order_date"] == $current_date && $found["product_id"] == get_the_ID()){
return false;
}
}
}
function has_bought_items()
{
$bought = false;
$order_date = '';
//get product id if single or for shop page u will have to retrieve in some other way
$product_id = get_the_ID();
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed' // Only orders with status "completed"
) );
foreach ( $customer_orders as $customer_order ) {
// Updated compatibility with WooCommerce 3+
$order_id = method_exists( $order, 'get_id' ) ? $order->get_id() : $order->id;
$order = wc_get_order( $customer_order );
// Iterating through each current customer products bought in the order
foreach ($order->get_items() as $item) {
// WC 3+ compatibility
if ( version_compare( WC_VERSION, '3.0', '<' ) )
$order_product_id = $item['product_id'];
else
$order_product_id = $item->get_product_id();
// Your condition related to your 2 specific products Ids
if ( $product_id == $product_id) ) {
$bought = true;
$order_date = $order->order_date;
// you can fetch your event date stored as order meta
$arr = array("product_id"=>$product_id,"order_date"=>$order_date,"bought"=>$bought);
return $arr;
}
}
}
$arr = array("product_id"=>"","order_date"=>$order_date,"bought"=>$bought);
return $order_date;
}

Woocommerce sort cart products by product category

The Problem
I would like to make it so my Woocommerce cart shows products in order of product category. (My products are assigned to a brand, and i want the products to appear in the cart area under their assigned brands.)
What I have tried
At the moment i've been able to get it to sort alphabetical by key however this is as far as my knowledge with arrays goes.
Example Code
add_action( 'woocommerce_cart_loaded_from_session', function() {
global $woocommerce;
$products_in_cart = array();
foreach ( $woocommerce->cart->cart_contents as $key => $item ) {
$products_in_cart[ $key ] = $item['data']->get_title();
}
ksort( $products_in_cart );
$cart_contents = array();
foreach ( $products_in_cart as $cart_key => $product_title ) {
$cart_contents[ $cart_key ] = $woocommerce->cart->cart_contents[ $cart_key ];
}
$woocommerce->cart->cart_contents = $cart_contents;
}, 100 );
Additional Notes
I know I can use this code to get the term ID of each product. But i'm not quite sure how best to structure my code to get the outcome I'm after.
$terms = wp_get_post_terms(get_the_ID(), 'product_cat' );
You've got all the right pieces.
To get the post terms in this context you need to tweak how you are getting the ID of the cart item
$terms = wp_get_post_terms($item['data']->id, 'product_cat' );
The result of getting the post terms will give you an array that looks like this
Array(
[0] => stdClass Object(
[term_id] => 877
[name] => Product Category Name
[slug] => Product Category Name
[term_group] => 0
[term_taxonomy_id] => 714
[taxonomy] => product_cat
[description] =>
[parent] => 0
[count] => 1
[filter] => raw
)
)
The code below will sort the cart by the first category in the array. This is not complete, you will need to account for no product category being set and also multiple product categories being set.
add_action( 'woocommerce_cart_loaded_from_session', function() {
global $woocommerce;
$products_in_cart = array();
foreach ( $woocommerce->cart->cart_contents as $key => $item ) {
$terms = wp_get_post_terms($item['data']->id, 'product_cat' );
$products_in_cart[ $key ] = $terms[0]->name;
}
ksort( $products_in_cart );
$cart_contents = array();
foreach ( $products_in_cart as $cart_key => $product_title ) {
$cart_contents[ $cart_key ] = $woocommerce->cart->cart_contents[ $cart_key ];
}
$woocommerce->cart->cart_contents = $cart_contents;
}, 100 );

Resources