In WooCommerce using the plugin Admin Custom Order Fields, I have a added a custom fields "Rebates Status" with 3 values "No Rebates", "Unpaid" and "Paid", on WooCommerce admin Orders list.
I have also displayed that on the view order screen just like the screenshot below:
Now I would like to bulk update the Rebates Status on selected orders, just as wooCommerce allow to do it for orders status bulk change.
Based on "Process custom bulk action on admin Orders list in Woocommerce" answer thread, I successfully added the 3 Rebates statuses on the bulk edit dropdown (as you can see on the first screenshot):
add_filter( 'bulk_actions-edit-shop_order', 'decrease_meals_orders_bulk_actions' );
function decrease_meals_orders_bulk_actions( $bulk_actions ) {
$bulk_actions['mr_norebates'] = 'Mark Transactions as No Rebates';
$bulk_actions['mr_unpaid'] = 'Mark Transactions as Unpaid';
$bulk_actions['mr_paid'] = 'Mark Transactions as Paid';
return $bulk_actions;
}
But no changes are applied when I try to bulk update the Rebates status for selected orders.
The meta key for Rebates Status is _wc_acof_2
Im stuck also and don't know how to solve the problem.
Any help is appreciated.
This is the complete, compact and optimized way to make it work for each of the 3 actions, to bulk update your custom "Rebate status" displaying a summary notice:
// Your settings in a function
function custom_admin_orders_bulk_actions( $labels = false ){
$domain = 'woocommerce';
return array(
'mr_norebates' => $labels ? __('No Rebates', $domain) : 'norebates',
'mr_unpaid' => $labels ? __('Unpaid', $domain) : 'unpaid',
'mr_paid' => $labels ? __('Paid', $domain) : 'paid',
);
}
// Display the custom actions on admin Orders bulk action dropdown
add_filter( 'bulk_actions-edit-shop_order', 'set_transactions_orders_bulk_actions' );
function set_transactions_orders_bulk_actions( $bulk_actions ) {
foreach( custom_admin_orders_bulk_actions(true) as $key => $label ) {
$bulk_actions[$key] = sprintf( __('Mark Transactions as %s', 'woocommerce'), $label );
}
return $bulk_actions;
}
// Process the bulk action from selected orders
add_filter( 'handle_bulk_actions-edit-shop_order', 'set_transactions_bulk_action_edit_shop_order', 10, 3 );
function set_transactions_bulk_action_edit_shop_order( $redirect_to, $action, $post_ids ) {
$actions = custom_admin_orders_bulk_actions();
if ( in_array( $action, array_keys($actions) ) ) {
$processed_ids = array(); // Initializing
foreach ( $post_ids as $post_id ) {
// Save the new value
update_post_meta( $post_id, '_wc_acof_2', $actions[$action] );
$processed_ids[] = $post_id; // Adding processed order IDs to an array
}
// Adding the right query vars to the returned URL
$redirect_to = add_query_arg( array(
'rebate_action' => $action,
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
return $redirect_to;
}
// Display the results notice from bulk action on orders
add_action( 'admin_notices', 'set_transactions_bulk_action_admin_notice' );
function set_transactions_bulk_action_admin_notice() {
global $pagenow;
if ( 'edit.php' === $pagenow && isset($_GET['post_type']) && 'shop_order' === $_GET['post_type']
&& isset($_GET['rebate_action']) && isset($_GET['processed_count']) && isset($_GET['processed_ids']) ) {
foreach( custom_admin_orders_bulk_actions(true) as $key => $label ) {
if ( $_GET['rebate_action'] === $key ) {
$count = intval( $_GET['processed_count'] );
printf( '<div class="notice notice-success fade is-dismissible"><p>' .
_n( '%s selected order updated to "%s" rebate status.',
'%s selected orders updated to "%s" rebate status.',
$count, 'woocommerce' )
. '</p></div>', $count, $label );
}
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Based on: Process custom bulk action on admin Orders list in Woocommerce
actually I made it work with the help of your code, but its too long, I appreciate if you can make it simple...
add_filter( 'bulk_actions-edit-shop_order', 'decrease_meals_orders_bulk_actions' );
function decrease_meals_orders_bulk_actions( $bulk_actions ) {
$bulk_actions['mr_norebates'] = 'Mark Transactions as No Rebates';
$bulk_actions['mr_unpaid'] = 'Mark Transactions as Unpaid';
$bulk_actions['mr_paid'] = 'Mark Transactions as Paid';
return $bulk_actions;
}
// Process the bulk action from selected orders
add_filter( 'handle_bulk_actions-edit-shop_order', 'decrease_meals_bulk_action_edit_shop_order', 10, 3 );
function decrease_meals_bulk_action_edit_shop_order( $redirect_to, $action, $post_ids ) {
if ( $action === 'mr_norebates' ){
$processed_ids = array(); // Initializing
foreach ( $post_ids as $post_id ) {
// Get number of meals
$nb_meal = get_post_meta( $post_id, '_wc_acof_2', true );
// Save the decreased number of meals ($meals - 1)
update_post_meta( $post_id, '_wc_acof_2', $nb_meal = 'norebates' );
$processed_ids[] = $post_id; // Adding processed order IDs to an array
}
// Adding the right query vars to the returned URL
$redirect_to = add_query_arg( array(
'mr_norebates' => 'No Rebates',
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
elseif ( $action === 'mr_unpaid' ){
$processed_ids = array(); // Initializing
foreach ( $post_ids as $post_id ) {
// Get number of meals
$nb_meal = get_post_meta( $post_id, '_wc_acof_2', true );
// Save the decreased number of meals ($meals - 1)
update_post_meta( $post_id, '_wc_acof_2', $nb_meal = 'unpaid' );
$processed_ids[] = $post_id; // Adding processed order IDs to an array
}
// Adding the right query vars to the returned URL
$redirect_to = add_query_arg( array(
'mr_unpaid' => 'Unpaid',
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
elseif ( $action === 'mr_paid' ){
$processed_ids = array(); // Initializing
foreach ( $post_ids as $post_id ) {
// Get number of meals
$nb_meal = get_post_meta( $post_id, '_wc_acof_2', true );
// Save the decreased number of meals ($meals - 1)
update_post_meta( $post_id, '_wc_acof_2', $nb_meal = 'paid' );
$processed_ids[] = $post_id; // Adding processed order IDs to an array
}
// Adding the right query vars to the returned URL
$redirect_to = add_query_arg( array(
'mr_paid' => 'Paid',
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
return $redirect_to;
}
// Display the results notice from bulk action on orders
add_action( 'admin_notices', 'decrease_meals_bulk_action_admin_notice' );
function decrease_meals_bulk_action_admin_notice() {
if ( empty( $_REQUEST['mr_norebates'] ) ) return; // Exit
$count = intval( $_REQUEST['processed_count'] );
printf( '<div id="message" class="updated fade"><p>' .
_n( 'Selected %s transaction updated.',
'Selected %s transactions updated.',
$count,
'mr_norebates'
) . '</p></div>', $count );
Related
I have this code which creates a drop-down on the product page. It saves the data chosen and it displays on the order. What I am need help with is displaying the value in the custom column.
Here's the code:
add_action( 'woocommerce_product_options_general_product_data', 'costcenter_management_group' );
function costcenter_management_group() {
global $post;
echo '<div class="options_group">';
woocommerce_wp_select( array(
'id' => '_cost_center',
'label' => __( 'Cost Center', 'woocommerce' ),
'options' => array(
'One' => __( 'Office One', 'woocommerce' ),
'Two' => __( 'Office Two', 'woocommerce' ),
)));
echo '</div>';
}
// save cost center settings
add_action( 'woocommerce_process_product_meta', 'costcenter_management_group_save' );
function costcenter_management_group_save( $post_id ){
if( isset( $_POST['_cost_center'] ) )
update_post_meta( $post_id, '_cost_center', esc_attr( $_POST['_cost_center'] ) );
}
// display on order
add_action('woocommerce_checkout_create_order_line_item', 'costcenter_management_group_display', 20, 4);
function costcenter_management_group_display($item, $cart_item_key, $values, $order) {
if ( $cost_center = $values['data']->get_meta('_cost_center') ) {
$item->update_meta_data( 'Cost Center', $cost_centre );
}}
// add cost center column on order view (WC admin)
add_filter('manage_edit-shop_order_columns', 'costcenter_management_group_column', 10, 1 );
function costcenter_management_group_column( $columns ) {
$actions_key = isset($columns['wc_actions']) ? 'wc_actions' : 'order_actions';
$order_actions = $columns[$actions_key];
unset($columns[$actions_key]);
$columns['cost_center'] = __("Cost Center", "woocommerce");
$columns[$actions_key] = $order_actions;
return $columns;
}
I know I can use echo but I don't know which value I'm supposed to use?
To display this custom order item metadata in your custom column use the following:
// Display data to custom column in admin orders list
add_action( 'manage_shop_order_posts_custom_column' , 'display_enclosed_invoice_order_column_data' );
function display_enclosed_invoice_order_column_data( $column ) {
global $the_order, $post;
if( $column == 'cost_center' ) {
$values = []; // Initializing
// Loop through order items
foreach ( $the_order->get_items() as $item ) {
if( $cost_centre = $item->get_meta( 'Cost Center' ) ) {
$values[] = $cost_centre;
}
}
// Display the value(s)
if( sizeof( $values ) > 0 ) {
echo implode( ', ', $values); // Convert the array to a coma separated string
}
}
}
Code goes on function.php file of your active child theme (or active theme). It should works.
I need kind of a seat reservation extension for woocommerce. Since most of the available plugins don't fit to well or don't work at all, i decided to try it myself.
Create the field (Product Meta) to store which seats are booked
add_action( 'woocommerce_product_data_panels', 'srd_seats' );
function srd_seats() {
global $woocommerce, $post;
?><div id="srd_seating_data" class="panel woocommerce_options_panel"><?php
woocommerce_wp_checkbox(
array(
'id' => '_seat_1',
'label' => __('1', 'woocommerce' ),
)
);
woocommerce_wp_checkbox(
array(
'id' => '_seat_2',
'label' => __('2', 'woocommerce' ),
)
);
woocommerce_wp_checkbox(
array(
'id' => '_seat_3',
'label' => __('3', 'woocommerce' ),
)
);
woocommerce_wp_checkbox(
array(
'id' => '_seat_4',
'label' => __('4', 'woocommerce' ),
)
);
woocommerce_wp_checkbox(
array(
'id' => '_seat_5',
'label' => __('5', 'woocommerce' ),
)
);
Saving the data
add_action( 'woocommerce_process_product_meta', 'srd_seatings_save' );
function srd_seatings_save( $post_id ){
$woocommerce_checkbox = isset( $_POST['_seat_1'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_1', $woocommerce_checkbox );
$woocommerce_checkbox = isset( $_POST['_seat_2'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_2', $woocommerce_checkbox );
$woocommerce_checkbox = isset( $_POST['_seat_3'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_3', $woocommerce_checkbox );
$woocommerce_checkbox = isset( $_POST['_seat_4'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_4', $woocommerce_checkbox );
$woocommerce_checkbox = isset( $_POST['_seat_5'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_5', $woocommerce_checkbox );
Output as checkboxes on checkout Page
Output all values as checkboxes: Booked seats get a special treatment and are disabled.
add_action( 'woocommerce_before_order_notes' , 'srd_seat_reservation');
function srd_seat_reservation($checkout){
// Loop through cart item quantity
foreach( WC()->cart->get_cart() as $cart_item ) {
$seats = array( '_seat_1',
'_seat_2',
'_seat_3',
'_seat_4',
'_seat_5',
);
foreach ($seats as $seat) {
$seatnumber = preg_replace('/[^0-9.]+/', '', $seat);
if ( get_post_meta( $cart_item[ 'product_id' ], $seat, true) == 'yes'){
echo '<input type="checkbox" disabled="disabled" checked="checked">'.$seatnumber.'<br>';}
else {
echo '<input type="checkbox" name="checkboxseat[]" value="'.$seatnumber.'">'.$seatnumber.'<br>';
}
}
}
}
Validate the selected fields
Customer needs to select exactly as many seats as there are items in cart.
add_action('woocommerce_checkout_process', 'srd_seating_process');
function srd_seating_process() {
$quantity = WC()->cart->get_cart_contents_count(); // Get cart quantity
$checked_arr = count($_POST['checkboxseat']); // Get Number of checked Checkboxes
if ($quantity > $checked_arr)
wc_add_notice( __( 'Bitte wählen Sie für jeden Teilnehmer einen Sitzplatz.', 'woocommerce' ), 'error' );
if ($quantity < $checked_arr)
wc_add_notice( __( 'Sie haben zu viele Sitzplätze ausgewählt.', 'woocommerce' ), 'error' );
}
My problem The next step would be to save the value ($seatnumber) of all selected checkboxes to the order meta and to update the product meta. I am struggeling to collect the selected checkboxes and save the values. Please see below my attempt of achieving it. Any help is highly appreciated! =)
add_action('woocommerce_checkout_create_order', 'srd_seating_create_order', 20, 2 );
function srd_seating_create_order( $order, $data ) {
foreach($_POST['checkboxseat'] as $check) {
$order->update_meta_data( 'Sitzplatz' , $check );
}
}
I prefer to answer in this thread than in your last related question.
You say that your code works, but there is some mistakes and oversights, so here is the complete revisited code, that will really save the data to the order and display it everywhere:
1) On product backend (settings):
// Add a custom tab to product pages settings
add_filter( 'woocommerce_product_data_tabs', 'add_custom_product_tab_settings' , 99 , 1 );
function add_custom_product_tab_settings( $product_data_tabs ) {
$product_data_tabs['seats'] = array(
'label' => __( 'Seats', 'my_text_domain' ),
'target' => 'seats_content',
);
return $product_data_tabs;
}
// The content of your custom product tab
add_action( 'woocommerce_product_data_panels', 'custom_product_tab_settings_content' );
function custom_product_tab_settings_content() {
global $post;
echo '<div id="seats_content" class="panel woocommerce_options_panel"><div class="options_group">';
$seats_number = (int) get_post_meta( $post->ID, '_seats', true );
$style1 = 'style="font-size:14px;font-weight:bold;"';
$style2 = 'style="color:#E32636;"';
$style3 = 'style="font-size:14px;font-style:italic;"';
if( ! ( $seats_number > 0 ) ) {
echo "<p $style1>" . sprintf( __('First, %s and %s (save)', 'woocommerce'),
"<span $style2>" . __('define the number of seats', 'woocommerce') . "</span>",
"<span $style2>" . __('Click on "Update"', 'woocommerce') . "</span>" ) . "</p>";
}
// Set the number of seats
woocommerce_wp_text_input( array(
'id' => '_seats',
'label' => __('Number of seats'),
'type' => 'number',
'value' => ( $seats_number > 0 ? $seats_number : 0 ),
));
echo '</div><div class="options_group">';
if( $seats_number > 0 ) {
// Loop through defined seats and display a checkbox for each
for ( $i = 1; $i <= $seats_number; $i++) {
woocommerce_wp_checkbox( array(
'id' => '_seat_'.$i,
'label' => $i,
));
}
} else {
echo "<p $style3>" . __('No defined seats number yet', 'woocommerce') . '</p>';
}
echo '</div></div>';
}
// Saving the data from your custom tab fields settings
add_action( 'woocommerce_process_product_meta', 'save_custom_product_data' );
function save_custom_product_data( $post_id ){
if( isset($_POST['_seats']) ){
// Update the seats number
update_post_meta( $post_id, '_seats', esc_attr($_POST['_seats']) );
// Loop through seats
for ( $i = 1; $i <= esc_attr($_POST['_seats']); $i++) {
$value = isset( $_POST['_seat_'.$i] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_'.$i, $value );
}
}
}
a) Step one: You will have to set first the number of seats for the product (when the value is zero, there is no checkboxes fields for the seats) and save.
b) Step two Now you get the number of checkboxes corresponding to the number of seats:
2) Front end - On checkout page (Display the available bookable seats fields):
add_action( 'woocommerce_before_order_notes' , 'checkout_seats_reservation');
function checkout_seats_reservation($checkout){
$item_count = 0;
echo '<h3 id="booking-seats">'.__("Book your purchased seats", "Woocommerce").'</h3>';
// Loop through cart item quantity
foreach( WC()->cart->get_cart() as $cart_item ) {
$item_count++;
$pid = $cart_item['product_id']; // The product Id
echo '<div class="item-seats item-'.$item_count.' product-'.$pid.'">
<p>Item '.$item_count.': "'. $cart_item['data']->get_name().'"
<em>(Purchased seats: <strong>'.$cart_item['quantity'].'</strong>)<em><p>
<ul style="list-style: none">';
// Get the number of seats for the current item (product)
$seats_number = get_post_meta( $pid, '_seats', true );
for ( $i = 1; $i <= $seats_number; $i++) {
if ( get_post_meta( $pid, '_seat_'.$i, true) === 'yes') {
echo '<li><input type="checkbox" disabled="disabled" checked="checked"><label>'.$i.'</label></li>';
} else {
echo '<li><input type="checkbox" name="checkboxseat_'.$pid.'[]" value="'.$i.'"><label>'.$i.'</label></li>';
}
}
echo '<ul></p></div>';
}
}
// Checkout Fields validation
add_action('woocommerce_checkout_process', 'seats_checkout_fields_validation');
function seats_checkout_fields_validation() {
$cart = WC()->cart;
$quantity = $cart->get_cart_contents_count(); // Get cart quantity
$seats_count = 0;
$unavailable = false;
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ){
$pid = $cart_item['product_id']; // The product ID
if( isset($_POST['checkboxseat_'.$pid]) ){
$checkboxseat = (array) $_POST['checkboxseat_'.$pid];
// Loop through selected
foreach( $checkboxseat as $seat ) {
// Check that selected seats are still available when order is submitted
if ( get_post_meta( $pid, '_seat_'.$seat, true) === 'yes') {
$unavailable = true;
break;
}
$seats_count++;
}
}
}
if( $unavailable ) {
wc_add_notice( __( 'Error: Some selected seats are not available anymore.', 'woocommerce' ), 'error' );
} elseif ( $quantity > $seats_count ) {
wc_add_notice( __( 'Bitte wählen Sie für jeden Teilnehmer einen Sitzplatz.', 'woocommerce' ), 'error' );
} elseif ( $quantity < $seats_count ) {
wc_add_notice( __( 'Sie haben zu viele Sitzplätze ausgewählt.', 'woocommerce' ), 'error' );
}
}
On checkout page, where checkboxes appear (Here 2 items):
3) Save the submitted data to Order, Update related products and display the chosen seats everywhere:
// Update the order meta data with the chosen seats raw array data
add_action('woocommerce_checkout_create_order', 'save_seats_reservation_to_order_and_update_product', 10, 2);
function save_seats_reservation_to_order_and_update_product( $order, $data ) {
$seats_arr = array();
// Loop through order items
foreach( $order->get_items() as $item ) {
$pid = $item->get_product_id();
if( isset( $_POST['checkboxseat_'.$pid]) ) {
$seats = (array) $_POST['checkboxseat_'.$pid];
// Loop through submitted seats
foreach ( $seats as $seat ){
// Update seats data in the product
update_post_meta( $pid, '_seat_'.$seat, 'yes' );
}
// Set and format our main array with the order item chosen seats
$seats_arr[] = $seats;
}
}
// Save selected seats multi-dimentional array data
$order->update_meta_data( '_sitzplatz', $seats_arr );
}
// Save chosen seats to each order item as custom meta data and display order items chosen seats everywhere
add_action('woocommerce_checkout_create_order_line_item', 'save_order_item_seats_reservation', 10, 4 );
function save_order_item_seats_reservation( $item, $cart_item_key, $values, $order ) {
// Get the chosen seats data from the order
if( isset( $_POST['checkboxseat_'.$item->get_product_id()]) ) {
$sitzplatz = $_POST['checkboxseat_'.$item->get_product_id()];
$value = implode( ', ', $sitzplatz );
// Save selected item seats to each order item as custom meta data
$item->update_meta_data( __('Sitzplatz'), $value );
}
}
On Order received (thank you):
On email notifications:
On Admin Order edit pages:
All code goes in function.php file of your active child theme (or active theme). Tested and works.
You should need to restyle things a bit to make it nicer.
// Update the order meta with the seats chosen
add_action('woocommerce_checkout_create_order', 'srd_seat_create_order', 20, 2);
function srd_seat_create_order( $order, $data ) {
$checks = $_POST['checkboxseat'];
foreach( WC()->cart->get_cart() as $cart_item ) {
foreach ($checks as $check){
$order->update_meta_data( '_sitzplatz'.$check , $check);
update_post_meta( $cart_item[ 'product_id'], $check , 'yes' );
}
}}
UPDATE Now it works =)
I've added two customer date inputs to the single product page. I need them to be required and validated before adding to the cart, and would also like the dates to be shown on the cart/checkout page and in the order emails.
I found the snippets needed here, however it was only for one custom field so I adjusted to make it for two: https://www.kathyisawesome.com/add-a-custom-field-to-woocommerce-product/
The input fields show up fine, but once you hit the Add to Cart button it doesn't carry throughout the order.
Here is the code used in my functions.php file:
/*
* Display inputs on single product page
*/
function amp_custom_option_1(){
$value = isset( $_POST['_est_delivery'] ) ? sanitize_text_field( $_POST['_est_delivery'] ) : '';
printf( '<div id="dates"><div class="delivery"><label>%s</label><input name="_est_delivery" value="%s" type="date" required /></div>', __( 'Estimated Delivery Date:', 'amp-plugin-textdomain-1' ), esc_attr( $value ) );
}
add_action( 'woocommerce_before_add_to_cart_form', 'amp_custom_option_1', 9 );
function amp_custom_option_2(){
$value = isset( $_POST['_est_pickup'] ) ? sanitize_text_field( $_POST['_est_pickup'] ) : '';
printf( '<div class="pickup"><label>%s</label><input name="_est_pickup" value="%s" type="date" required /></div></div>', __( 'Estimated Pickup Date:', 'amp-plugin-textdomain-2' ), esc_attr( $value ) );
}
add_action( 'woocommerce_before_add_to_cart_form', 'amp_custom_option_2', 9 );
/*
* Validate when adding to cart
*/
function amp_add_to_cart_validation_1($passed, $product_id, $qty){
if( isset( $_POST['_est_delivery'] ) && sanitize_text_field( $_POST['_est_delivery'] ) == '' ){
$product = wc_get_product( $product_id );
wc_add_notice( sprintf( __( '%s cannot be added to the cart until you enter a delivery date.', 'amp-plugin-textdomain-1' ), $product->get_title() ), 'error' );
return false;
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'amp_add_to_cart_validation_1', 10, 3 );
function amp_add_to_cart_validation_2($passed, $product_id, $qty){
if( isset( $_POST['_est_pickup'] ) && sanitize_text_field( $_POST['_est_pickup'] ) == '' ){
$product = wc_get_product( $product_id );
wc_add_notice( sprintf( __( '%s cannot be added to the cart until you enter a pickup date.', 'amp-plugin-textdomain-2' ), $product->get_title() ), 'error' );
return false;
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'amp_add_to_cart_validation_2', 10, 3 );
/*
* Add custom data to the cart item
*/
function amp_add_cart_item_data_1( $cart_item, $product_id ){
if( isset( $_POST['_est_delivery'] ) ) {
$cart_item['est_delivery'] = sanitize_text_field( $_POST['_est_delivery'] );
}
return $cart_item;
}
add_filter( 'woocommerce_add_cart_item_data', 'amp_add_cart_item_data_1', 10, 2 );
function amp_add_cart_item_data_2( $cart_item, $product_id ){
if( isset( $_POST['_est_pickup'] ) ) {
$cart_item['est_pickup'] = sanitize_text_field( $_POST['_est_pickup'] );
}
return $cart_item;
}
add_filter( 'woocommerce_add_cart_item_data', 'amp_add_cart_item_data_2', 10, 2 );
/*
* Load cart data from session
*/
function amp_get_cart_item_from_session_1( $cart_item, $values ) {
if ( isset( $values['est_delivery'] ) ){
$cart_item['est_delivery'] = $values['est_delivery'];
}
return $cart_item;
}
add_filter( 'woocommerce_get_cart_item_from_session', 'amp_get_cart_item_from_session_1', 20, 2 );
function amp_get_cart_item_from_session_2( $cart_item, $values ) {
if ( isset( $values['est_pickup'] ) ){
$cart_item['est_pickup'] = $values['est_pickup'];
}
return $cart_item;
}
add_filter( 'woocommerce_get_cart_item_from_session', 'amp_get_cart_item_from_session_2', 20, 2 );
/*
* Add meta to order item
*/
function amp_add_order_item_meta_1( $item_id, $values ) {
if ( ! empty( $values['est_delivery'] ) ) {
woocommerce_add_order_item_meta( $item_id, 'est_delivery', $values['est_delivery'] );
}
}
add_action( 'woocommerce_add_order_item_meta', 'amp_add_order_item_meta_1', 10, 2 );
function amp_add_order_item_meta_2( $item_id, $values ) {
if ( ! empty( $values['est_pickup'] ) ) {
woocommerce_add_order_item_meta( $item_id, 'est_pickup', $values['est_pickup'] );
}
}
add_action( 'woocommerce_add_order_item_meta', 'amp_add_order_item_meta_2', 10, 2 );
/*
* Get item data to display in cart
*/
function amp_get_item_data_1( $other_data, $cart_item ) {
if ( isset( $cart_item['est_delivery'] ) ){
$other_data[] = array(
'name' => __( 'Estimated Delivery Date:', 'amp-plugin-textdomain-1' ),
'value' => sanitize_text_field( $cart_item['est_delivery'] )
);
}
return $other_data;
}
add_filter( 'woocommerce_get_item_data', 'amp_get_item_data_1', 10, 2 );
function amp_get_item_data_2( $other_data, $cart_item ) {
if ( isset( $cart_item['est_pickup'] ) ){
$other_data[] = array(
'name' => __( 'Estimated Pickup Date', 'amp-plugin-textdomain-2' ),
'value' => sanitize_text_field( $cart_item['est_pickup'] )
);
}
return $other_data;
}
add_filter( 'woocommerce_get_item_data', 'amp_get_item_data_2', 10, 2 );
/*
* Show custom field in order overview
*/
function amp_order_item_product_1( $cart_item, $order_item ){
if( isset( $order_item['est_delivery'] ) ){
$cart_item_meta['est_delivery'] = $order_item['est_delivery'];
}
return $cart_item;
}
add_filter( 'woocommerce_order_item_product', 'amp_order_item_product_1', 10, 2 );
function amp_order_item_product_2( $cart_item, $order_item ){
if( isset( $order_item['est_pickup'] ) ){
$cart_item_meta['est_pickup'] = $order_item['est_pickup'];
}
return $cart_item;
}
add_filter( 'woocommerce_order_item_product', 'amp_order_item_product_2', 10, 2 );
/*
* Add the field to order emails
*/
function amp_email_order_meta_fields_1( $fields ) {
$fields['est_delivery'] = __( 'Estimated Delivery Date:', 'amp-plugin-textdomain-1' );
return $fields;
}
add_filter('woocommerce_email_order_meta_fields', 'amp_email_order_meta_fields_1');
function amp_email_order_meta_fields_2( $fields ) {
$fields['est_delivery'] = __( 'Estimate Pickup Date:', 'amp-plugin-textdomain-2' );
return $fields;
}
add_filter('woocommerce_email_order_meta_fields', 'amp_email_order_meta_fields_2');
I'm not sure what is wrong with my code? Any help is appreciated.
There was some errors and mistakes. I have changed and removed some hooks, remove unnecessary code, merged functions, revisited all your code. As Your 2 dates fields are on single product pages, they will be related to cart items and order items (so order items meta data).
I have set your 2 date fields slugs and labels in the first function, inside an array. Then I call that function everywhere else and I use a foreach loop to process each field. This avoid repetitions, optimize and compact the code.
The code (commented):
// Utility function that contain the 2 field keys and labels pairs used on all other functions
function get_date_label_keys(){
$text_domain = 'woocommerce';
return array( 'est_delivery' => __( 'Estimated Delivery Date', $text_domain ),
'est_pickup' => __( 'Estimated Pickup Date', $text_domain ) );
}
// Display custom fields on single product page (hook replaced)
add_action( 'woocommerce_before_add_to_cart_button', 'amp_display_custom_fields', 20 );
function amp_display_custom_fields(){
echo '<div id="dates">';
// Loop through each custom field
foreach( get_date_label_keys() as $key => $label ){
$class = str_replace('est_', '', $key); // The class
$value = isset($_POST[$key]) ? sanitize_text_field($_POST[$key]) : ''; // Display the value
printf( '<div class="%s"><label>%s:</label> <input type="date" name="%s" value="%s" required /></div>', $class, $label, $key, $value );
}
echo '</div><br clear="all">';
}
// Add to cart fields validation (in case of need)
add_filter( 'woocommerce_add_to_cart_validation', 'amp_add_to_cart_validation', 20, 3 );
function amp_add_to_cart_validation( $passed, $product_id, $qty ){
// Loop through each custom field
foreach( get_date_label_keys() as $key => $label ){
if( isset( $_POST[$key] ) && empty( $_POST[$key] ) ){
wc_add_notice( sprintf( __( '%s cannot be added to the cart until you enter a delivery date.', $domain ), get_the_title() ), 'error' );
$passed = false;
}
}
return $passed;
}
// Add to cart items the custom data
add_filter( 'woocommerce_add_cart_item_data', 'amp_add_cart_item_data', 20, 2 );
function amp_add_cart_item_data( $cart_item, $product_id ){
// Loop through each custom field
foreach( get_date_label_keys() as $key => $label ){
if( isset( $_POST[$key] ) )
$cart_item['dates'][$key] = sanitize_text_field( $_POST[$key] );
}
return $cart_item;
}
// Display the dates in cart items on cart and checkout pages
add_filter( 'woocommerce_get_item_data', 'amp_get_item_data', 20, 2 );
function amp_get_item_data( $item_data, $cart_item = null ) {
// Loop through each custom field
foreach( get_date_label_keys() as $key => $label ){
if ( isset( $cart_item['dates'][$key] ) )
$item_data[] = array(
'name' => $label,
'value' => sanitize_text_field( $cart_item['dates'][$key] )
);
}
return $item_data;
}
// Add order item meta data and Display the data in order items (hook replaced)
add_action( 'woocommerce_checkout_create_order_line_item', 'amp_add_order_item_meta', 20, 4 );
function amp_add_order_item_meta( $item, $cart_item_key, $values, $order ) {
foreach( get_date_label_keys() as $key => $label ){
// Loop through each custom field
if ( ! empty( $values['dates'][$key] ) )
$item->update_meta_data( $label, $values['dates'][$key] );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
On cart page (and checkout too):
Order received and order view pages (in admin order edit pages and email notifications too):
I am using the woocommerce action below to call a custom function, but for some reason it's being triggered twice on every order. Does anyone know why this could be or how to fix it so that it only calls once per order?
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
function parent_referral_for_all( $order_id ) {
....
}
UPDATE
I thought the action was being triggered twice but I'm not so sure now. I'm using this action to add another referral inside the affiliatewp plugin, which is adding twice, yet my echo of "Thank You" only appears once.
Everything is working as intended except that the referral (and it's associated order note) are being added twice.
Any help would be greatly appreciated.
That full function:
function parent_referral_for_all( $order_id ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
To avoid this repetition, you couldadd a custom post meta data to the current order, once your "another" referral has been added the first time.
So your code will be:
function parent_referral_for_all( $order_id ) {
## HERE goes the condition to avoid the repetition
$referral_done = get_post_meta( $order_id, '_referral_done', true );
if( empty($referral_done) ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
## HERE you Create/update your custom post meta data to avoid repetition
update_post_meta( $order_id, '_referral_done', 'yes' )
}
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
I hope this will help.
You can check for the existence of the _thankyou_action_done post_meta key like so:
<?php
/**
* Allow code execution only once when the hook is fired.
*
* #param int $order_id The Woocommerce order ID.
* #return void
*/
function so41284646_trigger_thankyou_once( $order_id ) {
if ( ! get_post_meta( $order_id, '_thankyou_action_done', true ) ) {
// Your code here.
}
}
add_action( 'woocommerce_thankyou', 'so41284646_trigger_thankyou_once', 10, 1 );
I have a a custom field value for each product. This custom field is inserted with the code below :
<?php
// Insert a Custom Admin Field
add_action( 'woocommerce_product_options_general_product_data', 'woo_add_custom_general_fields' );
function woo_add_custom_general_fields() {
echo '<div class="options_group">';
woocommerce_wp_text_input( array(
'id' => 'days_manufacture',
'label' => __( 'Days for Manufacture', 'woocommerce' ),
'placeholder' => '',
'description' => __( 'Insert here', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'step' => 'any',
'min' => '1'
),
) );
echo '</div>';
}
// Save the field
add_action( 'woocommerce_process_product_meta', 'woo_add_custom_general_fields_save' );
function woo_add_custom_general_fields_save( $post_id ){
$woocommerce_number_field = $_POST['days_manufacture'];
if( !empty( $woocommerce_number_field ) )
update_post_meta( $post_id, 'days_manufacture', esc_attr( $woocommerce_number_field ) );
}
// Store custom field
add_action( 'woocommerce_add_cart_item_data', 'save_days_field', 10, 2 );
function save_days_field( $cart_item_data, $product_id ) {
$special_item = get_post_meta( $product_id , 'days_manufacture',true );
if(!empty($special_item)) {
$cart_item_data[ 'days_manufacture' ] = $special_item;
// below statement make sure every add to cart action as unique line item
$cart_item_data['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'days_manufacture', $special_item );
}
return $cart_item_data;
}
// Render meta on cart and checkout
add_filter( 'woocommerce_get_item_data', 'rendering_meta_field_on_cart_and_checkout', 10, 2 );
function rendering_meta_field_on_cart_and_checkout( $cart_data, $cart_item ) {
$custom_items = array();
// Woo 2.4.2 updates
if( !empty( $cart_data ) ) {
$custom_items = $cart_data;
}
if( isset( $cart_item['days_manufacture'] ) ) {
$custom_items[] = array( "name" => __( "Days", "woocommerce" ), "value" => $cart_item['days_manufacture'] );
}
return $custom_items;
} ?>
This code works perfectly.
Now I would like on Cart and checkout (before coupon notice) pages to display the highest value of this custom field (days_manufacture) on a custom message, when multiple items are in Cart, like in this screenshot.
How can I achieve this?
Thanks.
Here is a custom function that will display a message on cart and checkout (before coupon notice) pages with the highest number of days of manufacture…
Here is the code:
add_action('woocommerce_before_cart', 'days_of_manufacture');
add_action('woocommerce_before_checkout_form', 'days_of_manufacture', 5);
function days_of_manufacture() {
$day_txt = ' ' . __('day', 'your_theme_domain_slug' );
$days_txt = ' ' . __('days', 'your_theme_domain_slug' );
$text = __('Your Order will be produced in: ', 'your_theme_domain_slug' );
$max_days = 0;
foreach( WC()->cart->get_cart() as $cart_item )
if($cart_item['days_manufacture'] > $max_days)
$max_days = $cart_item['days_manufacture'];
if($max_days != 0) {
if ($max_days == 1)
$days_txt = $day_txt;
$output = $text . $max_days . $days_txt;
echo "<div class='woocommerce-info'>$output</div>";
}
}
This code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works.
Store the variable in the $_SESSION, then loop thru them and pick the highest, so you can use it later anywhere in your code