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' );}
}
}
}
}
}
}
Related
To achieve this, i have written 2 functions one for grouped product save and another for child product save.
1 - Adding all child attributes to the grouped product on grouped product save:
add_action('woocommerce_after_product_object_save', 'nd_update_group_product_attributes_before_save_func', 9993, 2);
function nd_update_group_product_attributes_before_save_func($product, $data_store) {
// echo 'has category <pre>';var_dump($product); echo '</pre>';die;
// exit if not the target post type
if ('product' !== $product->post_type) {
return;
}
// $product_type = $product->get_type();
$product_type = $product->post_type;
if ($product->is_type('grouped')) {
$group_product_id = $product->get_id();
nd_sync_child_attribute_to_group($group_product_id, 'pa_bedrooms');
}
}
function nd_sync_child_attribute_to_group($group_product_id, $attribute_slug){
$group_product = wc_get_product($group_product_id);
$child_product_ids = $group_product->get_children();
$all_child_attributes = array();
if($child_product_ids){
foreach ($child_product_ids as $child_product_id) {
$child_product = wc_get_product($child_product_id);
$child_attributes = wc_get_product_terms( $child_product_id, $attribute_slug, array( 'fields' => 'names' ) );
$all_child_attributes = array_unique(array_merge($all_child_attributes, $child_attributes));
}
}
if ($all_child_attributes) {
$group_attributes = wc_get_product_terms( $group_product_id, $attribute_slug, array( 'fields' => 'names' ) );
if($group_attributes){
wp_remove_object_terms( $group_product_id, $group_attributes, $attribute_slug );
}
foreach ($all_child_attributes as $attr) {
wp_set_object_terms($group_product_id, $attr, $attribute_slug, true);
}
}
}
2 - Adding all child attributes to the grouped product on child product save by triggering grouped product save.
add_action('woocommerce_update_product', 'nd_update_group_product_attributes_on_child_update', 1002, 2);
function nd_update_group_product_attributes_on_child_update($product_id) {
$product = wc_get_product( $product_id );
// exit if not the target post type
if ( !$product->is_type('simple') || !has_term( 'home-design-floor-plans', 'product_cat' , $product_id )) {
return;
}
if ( $product->is_type('simple') ) {
$children_id = $product->get_id();
$group_args = array(
'post_type' => 'product',
'meta_query' => array(
array(
'key' => '_children',
'value' => 'i:' . $product->get_id() . ';',
'compare' => 'LIKE',
)
),
'fields' => 'ids' // THIS LINE FILTERS THE SELECT SQL
);
$parent_ids = get_posts( $group_args );
if($parent_ids){
foreach ($parent_ids as $parent_grouped_id){
if($parent_grouped_id){
$parent_product = wc_get_product( $parent_grouped_id );
if($parent_product){
$parent_product->save();
}
}
}
}
}
}
I have an issue with this once I add an attribute value to a grouped product, it was not showing on the admin area group product edit > attributes.
Is there anything I missed or is there any better way to achieve this?
All I want is to sync child product attributes to parent i.e. group product.
I have a custom date field, added with ACF, for each of my products at which point the product status should change to draft.
I know there are a bunch of schedular and count time plugins, but it comes with a bell and whistle I don't need. Is there a simple way to achieve this
Thank you
You can use WorsPress CRON. You can use wp_schedule_event. You have to get all products and get your ACF field to compare against today or current date then use the wp_update_post function to update posts status. try the below code.
// Schedule an action if it's not already scheduled
if ( ! wp_next_scheduled( 'check_daily_for_change_product_status' ) ) {
wp_schedule_event( time(), 'daily', 'check_daily_for_change_product_status' );
}
// Hook into that action that'll fire every three minutes
add_action( 'check_daily_for_change_product_status', 'check_daily_for_change_product_status_func' );
function check_daily_for_change_product_status_func() {
$args = array(
'post_type' => 'product',
'posts_per_page' => -1,
'post_status' => 'publish'
);
$get_products = new WP_Query( $args );
if( $get_products->have_posts() ){ while ( $get_products->have_posts() ) { $get_products->the_post();
$draft_date = strtotime( get_field('keyname', get_the_ID() ) );
$current_date = time();
if( $current_date >= $draft_date ){
$my_post = array(
'ID' => get_the_ID(),
'post_status' => 'draft',
);
wp_update_post( $my_post );
}
} wp_reset_postdata(); }
}
I have a product attribute variation which has options of -
small - $20
medium - $25
large - $30
Since all the products in the store has the same price for this variations.
How to set the price for the attribute values programmatically?
3 attribute variation buttons are show on product page when I add them as variations.
How to change the price of product when "small" option is selected programmatically instead of setting price in variation options in admin panel.
As Per our understanding to want to add products with different sizes and different prices.
There is 2 way:
1. You can add from woocommerece dashboard like this:
https://www.ostraining.com/blog/woocommerce/product-variations/
You can add programmatically like this:
function insert_product ($product_data)
{
$post = array( // Set up the basic post data to insert for our product
'post_author' => 1,
'post_content' => $product_data['description'],
'post_status' => 'publish',
'post_title' => $product_data['name'],
'post_parent' => '',
'post_type' => 'product'
);
$post_id = wp_insert_post($post); // Insert the post returning the new post id
if (!$post_id) // If there is no post id something has gone wrong so don't proceed
{
return false;
}
update_post_meta($post_id, '_sku', $product_data['sku']); // Set its SKU
update_post_meta( $post_id,'_visibility','visible'); // Set the product to visible, if not it won't show on the front end
wp_set_object_terms($post_id, $product_data['categories'], 'product_cat'); // Set up its categories
wp_set_object_terms($post_id, 'variable', 'product_type'); // Set it to a variable product type
insert_product_attributes($post_id, $product_data['available_attributes'], $product_data['variations']); // Add attributes passing the new post id, attributes & variations
insert_product_variations($post_id, $product_data['variations']); // Insert variations passing the new post id & variations
}
function insert_product_attributes ($post_id, $available_attributes, $variations)
{
foreach ($available_attributes as $attribute) // Go through each attribute
{
$values = array(); // Set up an array to store the current attributes values.
foreach ($variations as $variation) // Loop each variation in the file
{
$attribute_keys = array_keys($variation['attributes']); // Get the keys for the current variations attributes
foreach ($attribute_keys as $key) // Loop through each key
{
if ($key === $attribute) // If this attributes key is the top level attribute add the value to the $values array
{
$values[] = $variation['attributes'][$key];
}
}
}
$values = array_unique($values); // Filter out duplicate values
wp_set_object_terms($post_id, $values, 'pa_' . $attribute);
}
$product_attributes_data = array(); // Setup array to hold our product attributes data
foreach ($available_attributes as $attribute) // Loop round each attribute
{
$product_attributes_data['pa_'.$attribute] = array( // Set this attributes array to a key to using the prefix 'pa'
'name' => 'pa_'.$attribute,
'value' => '',
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '1'
);
}
update_post_meta($post_id, '_product_attributes', $product_attributes_data); // Attach the above array to the new posts meta data key '_product_attributes'
}
function insert_product_variations ($post_id, $variations)
{
foreach ($variations as $index => $variation)
{
$variation_post = array( // Setup the post data for the variation
'post_title' => 'Variation #'.$index.' of '.count($variations).' for product#'. $post_id,
'post_name' => 'product-'.$post_id.'-variation-'.$index,
'post_status' => 'publish',
'post_parent' => $post_id,
'post_type' => 'product_variation',
'guid' => home_url() . '/?product_variation=product-' . $post_id . '-variation-' . $index
);
$variation_post_id = wp_insert_post($variation_post); // Insert the variation
foreach ($variation['attributes'] as $attribute => $value) // Loop through the variations attributes
{
$attribute_term = get_term_by('name', $value, 'pa_'.$attribute); // We need to insert the slug not the name into the variation post meta
update_post_meta($variation_post_id, 'attribute_pa_'.$attribute, $attribute_term->slug);
}
update_post_meta($variation_post_id, '_price', $variation['price']);
update_post_meta($variation_post_id, '_regular_price', $variation['price']);
}
}
function insert_products ($products)
{
if (!empty($products)) // No point proceeding if there are no products
{
array_map('insert_product', $products); // Run 'insert_product' function from above for each product
}
}
$json = file_get_contents('product-data.json'); // Get json from sample file
// $products_data = json_decode($json_file, true); // Decode it into an array
echo json_last_error();
// if(get_magic_quotes_gpc()){
// $d = stripslashes($json);
// }else{
// $d = $json;
// }
$d = json_decode($d,true);
$products_data = json_decode( preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $json), true );
echo "<h1>json</h1>";
echo "<pre>";
print_r($json);
echo "</pre>";
echo "<h1>Product data</h1>";
echo "<pre>";
var_dump($products_data);
echo "</pre>";
insert_products($products_data);
Where $product_data sample:
$product_data = array(
'sku' => '123SKU',
'categories' => array('size', 'color'),
'available_attributes' => array('size', 'color'),
'variations' => array(
'size' => array(
'attributes' => array( 'XL', 'M', 'S' ),
),
'color' => array(
'attributes' => array( 'Blue', 'Red', 'Green' )
)
)
)
insert_products($products_data);
Note: You can create product-data.json and import product or you can create $product_data = (); and set on insert_products() function.
For Aditional Info:
You can also check this URL for more info https://newbedev.com/create-programmatically-a-woocommerce-product-variation-with-new-attribute-values
I'm trying to edit the cross-sell section of the cart page in WooCommerce. I want to display random products from the same category in the cross-sell section.
For example, someone adds something from the category women's fashion then it will display other products in the cross-sell section from the same category.
or if they have items from multiple categories to then just pick two at random.
Is there a way of doing this or is it a case of having to go through every product individually to select the cross-sell products?
Following code returns automatically $cross_sells IDs that belong to the product categor(y)(ies) from the products in the cart.
function filter_woocommerce_cart_crosssell_ids( $cross_sells, $cart ) {
// Initialize
$product_cats_ids = array();
$product_cats_ids_unique = array();
foreach ( $cart->get_cart() as $cart_item ) {
// Get product id
$product_id = $cart_item['product_id'];
// Get current product categories id(s) & add to array
$product_cats_ids = array_merge( $product_cats_ids, wc_get_product_term_ids( $product_id, 'product_cat' ) );
}
// Not empty
if ( !empty( $product_cats_ids ) ) {
// Removes duplicate values
$product_cats_ids_unique = array_unique( $product_cats_ids, SORT_REGULAR );
// Get product id(s) from a certain category, by category-id
$product_ids_from_cats_ids = get_posts( array(
'post_type' => 'product',
'numberposts' => -1,
'post_status' => 'publish',
'fields' => 'ids',
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'id',
'terms' => $product_cats_ids_unique,
'operator' => 'IN',
)
),
) );
// Removes duplicate values
$cross_sells = array_unique( $product_ids_from_cats_ids, SORT_REGULAR );
}
return $cross_sells;
}
add_filter( 'woocommerce_cart_crosssell_ids', 'filter_woocommerce_cart_crosssell_ids', 10, 2 );
Note: 'numberposts' => -1 means ALL, this can be adapted to your needs
The limit ('posts_per_page') can be set via the woocommerce_cross_sells_total filter hook
function filter_woocommerce_cross_sells_total( $limit ) {
// Set limit
$limit = 4;
return $limit;
}
add_filter( 'woocommerce_cross_sells_total', 'filter_woocommerce_cross_sells_total', 10, 1 );
I've been struggling a bit with this. I have one custom post type that I created called "pt_initiatives" and one that The Event Calendar created called "tribe_events". With Advanced Custom Fields, I added a relationship field to both post types which is also bi-directional. The relationship field is called "bi_initiatives-events".
I have The Events Calendar Pro with the Filter Bar add on. I am trying to create a filter that shows the pt_initiatives posts as options in a multi-checkbox filter. (There are only 9 of these posts). I have that part working so far. I am a bit lost on how to update the list of events when one of these pt_initiatives are chosen.
I've been playing with the wpdb queries with no success. I'm not sure how to tell it to look at tribe_events posts to find which has posts in common. I've been scouring the internet and haven't come across anything too helpful and The Event Calendar folks don't seem too keen on helping since this is considered "Customization". However, they offer the ability to add filters as a feature... They have a very vague page on how to do this but I personally did not find it too helpful.
I am not too familiar with writing wpdb queries, hence you can see me adding a post loop to achieve the same thing. At least, I think it's the same output as some examples I came across.
Thanks in advance!
class Tribe__Events__Filterbar__Filters__Initiative extends Tribe__Events__Filterbar__Filter {
public $type = 'checkbox';
public function get_admin_form() {
$title = $this->get_title_field();
$type = $this->get_multichoice_type_field();
return $title.$type;
}
protected function get_values() {
/** #var wpdb $wpdb */
global $wpdb;
// get initiative IDs associated with published posts
// $initiative_ids = $wpdb->get_col(
// $wpdb->prepare(
// "SELECT DISTINCT m.meta_value
// FROM {$wpdb->postmeta} m
// INNER JOIN {$wpdb->posts} p
// ON p.ID=m.post_id
// WHERE p.post_type=%s,
// AND p.post_status='publish'
// AND m.meta_key='bi_initiatives-events'
// AND m.meta_value > 0", $post_type
// )
// );
$initiative_ids = array();
// WP_Query arguments
$args = array(
'post_type' => array( 'pt_initiatives' ),
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'bi_initiatives-events',
// 'value' => '',
'compare' => 'EXISTS'
)
)
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$initiative_ids[] = get_the_ID();
}
}
wp_reset_postdata();
array_filter( $initiative_ids );
if ( empty( $initiative_ids ) ) {
return array();
}
/**
* Filter Total Initiatives in Filter Bar
* Use this with caution, this will load initiatives on the front-end, may be slow
* The base limit is 200 for safety reasons
*
*
* #parm int 200 posts per page limit
* #parm array $initiative_ids ids of initiatives attached to events
*/
$limit = apply_filters( 'tribe_events_filter_bar_pt_initiatives_limit', 200, $initiative_ids );
$initiatives = get_posts( array(
'post_type' => 'pt_initiatives',
'posts_per_page' => $limit,
'suppress_filters' => false,
'post__in' => $initiative_ids,
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC',
) );
$initiatives_array = array();
foreach ( $initiatives as $initiative => $value ) {
$initiatives_array[] = array(
'name' => $value->post_title,
'value' => $value->ID,
);
}
return $initiatives_array;
}
protected function setup_join_clause() {
add_filter( 'posts_join', array( 'Tribe__Events__Query', 'posts_join' ), 10, 2 );
global $wpdb;
$this->joinClause .= " LEFT JOIN {$wpdb->postmeta} AS initiatives_filter ON ({$wpdb->posts}.ID = initiatives_filter.post_id AND initiatives_filter.meta_key = 'bi_initiatives-events')";
}
protected function setup_where_clause() {
if ( is_array( $this->currentValue ) ) {
$initiative_ids = implode( ',', array_map( 'intval', $this->currentValue ) );
} else {
$initiative_ids = esc_attr( $this->currentValue );
}
$this->whereClause = " AND initiatives_filter.meta_value IN ($initiative_ids) ";
}
}
new Tribe__Events__Filterbar__Filters__Initiative( __( 'Initiatives', 'tribe-events-filter-view' ), 'initiatives_filter' );