I'm working with Wordpress, Woocommerce & Eventon plugin in order to create a selling tickets for events site. Users can order tickets for different dates.
I'm trying to implement a sortable column wich shows for every ticket sold the date for the event (stored in woocommerce_order_itemmeta table as meta_value of meta_key 'Event-Time'). So first of all I created and populated that column like this:
add_filter( 'manage_edit-shop_order_columns', 'wooc_set_custom_column_order_columns');
//create custom woocommerce columns
function wooc_set_custom_column_order_columns($columns) {#
$nieuwearray = array();
foreach($columns as $key => $title) {
if ($key=='billing_address') { // in front of the Billing column
$nieuwearray['order_product'] = __( 'Products', 'woocommerce' );
$nieuwearray['Event-Time'] = __( 'Reservation Date', 'woocommerce' );
}
$nieuwearray[$key] = $title;
}
return $nieuwearray ;
}
//populate custom woocommerce columns
add_action( 'manage_shop_order_posts_custom_column' , 'custom_shop_order_column', 10, 2 );
function custom_shop_order_column( $column ) {
global $post, $woocommerce, $the_order;
switch ( $column ) {
case 'order_product' :
$terms = $the_order->get_items();
if ( is_array( $terms ) ) {
foreach($terms as $term)
{
echo $term['item_meta']['_qty'][0] .' x ' . $term['name'] .'<br />';
}
} else {
_e( 'Unable get the product', 'woocommerce' );
}
break;
case 'Event-Time' :
$terms = $the_order->get_items();
if ( is_array( $terms ) ) {
foreach($terms as $term) {
if(array_key_exists( 'Event-Time', $term ) ){
$date_event = substr($term['item_meta']['Event-Time'][0], 0, 10);
echo $date_event . '<br />';
} else {
echo '<b>' . __('No existe fecha reserva?', 'woocommerce') . '</b>';
}
}
} else {
_e( 'Unable get the product', 'woocommerce' );
}
break;
}
}
In order to make Event-Time column sortable, I added this code:
add_filter( "manage_edit-shop_order_sortable_columns", 'sort_columns_woocommerce' );
function sort_columns_woocommerce( $columns ) {
$custom = array(
//'order_producten' => 'MY_COLUMN_1_POST_META_ID',
'Event-Time' => 'Event-Time'
);
return wp_parse_args( $custom, $columns );
}
but nothing happened, so after a few hours looking it up on Google, I found similar solution that I tried to adapt to my case:
function order_by_event_time_join($query) {
global $wpdb;
if ( is_admin() && (isset($_GET['post_type']) && $_GET['post_type'] === 'shop_order') && (isset($_GET['orderby']) && $_GET['orderby'] === 'Event-Time') ) {
$first_item_in_order = "SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = $wpdb->posts.ID AND order_item_type = 'line_item' LIMIT 0, 1";
$query .= "LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS items ON $wpdb->posts.ID = items.order_id AND items.order_item_id = ($first_item_in_order) ";
$query .= "LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS itemmeta ON items.order_item_id = itemmeta.order_item_id AND (itemmeta.meta_key = 'Event-Time') ";
}
return $query;
}
add_filter('posts_join', 'order_by_event_time_join');
function order_by_event_time_where($where) {
global $wpdb;
if( is_admin() && $_GET['post_type'] === 'shop_order' && (isset($_GET['orderby']) && $_GET['orderby'] === 'Event-Time') ) {
if(strpos($where, 'shop_webhook')) {
return " AND $wpdb->posts.post_type = 'shop_order' AND itemmeta.meta_key = 'Event-Time'";
}
}
return $where;
}
add_filter('posts_where', 'order_by_event_time_where');
But no luck... What I understand the problem is I'm not able to sort columns by the meta_value 'Event-Time' in the ticket-orders section of woocommerce...
I can show Event-Time data but impossible to sort/filter it.
Related
I would like to add a quanity eg 2 which users are allowed to buy within its timeframe. Also the error notice message is showing on the product page. How would I go about letting the product added to cart and the error message showing on the cart page?
Based on Allow customers to buy only one product from defined WooCommerce product category answer code, here is my code attempt:
// Based partially on wc_customer_bought_product(), will return a boolean value based on orders count (false for O orders and true when there is at least one paid order)
function has_bought( $value = 0 ) {
if ( ! is_user_logged_in() && $value === 0 ) {
return false;
}
global $wpdb;
// Based on user ID (registered users)
if ( is_numeric( $value ) ) {
$meta_key = '_customer_user';
$meta_value = $value == 0 ? (int) get_current_user_id() : (int) $value;
}
// Based on billing email (Guest users)
else {
$meta_key = '_billing_email';
$meta_value = sanitize_email( $value );
}
$paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
$count = $wpdb->get_var( $wpdb->prepare("
SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )
AND p.post_type LIKE 'shop_order'
AND pm.meta_key = '%s'
AND pm.meta_value = %s
AND UNIX_TIMESTAMP(p.post_date) >= (UNIX_TIMESTAMP(NOW()) - (86400 * 1))
LIMIT 1
", $meta_key, $meta_value ) );
// Return a boolean value based on orders count
return $count > 0 ? true : false;
}
function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
// Set categories
$categories = array ( 'kitchen' );
// If passed & has category
if ( $passed && has_term( $categories, 'product_cat', $product_id ) ) {
// Initialize
$value = '';
// User logged in
if ( is_user_logged_in() ) {
// Get the current user's ID
$value = get_current_user_id();
} else {
// Get billing_email
$value = WC()->customer->get_billing_email();
// When empty
if ( empty ( $value ) ) {
// Get account email
$value = WC()->customer->get_email();
}
}
// NOT empty
if ( ! empty ( $value ) ) {
if ( has_bought( $value ) ) {
// Display an error message
wc_add_notice( __( 'My custom error message', 'woocommerce' ), 'error' );
// False
$passed = false;
}
}
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );
I have a custom column in the WooCoomerce admin order list that displays customer notes.
I only want to show the notes in the order list if the order is in a specific custom status wc-autoquote.
Is this possible to filter this out for all other order statuses?
Code for my custom column:
add_filter('manage_edit-shop_order_columns', 'add_customer_note_column_header');
function add_customer_note_column_header($columns) {
$new_columns = (is_array($columns)) ? $columns : array();
$new_columns['order_customer_note'] = 'Customer Notes';
return $new_columns;
}
add_action('admin_print_styles', 'add_customer_note_column_style');
function add_customer_note_column_style() {
$css = '.widefat .column-order_customer_note { width: 15%; }';
wp_add_inline_style('woocommerce_admin_styles', $css);
}
add_action('manage_shop_order_posts_custom_column', 'add_customer_note_column_content');
function add_customer_note_column_content($column) {
global $post, $the_order;
if(empty($the_order) || $the_order->get_id() != $post->ID) {
$the_order = wc_get_order($post->ID);
}
$customer_note = $the_order->get_customer_note();
if($column == 'order_customer_note') {
echo('<span class="order-customer-note">' . $customer_note . '</span>');
}
You could use the following where you are going to specify the status
// If has order status
if ( $order->has_status( 'MY STATUS' ) ) {...
So you get
// Add a Header
function filter_manage_edit_shop_order_columns( $columns ) {
// Add new column
$columns['order_customer_note'] = __( 'Customer Notes', 'woocommerce' );
return $columns;
}
add_filter( 'manage_edit-shop_order_columns', 'filter_manage_edit_shop_order_columns', 10, 1 );
// Populate the Column
function action_manage_shop_order_posts_custom_column( $column, $post_id ) {
// Compare
if ( $column == 'order_customer_note' ) {
// Get order
$order = wc_get_order( $post_id );
// Has order status
if ( $order->has_status( 'autoquote' ) ) {
// Get customer note
$customer_note = $order->get_customer_note();
// NOT empty
if ( ! empty ( $customer_note ) ) {
echo '<span class="order-customer-note">' . $customer_note . '</span>';
}
}
}
}
add_action( 'manage_shop_order_posts_custom_column' , 'action_manage_shop_order_posts_custom_column', 10, 2 );
I added the column with the highest value of the offer. And then I would like to make it sortable. I've prepared this:
// asl this will add extra column in the product list
add_filter( 'manage_edit-product_columns', array($this,'show_product_offers_amounts'),15 );
add_action( 'manage_product_posts_custom_column', array($this,'show_product_offers_amount_max'), 10, 2 );
// asl show the column
function show_product_offers_amounts($columns){
$columns['orig_offer_amount'] = 'Amount';
return $columns;
}
// asl add the datas to column
function show_product_offers_amount_max( $column, $postid ) {
global $wpdb;
if ( $column == 'orig_offer_amount' ) {
$offers_max = $wpdb->get_var( " select max(meta_value)
from ".$wpdb->postmeta."
where meta_key='orig_offer_amount'
and meta_value!=''
and post_id in(
select p.post_id
from ".$wpdb->postmeta." as p
where p.meta_key='orig_offer_product_id' and
p.meta_value=".$postid.")"
);
if($offers_max > 0){
echo '<mark style="color: #3973aa;font-size: 13px;font-weight: 500;background: 0 0;line-height: 1;">'.$offers_max.'</mark>';
}
else{
echo '<span class="na">–</span>';
}
}
}
// asl register the column as sortable
function price_column_register_sortable( $columns ) {
$columns['orig_offer_amount'] = 'orig_offer_amount';
return $columns;
}
add_filter( 'manage_edit-product_sortable_columns', 'price_column_register_sortable' );
function price_column_orderby( $vars ) {
if ( isset( $vars['orderby'] ) && 'price' == $vars['orderby'] ) {
$offers_max = $wpdb->get_var( " select max(meta_value)
from ".$wpdb->postmeta."
where meta_key='orig_offer_amount'
and meta_value!=''
and post_id in(
select p.post_id
from ".$wpdb->postmeta." as p
where p.meta_key='orig_offer_product_id' and
p.meta_value=".$postid.")"
);
if($offers_max > 0){
$offers_max = $offers_max;
}
else{
$offers_max = 0;;
}
$vars = array_merge( $vars, array(
'meta_key' => 'orig_offer_amount',
'orderby' => $offers_max
) );
}
return $vars;
}
add_filter( 'request', 'price_column_orderby' );
This code makes that wordpress recognize the column as sortable. But it doesn't sort properly.
Any idea?
Using LoicTheAztec suggestion I prepared something like this:
add_action( 'save_post', 'new_postmeta_to_products' );
function new_postmeta_to_products($post_id){
$post_type = get_post_type($post_id);
if($post_type == 'products') {
global $wpdb;
$maxId = $wpdb->get_var( " select post_id
from ".$wpdb->postmeta."
where meta_key='orig_offer_amount'
and meta_value!=''
and post_id in(
select p.post_id
from ".$wpdb->postmeta." as p
where p.meta_key='orig_offer_product_id' and
p.meta_value=".$post_id.")
order by meta_value desc limit 1"
);
add_post_meta($post_id,'offer_max_id',$maxId);
}
}
But it doesn't work :( maybe because $post_id
Thanks to LoicTheAztec's hints I've prepared some snippet. First I've added new postmeta for product, when is created:
add_post_meta($post_id, 'offers_max_id', null);
add_post_meta($post_id, 'offers_max_value', null);
add_post_meta($post_id, 'offers_count', null);
Then, a few lines to set the new postmeta when an offer is created in www/wp-content/plugins/offers-for-woocommerce/public/class-offers-for-woocommerce.php in function new_offer_form_submit() :
global $post;
$_product = get_post_meta($parent_post_id, 'offer_product_id', true);
global $wpdb;
$offerMax = $wpdb->get_results( " select post_id, meta_value
from ".$wpdb->postmeta."
where meta_key='orig_offer_amount'
and meta_value!=''
and post_id in(
select p.post_id
from ".$wpdb->postmeta." as p
where p.meta_key='orig_offer_product_id' and
p.meta_value=".$_product.")
order by meta_value desc limit 1"
,ARRAY_N);
update_post_meta($_product, 'offers_max_id', $offerMax[0][0]);
update_post_meta($_product, 'offers_max_value', $offerMax[0][1]);
$offerCount = $wpdb->get_var( "select count(post_id)
from ".$wpdb->postmeta."
where meta_key='orig_offer_product_id' and
meta_value=".$_product
);
update_post_meta($_product, 'offers_count', $offerCount);
Then, we must add new columns for products' admin panel in function.php:
// asl show the column
function my_cpt_columns( $columns ) {
$columns["offermaxid"] = "OfferId";
$columns["offercount"] = "Offers";
$columns["offermaxvalue"] = "Amount";
$columns["dateofend"] = "EndTime";
return $columns;
}
add_filter('manage_edit-product_columns', 'my_cpt_columns');
// asl take a data to the column
function my_cpt_column( $colname, $cptid ) {
if ( $colname == 'offermaxid')
echo get_post_meta( $cptid, 'offers_max_id', true );
if ( $colname == 'offercount')
echo get_post_meta( $cptid, 'offers_count', true );
if ( $colname == 'offermaxvalue')
echo get_post_meta( $cptid, 'offers_max_value', true );
if ( $colname == 'dateofend')
echo date("j M y H:i:s", (get_post_meta( $cptid, '_sale_price_dates_to', true )+60*60) );
if ( $colname == 'offerlefttime') {
$atime = time();
$d = (get_post_meta( $cptid, '_sale_price_dates_to', true ) - $atime)/(60*60*24);
$h = ( get_post_meta( $cptid, '_sale_price_dates_to', true ) - $atime - ( (60*60*24) * explode(".", $d)[0] ) )/(60*60);
$m = ( get_post_meta( $cptid, '_sale_price_dates_to', true ) - $atime - ( (60*60*24) * explode(".", $d)[0] ) - ( (60*60) * explode(".", $h)[0] ) )/(60);
echo floor($d) . "d " . floor($h) . "h " . floor($m) . "m";
}
}
add_action('manage_product_posts_custom_column', 'my_cpt_column', 10, 2);
// asl sortowanie
add_filter('manage_edit-product_sortable_columns', 'my_cpt_columns');
function my_sort_metabox( $vars ) {
if( array_key_exists('orderby', $vars )) {
if('OfferId' == $vars['orderby']) {
$vars['orderby'] = 'meta_value';
$vars['meta_key'] = 'offers_max_id';
}
if('Offers' == $vars['orderby']) {
$vars['orderby'] = 'meta_value';
$vars['meta_key'] = 'offers_count';
}
if('Amount' == $vars['orderby']) {
$vars['orderby'] = 'meta_value';
$vars['meta_key'] = 'offers_max_value';
}
if('EndTime' == $vars['orderby']) {
$vars['order'] = 'ASC';
$vars['orderby'] = 'meta_value';
$vars['meta_key'] = '_sale_price_dates_to';
}
}
return $vars;
}
add_filter('request', 'my_sort_metabox');
// asl dodanie kolumny wynikowej z czasem do końca
function my_cpt_column_time( $columns ) {
$columns["offerlefttime"] = "LeftTime";
return $columns;
}
add_filter('manage_edit-product_columns', 'my_cpt_column_time');
And everything nice but one thing doesn't work. I want to have the products sort by column 'EndTime' ASC.
Any suggestions?
Since I will be using offline payments (bank billets - standard in Brazil). What I am trying to achieve is to auto-cancel "on-hold" orders after 9 days, which is when the billet expires. I found a few references of code: one from woocommerce github and another from stackoverflow. What the code does (kinda messy in my opinion) is mirror the "pending" cancelation to "on hild". On github they are saying that it's important to use date_modified arguments to pull orders from the last hour. I have tested this out but it's not working. Do not know what the problem is.
<?php
function wc_foo_cancel_unpaid_onhold_orders() {
global $wpdb;
$held_duration = get_option('woocommerce_hold_stock_minutes');
if ( $held_duration < 1 || 'yes' !== get_option( 'woocommerce_manage_stock')) {
return;
}
$unpaid_orders = wc_foo_cancel_unpaid_onhold_orders( strtotime( '-' . absint( $held_duration) . ' MINUTES'. current_time( 'timestamp')));
if ( $unpaid_orders) {
foreach ( $unpaid_orders as $unpaid_orders ){
$order = wc_get_order( $unpaid_order);
if ( apply_filters( 'woocommerce_cancel_unpaid_order', 'checkout' == $order->get_created_via(), $order ) ) {
$order ->update_status( 'cancelled', _( 'Unpaid order cancelled - time limite reached.', 'woocommerce'));
}
}
}
}
add_action( 'woocommerce_cancel_unpaid_orders', 'wc_foo_cancel_unpaid_onhold_orders');
function wc_foo_get_unpaid_onhold_orders( $date ){
global $wpdb;
$args = array(
'date_modified' => '>' . ( time() - HOUR_IN_SECONDS ),
'status' => 'on-hold',);
$orders = wc_get_orders( $args );
$unpaid_orders = $wpdb->get_col( $wpdb->prepare( "
SELECT posts.id
FROM {$wpdb->posts} AS posts
WHERE posts.posts_type IN ('" . implode( "','", wc_get_order_types()). "')
AND posts.post_status = 'wc-on-hold'
AND posts.date_modified < %s
", date( 'Y-m-d H:i:s', absint( $date)) ) );
}
?>
So I had a little bit of help form #danaharrison at Wordpress.org to achieve this. So the code only applies to 'on-hold' orders.
// To change the amount of days just change '-7 days' to your liking.
function get_unpaid_submitted() {
global $wpdb;
$unpaid_submitted = $wpdb->get_col( $wpdb->prepare( "
SELECT posts.ID
FROM {$wpdb->posts} AS posts
WHERE posts.post_status = 'wc-on-hold'
AND posts.post_date < %s
", date( 'Y-m-d H:i:s', strtotime('-7 days') ) ) );
return $unpaid_submitted;
}
// This excludes check payment type.
function wc_cancel_unpaid_submitted() {
$unpaid_submit = get_unpaid_submitted();
if ( $unpaid_submit ) {
foreach ( $unpaid_submit as $unpaid_order ) {
$order = wc_get_order( $unpaid_order );
$cancel_order = True;
foreach ( $order->get_items() as $item_key => $item_values) {
$manage_stock = get_post_meta( $item_values['variation_id'], '_manage_stock', true );
if ( $manage_stock == "no" ) {
$payment_method = $order->get_payment_method();
if ( $payment_method == "cheque" ) {
$cancel_order = False;
}
}
}
if ( $cancel_order == True ) {
$order -> update_status( 'cancelled', __( 'Pagamento não identificado e cancelado.', 'woocommerce') );
}
}
}
}
add_action( 'woocommerce_cancel_unpaid_submitted', 'wc_cancel_unpaid_submitted' );
/* End of code. */
I need to implement a filter in the woocommerce backend, that I can use to filter the orders by the selected shipping method.
I can create a filter on custom fields and alter the query, but the problem is that woocommerce stores the shipping method in a custom table of the DB.
Any hints on how to achieve this filter?
I solved adding a dropdown menu, using this hook:
add_action( 'restrict_manage_posts', 'display_shipping_dropdown' );
And then used this other hook to extend the where clause:
add_filter( 'posts_where', 'admin_shipping_filter', 10, 2 );
function admin_shipping_filter( $where, &$wp_query )
{
global $pagenow;
$method = $_GET['shipping_filter'];
if ( is_admin() && $pagenow=='edit.php' && $wp_query->query_vars['post_type'] == 'shop_order' && !empty($method) ) {
$where .= $GLOBALS['wpdb']->prepare( 'AND ID
IN (
SELECT order_id
FROM wp_woocommerce_order_items
WHERE order_item_type = "shipping"
AND order_item_name = "' . $method . '"
)' );
}
return $where;
}
To complete Lorenzo's answer, here is a function you can use to generate the filter html :
function display_shipping_dropdown(){
if (is_admin() && !empty($_GET['post_type']) && $_GET['post_type'] == 'shop_order'){
$exp_types = array();
$zones = WC_Shipping_Zones::get_zones();
foreach($zones as $z) {
foreach($z['shipping_methods'] as $method) {
$exp_types[] = $method->title;
}
}
?>
<select name="shipping_method">
<option value=""><?php _e('Filter par expédition', 'woocommerce'); ?></option>
<?php
$current_v = isset($_GET['shipping_method']) ? $_GET['shipping_method'] : '';
foreach ($exp_types as $label) {
printf
(
'<option value="%s"%s>%s</option>',
$label,
$label == $current_v? ' selected="selected"':'',
$label
);
}
?>
</select>
<?php
}
}
add_action( 'restrict_manage_posts', 'display_shipping_dropdown' );
in Lorenzo's solution, the algorithm will fail if the title of the shipping method is changed. (in my case dynamically by a plugin).
Using instance_id instead of title will avoid this.
add_action( 'restrict_manage_posts', function () {
if ( is_admin() && ! empty( $_GET['post_type'] ) && $_GET['post_type'] == 'shop_order' ) {
$exp_types = array();
$zones = WC_Shipping_Zones::get_zones();
foreach ( $zones as $z ) {
foreach ( $z['shipping_methods'] as $method ) {
$exp_types[ $method->instance_id ] = $method->title;
}
}
?>
<select name="shipping_method">
<option value=""><?php _e( 'Shipping filter' ); ?></option>
<?php
$current_v = isset( $_GET['shipping_method'] ) ? $_GET['shipping_method'] : '';
foreach ( $exp_types as $key => $label ) {
printf( '<option value="%s"%s>%s</option>', $key, $key == $current_v ? ' selected="selected"' : '', $label );
}
?>
</select>
<?php
}
} );
add_filter( 'posts_where', function ( $where, &$wp_query ) {
global $pagenow, $wpdb;
$method = isset( $_GET['shipping_method'] ) ? $_GET['shipping_method'] : false;
if ( is_admin() && $pagenow == 'edit.php' && $wp_query->query_vars['post_type'] == 'shop_order' && ! empty( $method ) ) {
$where .= $wpdb->prepare( "AND ID
IN (
SELECT order_id
FROM {$wpdb->prefix}woocommerce_order_itemmeta m
LEFT JOIN {$wpdb->prefix}woocommerce_order_items i
ON i.order_item_id = m.order_item_id
WHERE meta_key = 'instance_id' and meta_value = '{$method}' )" );
}
return $where;
}, 10, 2 );
If you combine MrSwed and Lorenzo's code, you get this (please correct me if I'm wrong but it works for me):
if ( is_admin()) {
add_filter( 'posts_where', 'admin_shipping_filter', 10, 2 );
function admin_shipping_filter( $where, $wp_query )
{
global $pagenow;
$method = $_GET['shipping_method'];
if ( is_admin() && $pagenow=='edit.php' && $wp_query->query_vars['post_type'] == 'shop_order' && !empty($method) ) {
$where .= $GLOBALS['wpdb']->prepare( 'AND ID
IN (
SELECT order_id
FROM wp_woocommerce_order_items
WHERE order_item_type = "shipping"
AND order_item_name = "' . $method . '"
)' );
}
return $where;
}
function display_shipping_dropdown(){
if (is_admin() && !empty($_GET['post_type']) && $_GET['post_type'] == 'shop_order'){
$exp_types = array();
$zones = WC_Shipping_Zones::get_zones();
foreach($zones as $z) {
foreach($z['shipping_methods'] as $method) {
$exp_types[ $method->instance_id ] = $method->title;
}
}
?>
<select name="shipping_method">
<option value=""><?php _e('Filter Shipping', 'woocommerce'); ?></option>
<?php
$current_v = isset($_GET['shipping_method']) ? $_GET['shipping_method'] : '';
foreach ($exp_types as $label) {
printf
(
'<option value="%s"%s>%s</option>',
$label,
$label == $current_v? ' selected="selected"':'',
$label
);
}
?>
</select>
<?php
}
}
add_action( 'restrict_manage_posts', 'display_shipping_dropdown' );
}