Skip to content

Instantly share code, notes, and snippets.

@mattallan
Last active November 30, 2021 07:36
Show Gist options
  • Save mattallan/e2ffe7d4234222601ee3 to your computer and use it in GitHub Desktop.
Save mattallan/e2ffe7d4234222601ee3 to your computer and use it in GitHub Desktop.
sync end date fixer
<?php
/**
* Plugin Name: Fix incorrect end dates calculated on Synced Subscriptions
* Plugin URI:
* Description: Add `&sync_date_fixer=log` to the URL to log any incorrect end dates (see log file: wcs_sync_fixer_test). Add `&sync_date_fixer=update` to the URL to log and update any incorrect end dates (see log file: wcs_sync_fixer_updated to find which subscriptions were incorrect and have been updated.)
* Version: 1.0
* Author: Matt Allan
* Author URI: http://prospress.com
* License: GPLv2
*/
/**
* Log and/or update the incorrect end dates on a synced subscription.
*
* - To just log and not update add `&sync_date_fixer=log` to the URL
* - To update end dates and log them as well add `&sync_date_fixer=update`
*/
function wcs_sync_end_date_fixer() {
global $wpdb;
// get all subscriptions that have an end date attached and also contain a synced subscription
$results = $wpdb->get_results( "SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_contains_synced_subscription' AND post_id IN (SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_schedule_end' AND meta_value != 0 )" );
$log = new WC_Logger();
foreach ( $results as $result ) {
$subscription = wcs_get_subscription( $result->post_id );
$current_end = $subscription->get_date( 'end' );
$start = $subscription->get_date( 'start' );
if ( $subscription ) {
foreach ( $subscription->get_items() as $item ) {
$product_id = wcs_get_canonical_product_id( $item );
$product = wc_get_product( $product_id );
$new_end_date = 0;
if ( WC_Subscriptions_Synchroniser::is_product_synced( $product ) ) {
$from_date = temp_wcs_calculate_sync_date_in_past( $product, 'mysql', $start );
remove_filter( 'woocommerce_subscriptions_product_expiration_date', 'WC_Subscriptions_Synchroniser::recalculate_product_expiration_date' );
$new_end_date = WC_Subscriptions_Product::get_expiration_date( $product_id, $from_date );
add_filter( 'woocommerce_subscriptions_product_expiration_date', 'WC_Subscriptions_Synchroniser::recalculate_product_expiration_date', 10, 3 );
}
if ( 0 != $new_end_date && $current_end != $new_end_date ) {
$line = '[SUB #' . $result->post_id . '] first sync payment: ' . $from_date . ', start: ' . $start . ', old end date: ' . $current_end . ', new end date: ' . $new_end_date;
if ( isset( $_GET['sync_date_fixer'] ) ) {
if ( 'update' == $_GET['sync_date_fixer'] ) {
$subscription->update_dates( array( 'end' => $new_end_date ) );
$log->add( 'wcs_sync_fixer_updated', $line );
} elseif ( 'log' == $_GET['sync_date_fixer'] ) {
$log->add( 'wcs_sync_fixer_test', $line );
}
}
}
}
}
}
}
add_action( 'admin_footer', 'wcs_sync_end_date_fixer' );
/**
* A copy of WC_Subscriptions_Synchroniser::calculate_first_payment_date() except
* this function does not require the first payment to be in the future.
*/
function temp_wcs_calculate_sync_date_in_past( $product, $type = 'mysql', $from_date = '' ) {
$period = WC_Subscriptions_Product::get_period( $product );
$trial_period = WC_Subscriptions_Product::get_trial_period( $product );
$trial_length = WC_Subscriptions_Product::get_trial_length( $product );
$from_date_param = $from_date;
if ( empty( $from_date ) ) {
$from_date = gmdate( 'Y-m-d H:i:s' );
}
// If the subscription has a free trial period, the first payment should be synced to a day after the free trial
if ( $trial_length > 0 ) {
$from_date = WC_Subscriptions_Product::get_trial_expiration_date( $product, $from_date );
}
$from_timestamp = strtotime( $from_date ) + ( get_option( 'gmt_offset' ) * 3600 ); // Site time
$payment_day = WC_Subscriptions_Synchroniser::get_products_payment_day( $product );
if ( 'week' == $period ) {
// strtotime() will figure out if the day is in the future or today (see: https://gist.github.com/thenbrent/9698083)
$first_payment_timestamp = strtotime( WC_Subscriptions_Synchroniser::$weekdays[ $payment_day ], $from_timestamp );
} elseif ( 'month' == $period ) {
// strtotime() needs to know the month, so we need to determine if the specified day has occured this month yet or if we want the last day of the month (see: https://gist.github.com/thenbrent/9698083)
if ( $payment_day > 27 ) { // we actually want the last day of the month
$payment_day = gmdate( 't', $from_timestamp );
$month = gmdate( 'F', $from_timestamp );
} elseif ( gmdate( 'j', $from_timestamp ) > $payment_day ) { // today is later than specified day in the from date, we need the next month
$month = date( 'F', wcs_add_months( $from_timestamp, 1 ) );
} else { // specified day is either today or still to come in the month of the from date
$month = gmdate( 'F', $from_timestamp );
}
$first_payment_timestamp = strtotime( "{$payment_day} {$month}", $from_timestamp );
} elseif ( 'year' == $period ) {
// We can't use $wp_locale here because it is translated
switch ( $payment_day['month'] ) {
case 1 :
$month = 'January';
break;
case 2 :
$month = 'February';
break;
case 3 :
$month = 'March';
break;
case 4 :
$month = 'April';
break;
case 5 :
$month = 'May';
break;
case 6 :
$month = 'June';
break;
case 7 :
$month = 'July';
break;
case 8 :
$month = 'August';
break;
case 9 :
$month = 'September';
break;
case 10 :
$month = 'October';
break;
case 11 :
$month = 'November';
break;
case 12 :
$month = 'December';
break;
}
$first_payment_timestamp = strtotime( "{$payment_day['day']} {$month}", $from_timestamp );
}
// We calculated a timestamp for midnight on the specific day in the site's timezone, let's push it to 3am to account for any daylight savings changes
$first_payment_timestamp += 3 * HOUR_IN_SECONDS;
// And convert it to the UTC equivalent of 3am on that day
$first_payment_timestamp -= ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
return ( 'mysql' == $type && 0 != $first_payment_timestamp ) ? date( 'Y-m-d H:i:s', $first_payment_timestamp ) : $first_payment_timestamp;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment