Get product total sales based on order status in WooCommerce - sql

I use product meta total_sales to display the total sales of a product, on the single product page in WooCommerce:
add_action( 'woocommerce_single_product_summary', 'wc_product_sold_count', 11 );
function wc_product_sold_count() {
global $product;
$units_sold = get_post_meta( $product->get_id(), 'total_sales', true );
echo '<p>' . sprintf( __( 'Units Sold: %s', 'woocommerce' ), $units_sold ) . '</p>';
}
But when some orders are canceled, the total sales are not automatically decreased. So, how can I get the total sales based on order status in WooCommerce?

To obtain the total sales, based on order statuses, you will have to use a custom function:
function get_product_total_sales_by_order_status( $product_id, $order_statuses ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare("
SELECT SUM( opl.product_qty )
FROM {$wpdb->prefix}wc_order_product_lookup as opl
INNER JOIN {$wpdb->prefix}posts as p
ON opl.order_id = p.ID
AND p.post_status IN ( 'wc-" . implode( "', 'wc-", $order_statuses ) . "' )
AND opl.product_id = %d
", $product_id ) );
}
Which can then be used as:
// Set the product ID
$p_id = 30;
// Set the orders statuses (without wc-)
$statuses = array( 'on-hold', 'processing', 'completed' );
// Call function, display result
echo get_product_total_sales_by_order_status( $p_id, $statuses );
Or in your specific case:
function action_woocommerce_single_product_summary() {
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Set the orders statuses (without wc-)
$statuses = array( 'completed' );
// Call function
$total_sales = get_product_total_sales_by_order_status( $product->get_id(), $statuses );
// Display result
if ( $total_sales >= 1 ) {
echo sprintf( __( 'Total sales: %d', 'woocommerce' ), $total_sales );
} else {
echo __( 'N/A', 'woocommerce' );
}
}
}
add_action( 'woocommerce_single_product_summary', 'action_woocommerce_single_product_summary', 11 );

Related

Display In stock available variations in WooCommerce single product

I have variable products with many variations where only a few items are actually In Stock while the majority of other variations are ''available on backorder''
I would like to be able to display a quick list of ONLY the items that are IN STOCK in the short product description of each product page so the customer doesn't have to try all variations one-by-one to finally find out which ones are in stock.
add_filter( 'woocommerce_short_description', 'display_in_stock_variations_to_short_description' );
function display_in_stock_variations_to_short_description( $excerpt ){
global $product;
if ( ! is_product() || empty($product) || ! is_a( $product, 'WC_Product' ) )
return $excerpt;
if( $product->is_type('variable') ) {
// Loop through visible children
foreach( $product->get_children() as $variation_id ) {
$variation = wc_get_product( $variation_id );
// Hide out of stock variations if 'Hide out of stock items from the catalog' is checked.
if ( ! $variation || ! $variation->exists() || ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $variation->is_in_stock() ) ) {
continue;
}
// Filter 'woocommerce_hide_invisible_variations' to optionally hide invisible variations (disabled variations and variations with empty price).
if ( apply_filters( 'woocommerce_hide_invisible_variations', true, $product->get_id(), $variation ) && ! $variation->variation_is_visible() ) {
continue;
}
$max_qty = 0 < $variation->get_max_purchase_quantity() ? $variation->get_max_purchase_quantity() : $variation->get_stock_quantity();
$term_names = []; // Initializing
// Loop through variation attributes for current varation
foreach ( $variation->get_variation_attributes() as $attribute => $term_slug ) {
// Set the term name in an array
$term_names[] = ucfirst( str_replace( ['-', '_'],[' ', ' '], $term_slug ) );
}
if ( $max_qty > 0 ) {
$excerpt .= sprintf( '<br/>%s: %s %s',
implode(', ', $term_names),
$max_qty,
__('in stock', 'woocommerce')
);
}
}
}
return $excerpt;
}
// Avoid additional content from product short description to be displayed in variation description
add_filter( 'woocommerce_available_variation', 'filter_wc_available_variation_desscription', 10, 3);
function filter_wc_available_variation_desscription( $data, $product, $variation ) {
$max_qty = 0 < $variation->get_max_purchase_quantity() ? $variation->get_max_purchase_quantity() : $variation->get_stock_quantity();
if( $max_qty > 0 )
$data['variation_description'] = get_post_meta( $variation->get_id(), '_variation_description', true );
return $data;
}
This code displays an inventory, but my website language is Persian and the names of the variables are defined in Persian, and this line does not display the variable name code correctly.
How can I fix his language?
And I want to translate the word instock into موجود
And the next point is that I want a button to be displayed on the page first with the title Show product inventory and when the method clicks, the customer will open a pop-up and display the inventory.
Here is the soultion with Persian language:
change
// Loop through variation attributes for current varation
foreach ( $variation->get_variation_attributes() as $attribute => $term_slug ) {
// Set the term name in an array
$term_names[] = ucfirst( str_replace( ['-', '_'],[' ', ' '], $term_slug ) );
}
if ( $max_qty > 0 ) {
$excerpt .= sprintf( '<br/>%s: %s %s',
implode(', ', $term_names),
$max_qty,
__('in stock', 'woocommerce')
);
}
to
// Loop through variation attributes for current varation
foreach ( $variation->get_variation_attributes() as $attribute => $term_name ) {
// Set the term name in an array
$taxonomy = str_replace('attribute_', '', $attribute);
$attribute_name = wc_attribute_label($taxonomy);
$term_name = get_term_by( 'slug', $term_name, $taxonomy)->name;
$term_names[] = $term_name;
}
if ( $max_qty > 0 ) {
$excerpt .= sprintf( '<br/>%s: %s %s',
implode(', ', $term_names),
$max_qty,
__('موجود')
);
}

Gravityform - update ACF Repeater subfield mixed with dynamic pre-populate fields

I have an ACF repeater field that create Courses with a certain amount of place available. This repeater is on each single custom-post-type courses.
In the other hand, I have a Gravityform that have a radio field who is auto-populated with those ACF repeater:
add_filter( 'gform_pre_render_3', 'populate_posts' );
add_filter( 'gform_pre_validation_3', 'populate_posts' );
add_filter( 'gform_pre_submission_filter_3', 'populate_posts' );
add_filter( 'gform_admin_pre_render_3', 'populate_posts' );
function populate_posts( $form ) {
foreach ( $form['fields'] as &$field ) {
// Vient check tous les fields du formulaire
if ( $field->type != 'radio' || strpos( $field->cssClass, 'populate-posts' ) === false ) {
// Choisit seulement les fields de type [radio] qui ont la class [populate-posts]
continue;
}
$date_type = get_field( 'date_type' );
$range_dates = get_field( 'date_range_repeater' );
$sinlge_dates = get_field( 'sinlge_dates_repeater' );
$choices = array();
if( $date_type === 'date_type_range' ){
foreach ( $range_dates as $range_date ) {
$dateformatstring = "j F Y";
$dateclean_start = strtotime( $range_date['date_starting'] );
$final_date_start = date_i18n($dateformatstring, $dateclean_start);
$dateclean_end = strtotime( $range_date['date_ending'] );
$final_date_end = date_i18n($dateformatstring, $dateclean_end);
$choices[] = array(
'text' => 'Du '.$final_date_start.' au '.$final_date_end.'<br />'.$range_date['availability'].' places disponibles',
'value' => 'Du '.$final_date_start.' au '.$final_date_end
);
}
}elseif( $date_type === 'date_type_single' ){
foreach ( $sinlge_dates as $sinlge_date ) {
$dateformatstring = "j F Y";
$dateclean_start = strtotime( $sinlge_date['date_starting'] );
$final_date_start = date_i18n($dateformatstring, $dateclean_start);
$dateclean_end = strtotime( $sinlge_date['date_ending'] );
$final_date_end = date_i18n($dateformatstring, $dateclean_end);
$choices[] = array(
'text' => 'Du '.$final_date_start.' au '.$final_date_end.'<br />'.$sinlge_date['availability'].' places disponibles',
'value' => 'Du '.$final_date_start.' au '.$final_date_end
);
}
}else{}
// update 'Select a Post' to whatever you'd like the instructive option to be
$field->placeholder = 'Select a Post';
$field->choices = $choices;
}
return $form;
}
I want that after each submission the available place number decrease from 1. I have figured out how to manage this with this ACF function:
https://www.advancedcustomfields.com/resources/update_sub_field/
This is my code for this:
add_action( 'gform_after_submission', 'set_post_content', 10, 2 );
function set_post_content( $entry, $form ) {
//$radio_value = rgar( $entry, '11' );
$repeater = 'date_range_repeater';
$acf_repeater = get_field('date_range_repeater' ); // get all the rows
$row = 0;
$specific_row = $acf_repeater[$row];
$sub_field = 'availability';
$current_availability = $specific_row['availability'];
$availability_new = --$current_availability;
update_sub_field( array($repeater, ++$row, $sub_field), $availability_new );
//echo $radio_value;
}
The thing is I have to select manually which row must be used to update de subfield. How can I manage to detect in which ACF repeater row is my submitted value ?
Thanks !
Maybe you can add some hidden fields in your GF and populate them in your function populate_posts( $form ){}like that you should have accessible directly in your after_submit function.

get table rate shipping data in Woocommerce REST API

I am trying to get all active shipping methods in Woocommerce REST API. How can I achieve this. I am new to woocommerce REST API, Any help is much appreciated.
I want response something like this -
{
"shipping_methods": {
"method_id": "method_title"
}
}
I , finally, come up with the solution. It may help others.
Below is complete code to get all table rate shipping data including shipping zones, shipping methods by zone and rates by zone -
header('Content-type: application/json');
$json_file=file_get_contents('php://input');
$jsonvalue= json_decode($json_file,true);
global $wpdb;
global $woocommerce;
$active_methods = array();
$shipping_methods = $woocommerce->shipping->load_shipping_methods();
//echo "<pre>";print_r($shipping_methods);
foreach ( $shipping_methods as $id => $shipping_method ) {
if ( isset( $shipping_method->enabled ) && 'yes' === $shipping_method->enabled ) {
$data_arr = array( 'title' => $shipping_method->title, 'tax_status' => $shipping_method->tax_status );
if($id=='table_rate'){
$raw_zones = $wpdb->get_results("SELECT zone_id, zone_name, zone_order FROM {$wpdb->prefix}woocommerce_shipping_zones order by zone_order ASC;");
//echo "<pre>";print_r($raw_zones);
$shipping = array();
$shippingarr = array();
foreach ($raw_zones as $raw_zone) {
$zones = new WC_Shipping_Zone($raw_zone->zone_id);
$zone_id = $zones->zone_id;
$zone_name = $zones->zone_name;
$zone_enabled = $zones->zone_enabled;
$zone_type = $zones->zone_type;
$zone_order = $zones->zone_order;
$shipping['zone_id'] = $zone_id;
$shipping['zone_name'] = $zone_name;
$shipping['zone_enabled'] = $zone_enabled;
$shipping['zone_type'] = $zone_type;
$shipping['zone_order'] = $zone_order;
$shipping_methods = $zones->shipping_methods;
foreach($shipping_methods as $shipping_method){
$methodid = $shipping_method["number"];
$raw_rates[$methodid]['rates'] = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}woocommerce_shipping_table_rates WHERE shipping_method_id={$methodid};",ARRAY_A);
}
$shipping['shipping_methods'] = $raw_rates;
$raw_country = $wpdb->get_results("SELECT location_code FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE zone_id={$zone_id};",ARRAY_N);
$shipping['countries'] = $raw_country;
$shippingarr[] = $shipping;
}
$data_arr['shipping_zones'] = $shippingarr;
}
$active_methods[ $id ] = $data_arr;
}
}
if(!empty($shippingarr)){
$result['success']='true';
$result['error']="0";
$result['msg']='Shipping methos found.';
$result['data']=$active_methods;
}else{
$result['success']='true';
$result['error']="0";
$result['msg']='Shipping methos found.';
$result['data']= array();
}
echo json_encode($result); `

onchage dropdown data not getting refreshed in codeigniter

I have following code to load data in website based on location selected, on change the ajax is getting fired and getting value in controller and from controller to model and going into where clause for sql by the result of sql is not getting showed on website.But always enters into else loop of model
View:
$(document).on('change','#delvloc',function(e){
e.preventDefault(); // stops the jump when an anchor clicked.
var title = this.value; // anchors do have text not values.
$.ajax({
url: 'index.php?route=product/category/newfunc',
data: {'title': title}, // change this to send js object
type: "get",
success: function(data){
//document.write(data); just do not use document.write
console.log(data);
}
});
});
Controller:
public function newfunc(){
$data = array(
'title' => $this->request->get['title']
);
$this->load->model('catalog/product');
$this->model_catalog_product->getProducts($data);
}
Model:
public function getProducts($data = array()) {
$sql="SELECT p.product_id,p.city, (SELECT AVG(rating) AS total FROM monster_review r1 WHERE r1.product_id = p.product_id AND r1.status = '1' GROUP BY r1.product_id) AS rating, (SELECT price FROM monster_product_discount pd2 WHERE pd2.product_id = p.product_id AND pd2.customer_group_id = '1' AND pd2.quantity = '1' AND ((pd2.date_start = '0000-00-00' OR pd2.date_start < NOW()) AND (pd2.date_end = '0000-00-00' OR pd2.date_end > NOW())) ORDER BY pd2.priority ASC, pd2.price ASC LIMIT 1) AS discount, (SELECT price FROM monster_product_special ps WHERE ps.product_id = p.product_id AND ps.customer_group_id = '1' AND ((ps.date_start = '0000-00-00' OR ps.date_start < NOW()) AND (ps.date_end = '0000-00-00' OR ps.date_end > NOW())) ORDER BY ps.priority ASC, ps.price ASC LIMIT 1) AS special FROM monster_product p LEFT JOIN monster_product_description pd ON (p.product_id = pd.product_id) LEFT JOIN monster_product_to_store p2s ON (p.product_id = p2s.product_id) WHERE pd.language_id = '1' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '0'"
if (!empty($data['title'])) {
$sql .= " and p.city='". $data['title']. "' GROUP BY p.product_id";
}
else{$sql .= " and p.city='Pune' GROUP BY p.product_id";}
}
My HTML:
Delivery Location:
<select name="delvloc" id="delvloc" style="width: 100px;" ">
<?php foreach ($city as $user_group) { ?>
<?php if ($user_group['city'] == $user_group_id) { ?>
<option value="<?php echo $user_group['user_group_id']; ?>" selected="selected"><?php echo $user_group['city']; ?></option>
<?php } else { ?>
<option value="<?php echo $user_group['city']; ?>"><?php echo $user_group['city']; ?></option>
<?php } ?>
<?php } ?>
</select>
</li>
In Model
function getProducts($data) {
$sql="SELECT p.product_id,p.city, (SELECT AVG(rating) AS total FROM monster_review r1 WHERE r1.product_id = p.product_id AND r1.status = '1' GROUP BY r1.product_id) AS rating, (SELECT price FROM monster_product_discount pd2 WHERE pd2.product_id = p.product_id AND pd2.customer_group_id = '1' AND pd2.quantity = '1' AND ((pd2.date_start = '0000-00-00' OR pd2.date_start < NOW()) AND (pd2.date_end = '0000-00-00' OR pd2.date_end > NOW())) ORDER BY pd2.priority ASC, pd2.price ASC LIMIT 1) AS discount, (SELECT price FROM monster_product_special ps WHERE ps.product_id = p.product_id AND ps.customer_group_id = '1' AND ((ps.date_start = '0000-00-00' OR ps.date_start < NOW()) AND (ps.date_end = '0000-00-00' OR ps.date_end > NOW())) ORDER BY ps.priority ASC, ps.price ASC LIMIT 1) AS special FROM monster_product p LEFT JOIN monster_product_description pd ON (p.product_id = pd.product_id) LEFT JOIN monster_product_to_store p2s ON (p.product_id = p2s.product_id) WHERE pd.language_id = '1' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '0'"; # Mssing ; here
if (!empty($data['title']))
{
$sql .= " and p.city='". $data['title']. "' GROUP BY p.product_id";
}
else{
$sql .= " and p.city='Pune' GROUP BY p.product_id";
}
$query = $this->db->query($sql);
$result = $query->result_array();
return $result;
}
In Controller
function newfunc()
{
$data = array(
'title' => $this->request->get['title']
);
$this->load->model('catalog/product');
$result = $this->model_catalog_product->getProducts($data);
$response['data'] = '';
if (empty($result))
{
$response['data'] = false;
}
else {
$response['data'] = $result;
}
echo json_encode($response);
}

replacing "view cart" button with "proceed to checkout" button upon adding a product to cart

After product page reloads upon adding the product to cart, a notification appears on top saying "product was added to cart" followed by a "view cart" button.
However after product gets added to cart I want to direct customers to checkout page (where I'll add cart page functionality), not cart page. How can I replace the notification's link to cart with a link to checkout page?
wc_add_to_cart_message() function (in includes/wc-cart-functions.php) seems to generate this particular notification, should I override this function? If so, how?
try this...
function rei_wc_add_to_cart_message( $message, $product_id ) {
$titles = array();
if ( is_array( $product_id ) ) {
foreach ( $product_id as $id ) {
$titles[] = get_the_title( $id );
}
} else {
$titles[] = get_the_title( $product_id );
}
$titles = array_filter( $titles );
$added_text = sprintf( _n( '%s has been added to your cart.', '%s have been added to your cart.', sizeof( $titles ), 'woocommerce' ), wc_format_list_of_items( $titles ) );
// Output success messages
if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
$return_to = apply_filters( 'woocommerce_continue_shopping_redirect', wp_get_referer() ? wp_get_referer() : home_url() );
$message = sprintf( '%s %s', esc_url( $return_to ), esc_html__( 'Continue Shopping', 'woocommerce' ), esc_html( $added_text ) );
} else {
$message = sprintf( '%s %s', esc_url( wc_get_page_permalink( 'checkout' ) ), esc_html__( 'Proceed to Checkout', 'woocommerce' ), esc_html( $added_text ) );
}
return $message;
}
add_filter('wc_add_to_cart_message','rei_wc_add_to_cart_message',10,2);