WooCommerce Conditional Delivery Notice based on weekdays and time - php

Based on Conditional Delivery Notice based on Time and Date in Woocommerce answer code, I'm trying to display a shipping message with the following lightly modified code:
I try this code without finding the result I hope :
add_action( 'woocommerce_before_customer_login_form', 'next_day_delivery' );
add_action( 'woocommerce_before_customer_login_form', 'next_day_delivery' );
add_action( 'woocommerce_before_checkout_form', 'next_day_delivery' );
add_action( 'woocommerce_before_shop_loop', 'next_day_delivery' );
add_action( 'woocommerce_before_single_product_summary', 'next_day_delivery' );
add_action( 'woocommerce_before_cart', 'next_day_delivery' );
function next_day_delivery() {
if( WC()->cart->is_empty() )
return; // Exit
// Set the time zone
date_default_timezone_set('Europe/Paris');
// From Monday to Thursday
$is_week_days = in_array( date('w'), array( 3, 4, 5, 6, 0 ) ) ? true : false;
$is_monday = date('w') == 1 ? true : false; // Monday
$is_tuesday = date('w') == 2 ? true : false; // Tuesday
$end_time = mktime('21', '00', '00', date('m'), date('d'), date('Y'));
$now_time = time();
$after_tomorow = date('l', strtotime('+2 days'));
$dateDiff = intval(($end_time - $now_time)/60);
$diff_hours = intval($dateDiff/60);
$diff_minutes = $dateDiff%60;
$hours_label = _n( 'heure', 'heures', $diff_hours, 'wooocommerce' );
$minutes_label = _n( 'minute', 'minutes', $diff_minutes, 'wooocommerce' );
if ( $is_monday && $now_time or $is_tuesday && $now_time < $end_time ) {
// print the information notice
$message = sprintf( __( '%s left to be delivered tomorrow!', 'woocommerce' ),
$diff_hours.' '.$hours_label.' and '.$diff_minutes.' '.$minutes_label);
}
elseif ( $end_time <= $now_time && $is_week_days ) {
$message = sprintf( __( 'Your order will be delivered this %s.', 'woocommerce' ), $after_tomorow );
} else {
$message = __( 'Your order will be prepared and shipped next upcoming monday and delivered on tuesday.', 'woocommerce' );
}
wc_print_notice( $message, 'success' );
}
But I cannot make it work for my needs…
We deliver our product each thursday, friday and saturday in different local pickups. So I'd like to have the delivery message that uses the following logic :
the orders made each week before tuesday 9pm (21h00 in TimeZone Europe/Paris), it should say "%s left to be delivered from thursday !"
the orders made each week after tuesday 9pm (21h00 in TimeZone Europe/Paris), it should say "Your order will be prepared and shipped from next upcoming wednesday and delivered from next thursday."
Any help will be very much appreciated.

Related

PHP: determination of the date minus weekends and holidays

There is a WordPress and ACF field with a date in the format 2022-04-30. From this date need to calculate 2 other dates, -2 days and +25 days. The problem is that it is necessary to take into account only working days, i.e. check weekends and holidays.
For example, we set the date May 3, 2022, which is Tuesday. From this date, I need to subtract 2 days, i.e. May 1, 2022, but this is Sunday, so we have to return to the first working day before May 1, i.e. Friday April 29, 2022. It's the same with holidays.
At the moment I have this code:
$setDate = get_field('set_date'); // ACF field, set May 3, 2022 (2022-05-03)
$offDate = wp_date('j F Y', strtotime('-2 days', strtotime($setDate)));
echo $offDate; // returns Sunday, May 1, 2022
I found holidays and weekends in json https://github.com/d10xa/holidays-calendar/blob/master/json/consultant2022.json
So I need to compare the given date with the dates from json and if there is a match, then minus one day and check the received date again. If there is a match, again minus one day and so on until no matches are found. Am I thinking correctly and can you tell me how to implement it? I am a very bad programmer, but there is a task)
At the moment I was only able to compare the dates and return the result found/not found. But I can't figure out how to take days off on the basis of this and send it for verification again(
$setDate = '2022-05-01';
$file = file_get_contents('https://raw.githubusercontent.com/d10xa/holidays-calendar/master/json/consultant2022.json', true);
$data = json_decode($file);
$found = array_search($setDate, $data->holidays);
if ($found === False) {
echo 'Not Found';
} else {
echo 'found';
}
The following has been tested on a few dates and I think it works as it should.
/*
JSON file is saved locally to a sub-directory
for the current working script directory.
This is to avoid unneccessary hits to the
remote site.
*/
$format='Y-m-d';
$url='https://raw.githubusercontent.com/d10xa/holidays-calendar/master/json/consultant2022.json';
$setDate = '2022-05-01';
$filepath=sprintf('%s/json/%s', __DIR__, basename( $url ) );
if( !file_exists( $filepath ) ){
file_put_contents( $filepath, file_get_contents( $url ) );
}
# read the file and generate JSON
$json=json_decode( file_get_contents( $filepath ) );
$hols=$json->holidays;
# create the initial DateTime object and find which weekday we are dealing with
# where 1 (for Monday) through 7 (for Sunday)
$obj=new DateTime( $setDate );
$date=$obj->format( $format );
$day=$obj->format('N');
# Is the given date a holiday/weekend?
if( array_search( $date, $hols ) ){
if( $day > 5 ){
# Date is either Sat or Sun... go back to previous working day
$subtract = 2 - ( 7 - $day );
$int=new DateInterval( sprintf('P%sD', $subtract ) );
$obj=new DateTime( $setDate );
$previous=$obj->sub( $int );
}else{
$previous=$obj->sub( new DateInterval('P2D') );
}
# create the future date ( add 25 days )
$int=new DateInterval('P25D');
$obj=new DateTime( $setDate );
$future=$obj->add( $int );
if( array_search( $future->format( $format ), $hols ) ){
# Future date is a holiday... go back to previous working day
$day=$future->format('N');
$subtract = 2 - ( 7 - $day );
$int=new DateInterval( sprintf('P%sD',$subtract ) );
$future=$future->sub( $int );
}
}else{
# Given date is NOT a holiday...
# take a copy of the original DateTime object for generating future date.
$ref=new DateTime( $setDate );
$int=new DateInterval( 'P2D' );
$previous=$obj->sub( $int );
$day=$previous->format('N');
# Is this a holiday?
if( $day > 5 ){
# yes - go back to previous working day
$subtract = 2 - ( 7 - $day );
$int=new DateInterval( sprintf('P%sD',$subtract ) );
$previous=$previous->sub( $int );
}
$int=new DateInterval('P25D');
$future=$ref->add( $int );
$day=$future->format('N');
# Is this a holiday?
if( $day > 5 ){
$subtract = 2 - ( 7 - $day );
$int=new DateInterval( sprintf('P%sD',$subtract ) );
$future=$future->sub( $int );
}
}
printf(
'<pre>
Given date: %s
Previous (-2): %s
Future (+25): %s
</pre>',
$date,
$previous->format( $format ),
$future->format( $format )
);
Which yields:
Given date: 2022-05-01
Previous (-2): 2022-04-29
Future (+25): 2022-05-26

WooCommerce Subscriptions: change renewal dates after checkout

I have 1 subscription with 3 variations.
I'm trying to force the first renewal date depending on the variation ID that was purchased.
Variation 1 (876) set to renew every day - I want first re-bill date to be 11/15/2020 12:00AM
Variation 2 (877) set to renew every 2 days - I want first re-bill date to be 2/15/2021 12:00AM
Variation 3 (878) set to renew every 3 days - I want first re-bill date to be 8/15/2021 12:00AM
I thought the below code worked. After the order is created the next bill date DOES show one of the above dates, but it must not be registering with WooCommerce or something, because the renewals are triggering regardless of the above dates.
For my test, I create an order for Variation 1, the next payment date is showing 11/15/2020, but it's renewing the next day.
Hoping to get some insight from someone smarter than me. Again, the sub's next bill date is showing the above dates, but renewals are still happening early/before the above dates.
function nextpaymentdatechange( $order_id ){
$order = wc_get_order( $order_id );
$items = $order->get_items();
foreach ( $items as $item_id => $item ) {
$product_id = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id();
if ( $product_id === 876 ) {
$subid = $order_id + 1;
$nextdate = get_post_meta( $subid, '_schedule_next_payment', true );
$new_date = date( 'Y-m-d H:i:s', strtotime( '2020-11-15 07:00:00', strtotime( $nextdate )) );
update_post_meta( $subid , '_schedule_next_payment', $new_date);
}
else{ if ( $product_id === 877 ) {
$subid = $order_id + 1;
$nextdate = get_post_meta( $subid, '_schedule_next_payment', true );
$new_date = date( 'Y-m-d H:i:s', strtotime( '2021-02-15 07:00:00', strtotime( $nextdate )) );
update_post_meta( $subid , '_schedule_next_payment', $new_date);
}
else{
if ( $product_id === 878 ) {
$subid = $order_id + 1;
$nextdate = get_post_meta( $subid, '_schedule_next_payment', true );
$new_date = date( 'Y-m-d H:i:s', strtotime( '2021-08-03 07:00:00', strtotime( $nextdate )) );
update_post_meta( $subid , '_schedule_next_payment', $new_date);
}
}
}
}
} ```
To make that work you need first to get WC_Subscription Objects from the order using:
$subscriptions = wcs_get_subscriptions_for_order( $order_id );
that gives an array of the WC_Subscription Object(s) for the Order, from an order Id.
Now you can use the WC_Subscription method update_dates($dates), that requires to set an array of all dates types from a subscription that are 'start', 'trial_end', 'next_payment', 'last_payment' and 'end'.
To get a specific date from a subscription, we use WC_Subscription method get_date($date_type).
Additionally I use WC_Subscription method can_date_be_updated($date_type), to check if a date type can be updated.
I am not sure about which ones dates require to be updated as they are likely related.
You can try the following that should update the related(s) subscription(s) dates from an order, based on your code (the code doesn't throw errors).
I thin that you need to change 'next_payment', 'last_payment' and 'end' dates.
Using the methods update_dates() and save() at the end of the code, allows to change dates, save all required data to database refreshing cached data.
The function code:
function nextpaymentdatechange( $order_id ){
// YOUR SETTINGS: Set in this array your desired dates (value(s)) by product Id (key)
$dates_for_product = array(
876 => array(
'next_payment' => '2020-11-15 07:00:00',
'last_payment' => '2020-11-16 07:00:00',
),
877 => array(
'next_payment' => '2021-02-15 07:00:00',
'last_payment' => '2021-02-16 07:00:00',
),
878 => array(
'next_payment' => '2021-08-03 07:00:00',
'last_payment' => '2021-08-04 07:00:00',
),
);
// The date types for subscriptions
$suscription_date_types = array('start', 'trial_end', 'next_payment', 'last_payment', 'end');
// Get the subscriptions from the current order Id
$subscriptions = wcs_get_subscriptions_for_order( $order_id );
// Loop through subscriptions
foreach ( $subscriptions as $subscription_id => $subscription ) {
// Loop through items in this subscription
foreach ( $subscription->get_items() as $item_id => $item ) {
$product = $item->get_product();
// Loop through defined products dates
foreach( $dates_for_product as $product_id => $new_dates ) {
// If current subscription product id matches
if ( $product->get_id() == $product_id ) {
$current_product_id = $product_id; // Set current product id
break; // Stop the current loop
}
}
if ( isset($current_product_id) ) {
break; // Stop the current loop
}
}
// Updating subscription dates
if ( isset($current_product_id) ) {
$updated_dates = array(); // Initializing
// Loop through subscription date types
foreach( $suscription_date_types as $date_type ) {
$date = $subscription->get_date($date_type);
// For 'next_payment' and 'last_payment' dates
if( isset($new_dates[$date_type]) && $subscription->can_date_be_updated($date_type) ) {
$updated_dates[$date_type] = $new_dates[$date_type];
}
// For 'end' date
elseif ( $date_type === 'end' && $subscription->can_date_be_updated($date_type) ) {
$updated_dates[$date_type] = $new_dates['last_payment']; // ??? Or may be instead: $date; … (or simply: 0;)
}
// Other dates
else {
$updated_dates[$date_type] = $date;
}
}
// Update subscription date, save to database and refresh cached data
$subscription->update_dates($updated_dates);
$subscription->save();
}
}
}
Code goes in functions.php file of your active child theme (or active theme). It should works.
if i understand, this :
function nextpaymentdatechange( $order_id ){
// YOUR SETTINGS: Set in this array your desired dates (value(s)) by product Id (key)
$dates_for_product = array(
588 => array(
'end' => '2020-12-05 07:00:00',
),
);
// The date types for subscriptions
$suscription_date_types = array('start', 'trial_end', 'next_payment', 'last_payment', 'end');
Set automaticaly a end date 05 of december for my product 588 (it's a simple subscription)?

How to limit the number of orders per customer via a time limit in WooCommerce

I want to prevent selling each 24 hour by a customer.
check if there are other purchases from that costumer at the past 24hr and display an error before payment and ask to return later
What I have tried so far
function prevent_repeat_order() {
$last_24_hours_from_order_results = wc_get_customer_last_order($user_id);
(array( 'date_created' => '>=' . (time() - 86400), // time in seconds 'paginate' => true // adds a total field to the results ));
if ( $last_24_hours_from_last_order->total > 1 ) {
wc_add_notice('Too many orders in the last 24 hours. Please return later.', 'error');
}
}
add_action('woocommerce_checkout_process', 'prevent_repeat_order', 10, 0);
Of course it depends on where you want to show the message. The following code shows a error message on the checkout page, and hides the proceed_to_checkout button if the condition is not met.
With https://www.php.net/manual/en/function.date.php
the seconds can be converted to hours, etc...
function new_order_allowed() {
// Only on cart and check out pages
if( ! ( is_cart() || is_checkout() ) ) return;
// Will only work for logged in users
if ( is_user_logged_in() ) {
// Get user id
$user_id = get_current_user_id();
// Get last order
$last_order = wc_get_customer_last_order( $user_id );
if ( $last_order ) {
// Get date last order created - Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
$date_created = $last_order->get_date_created()->format( 'U' );
// Get current date - Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
$current_time = current_time( 'U', true );
// 24hr in seconds
$day_in_sec = 60 * 60 * 24;
// Seconds have passed since the last order
$seconds_passed = $current_time - $date_created;
// Check
if ( $seconds_passed < $day_in_sec ) {
// Add notice
wc_add_notice( sprintf( 'New orders are only allowed %1$s seconds after your previous order, currently %2$s seconds are passed', $day_in_sec, $seconds_passed ), 'error' );
// Removing the Proceed to checkout button from the Cart page
remove_action( 'woocommerce_proceed_to_checkout','woocommerce_button_proceed_to_checkout', 20);
}
}
}
}
add_action( 'woocommerce_check_cart_items', 'new_order_allowed' );

Display next delivery day based on date time rules in Woocommerce products loop

I have the following code below that creates a line of text under all products on the home, category and related products pages, that says "GREATER CAPE TOWN AREA"…
add_action( 'woocommerce_after_shop_loop_item_title', 'woocommerce_products_loop', 20 );
function woocommerce_products_loop(){
global $product;
echo '<p class="deliveryline">' . __("GREATER CAPE TOWN AREA", "woocommerce") . '</p>';
}
I'd like to have a line of text above this that says "NEXT DELIVERY: " and then uses the following logic:
If it's a weekday BEFORE 12pm, it should say "TOMORROW"
If it's a weekday AFTER 12pm, it should say the name of the business day after tomorrow.
if it's a friday BEFORE 12pm it should say "MONDAY"
if it's a friday AFTER 12pm it should say "TUESDAY"
Basically, we offer next day delivery on weekdays, for orders placed before 12pm.
I have code for something similar that may work..
$now = new Zend_Date();
if (($now->get(Zend_Date::WEEKDAY_DIGIT) % 6 == 0)
|| ($now->isLater('17:00:00', Zend_Date::TIMES))
) {
$now->set(
strtotime('+1 weekday', $now->toString(Zend_Date::TIMESTAMP)),
Zend_Date::TIMESTAMP
);
}
echo $now->toString(Zend_Date::W3C);
I just need help please figuring out the correct maths for what I need (this code is based on same day if before 5pm), and then where do I place it within the original code?
Could someone please help with this complete code snippet? Ideally I want it to look like the attached image.
Desired result:
The following code will display dynamically the delivery day based on your time and dates specifications (You will have to set your shop time zone in the code):
add_action( 'woocommerce_after_shop_loop_item_title', 'woocommerce_products_loop', 20 );
function woocommerce_products_loop(){
global $product;
// Set Your shop time zone (http://php.net/manual/en/timezones.php)
date_default_timezone_set('Europe/Paris');
$is_week_days = in_array( date('w'), array( 1, 2, 3, 4 ) ) ? true : false; // From Monday to Thursday
$is_friday = date('w') == 5 ? true : false; // Friday
$is_week_end = in_array( date('w'), array( 0, 6 ) ) ? true : false; // Weekend days
$end_time = mktime('12', '00', '00', date('m'), date('d'), date('Y')); // 12h00
$now_time = time();
$after_tomorow = date('l', strtotime('+2 days'));
// Displayed day conditions
if( $is_week_days && $now_time < $end_time ) {
$displayed_day = __("TOMORROW", "woocommerce");
} elseif( $is_week_days && $now_time >= $end_time ) {
$displayed_day = strtoupper( date( 'l', strtotime('+2 days') ) );
} elseif( $is_friday && $now_time < $end_time ) {
$displayed_day = __("MONDAY", "woocommerce");
} elseif( ( $is_friday && $now_time >= $end_time ) || $is_week_end ) {
$$displayed_day = __("THUESDAY", "woocommerce");
}
// Dynamic text Output based on date and time
echo '<p class="deliveryline">' . __("NEXT DELIVERY: ", "woocommerce") . $displayed_day .
'<br>' . __("GREATER CAPE TOWN AREA", "woocommerce") . '</p>';
}
Code goes in function.php file of your active child theme (active theme). Tested and works.

Get seconds between two dates and display it with human_time_diff()

I want to calculate seconds between two dates and display the purchased x seconds,minutes,hours ago text on my WooCommerce page.
I have this function:
$t_time = get_the_time( __( 'd/m/Y g:i:s', 'woocommerce' ), $order->ID );
/* $gmt_time = strtotime( $post->post_date_gmt ); */
$t_timestamp = strtotime ($t_time);
date_default_timezone_set('Europe/Budapest');
$ido = date('d/m/Y g:i:s');
$current_time = strtotime(date('d/m/Y g:i:s'));
//$current_time = strtotime(date('d/m/Y g:i:s'));
$time_diff = $current_time - $t_timestamp;
if ( $time_diff > 0 && $time_diff < 24*60*60 )
$h_time = sprintf( __( '%s ago', 'woocommerce' ), human_time_diff( $t_timestamp, $current_time ));
else
$h_time = get_the_time( $date_format, $order->ID );
}
Which outputs:
HTIME: 2014. szeptember 12.
time_diff: -2809
ido: 12/09/2014 5:36:17
t_time: 12/09/2014 5:23:06
current_time: 1418099777
t_timestamp: 1418102586
HTIME: 2 óra ago
gmt_time:
time_diff: 5891
ido: 12/09/2014 5:36:17
t_time: 12/09/2014 3:58:06
current_time: 1418099777
t_timestamp: 1418093886
My question is: How can be the current time smaller then the purchase date? In my opinion 12/09/2014 5:36:17 is bigger than 5:23:06.
Every help is appreciated :)
I have tried DateTime object, but if I add it, my jQuery is corrupted, and some parts of page doesn't load. There are an Uncaught TypeError errors.
I have solved the problem with the help of this code snippet.
http://www.intechgrity.com/calculate-the-difference-between-two-dates-in-php-using-php-5-3-oop-or-5-2-procedural/

Categories