Skip to content

Instantly share code, notes, and snippets.

@pramodjodhani
Last active June 16, 2025 12:05
Show Gist options
  • Save pramodjodhani/fbec33ad84fec1a60c0ac00894ad0899 to your computer and use it in GitHub Desktop.
Save pramodjodhani/fbec33ad84fec1a60c0ac00894ad0899 to your computer and use it in GitHub Desktop.
<?php
/**
* Plugin Name: Recurring Slot Calculator Test
* Plugin URI: https://iconicwp.com
* Description: Test plugin for RecurringSlotCalculator class
* Version: 1.0.0
* Author: IconicWP
* Text Domain: recurring-slot-test
*
* @package RecurringSlotTest
*/
namespace RecurringSlotTest;
use Iconic_WDS\Subscriptions\RecurringSlotCalculator;
use Iconic_WDS\Subscriptions\Dto\SubscriptionOrderMetaData;
use DateTime;
use Exception;
defined( 'ABSPATH' ) || exit;
/**
* Main test plugin class.
*/
class RecurringSlotTest {
/**
* Plugin instance.
*
* @var RecurringSlotTest
*/
private static $instance = null;
/**
* Get plugin instance.
*
* @return RecurringSlotTest
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor.
*/
private function __construct() {
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
add_action( 'admin_init', array( $this, 'handle_form_submission' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
}
/**
* Add admin menu.
*/
public function add_admin_menu() {
add_menu_page(
__( 'Recurring Slot Test', 'recurring-slot-test' ),
__( 'Slot Calculator Test', 'recurring-slot-test' ),
'manage_options',
'recurring-slot-test',
array( $this, 'admin_page_callback' ),
'dashicons-calendar-alt',
30
);
}
/**
* Enqueue scripts.
*
* @param string $hook Current admin page hook.
*/
public function enqueue_scripts( $hook ) {
if ( 'toplevel_page_recurring-slot-test' !== $hook ) {
return;
}
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'jquery-ui-datepicker' );
wp_enqueue_style( 'jquery-ui-datepicker', 'https://code.jquery.com/ui/1.12.1/themes/ui-lightness/jquery-ui.css', array(), '1.12.1' );
}
/**
* Handle form submission.
*/
public function handle_form_submission() {
if ( ! isset( $_POST['recurring_slot_test_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['recurring_slot_test_nonce'] ) ), 'recurring_slot_test' ) ) {
return;
}
if ( ! isset( $_POST['submit_test'] ) ) {
return;
}
$this->process_test_form();
}
/**
* Process the test form.
*
* @throws Exception When validation fails or calculation errors occur.
*/
private function process_test_form() {
try {
// Validate nonce first.
if ( ! isset( $_POST['recurring_slot_test_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['recurring_slot_test_nonce'] ) ), 'recurring_slot_test' ) ) {
throw new Exception( 'Invalid nonce verification.' );
}
// Sanitize input data.
$subscription_id = isset( $_POST['subscription_id'] ) ? absint( $_POST['subscription_id'] ) : 0;
$start_date_string = isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : '';
$order_date_string = isset( $_POST['order_date'] ) ? sanitize_text_field( wp_unslash( $_POST['order_date'] ) ) : '';
// Validate required fields.
if ( empty( $subscription_id ) || empty( $start_date_string ) || empty( $order_date_string ) ) {
throw new Exception( 'Subscription ID, Start Date, and Order Date are required.' );
}
// Get subscription data.
if ( ! function_exists( 'wcs_get_subscription' ) ) {
throw new Exception( 'WooCommerce Subscriptions is not active.' );
}
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
throw new Exception( 'Subscription not found with ID: ' . $subscription_id );
}
// Get subscription period and interval.
$period = $subscription->get_billing_period();
$interval = $subscription->get_billing_interval();
// Get the anchor day from the subscription's parent order.
$parent_order = $subscription->get_parent();
if ( ! $parent_order ) {
throw new Exception( 'Parent order not found for subscription.' );
}
$order_meta_data = SubscriptionOrderMetaData::from_order( $parent_order );
$anchor_day = $order_meta_data->timeslot_anchor_day;
// Create DateTime objects.
$start_date = DateTime::createFromFormat( 'Y-m-d', $start_date_string, wp_timezone() );
$order_date = DateTime::createFromFormat( 'Y-m-d', $order_date_string, wp_timezone() );
if ( ! $start_date || ! $order_date ) {
throw new Exception( 'Invalid date format. Please use YYYY-MM-DD format.' );
}
// Create calculator instance.
$calculator_data = array(
'anchor_day' => $anchor_day,
'period' => $period,
'interval' => $interval,
'start_date' => $start_date,
'subscription_id' => $subscription_id,
);
$calculator = RecurringSlotCalculator::from_array( $calculator_data );
// Calculate delivery date.
$delivery_date = $calculator->calculate_adjusted_delivery_date( $order_date );
// Store result for display.
set_transient(
'recurring_slot_test_result',
array(
'success' => true,
'delivery_date' => $delivery_date ? $delivery_date->format( 'Y-m-d H:i:s' ) : null,
'input_data' => $calculator_data,
'subscription_id' => $subscription_id,
'start_date' => $start_date_string,
'order_date' => $order_date_string,
),
300
);
// Store form values for maintaining state.
set_transient(
'recurring_slot_test_form_values',
array(
'subscription_id' => $subscription_id,
'start_date' => $start_date_string,
'order_date' => $order_date_string,
),
300
);
} catch ( Exception $e ) {
// Store error for display.
set_transient(
'recurring_slot_test_result',
array(
'success' => false,
'error' => $e->getMessage(),
),
300
);
// Store form values even on error for maintaining state.
$subscription_id = isset( $_POST['subscription_id'] ) ? absint( $_POST['subscription_id'] ) : 0;
$start_date_string = isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : '';
$order_date_string = isset( $_POST['order_date'] ) ? sanitize_text_field( wp_unslash( $_POST['order_date'] ) ) : '';
set_transient(
'recurring_slot_test_form_values',
array(
'subscription_id' => $subscription_id,
'start_date' => $start_date_string,
'order_date' => $order_date_string,
),
300
);
}
// Redirect to avoid resubmission.
wp_redirect( admin_url( 'admin.php?page=recurring-slot-test&calculated=1' ) );
exit;
}
/**
* Get a sanitized POST value safely.
*
* @param string $key The POST key.
* @param mixed $default Default value.
* @return mixed The sanitized value or default.
*/
private function get_post_value( $key, $default = '' ) {
if ( ! isset( $_POST['recurring_slot_test_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['recurring_slot_test_nonce'] ) ), 'recurring_slot_test' ) ) {
return $default;
}
if ( ! isset( $_POST[ $key ] ) ) {
return $default;
}
if ( is_numeric( $default ) ) {
return absint( $_POST[ $key ] );
}
return sanitize_text_field( wp_unslash( $_POST[ $key ] ) );
}
/**
* Admin page callback.
*/
public function admin_page_callback() {
// Get saved form values from transient first, then fall back to POST or defaults.
$saved_values = get_transient( 'recurring_slot_test_form_values' );
if ( $saved_values ) {
$prev_subscription_id = $saved_values['subscription_id'];
$prev_start_date = $saved_values['start_date'];
$prev_order_date = $saved_values['order_date'];
} else {
// Fall back to POST values or defaults.
$prev_subscription_id = $this->get_post_value( 'subscription_id', 1 );
$prev_start_date = $this->get_post_value( 'start_date', gmdate( 'Y-m-d' ) );
$prev_order_date = $this->get_post_value( 'order_date', gmdate( 'Y-m-d' ) );
}
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<?php $this->display_result(); ?>
<div class="card" style="max-width: 800px;">
<h2>Test RecurringSlotCalculator</h2>
<form method="post" action="">
<?php wp_nonce_field( 'recurring_slot_test', 'recurring_slot_test_nonce' ); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="subscription_id">Subscription ID</label>
</th>
<td>
<input type="number" id="subscription_id" name="subscription_id"
value="<?php echo esc_attr( $prev_subscription_id ); ?>"
min="1" required class="regular-text" />
<p class="description">The subscription ID to get anchor day, period, and interval from</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="start_date">Subscription Start Date</label>
</th>
<td>
<input type="text" id="start_date" name="start_date"
value="<?php echo esc_attr( $prev_start_date ); ?>"
required class="regular-text datepicker" />
<p class="description">The first delivery date for the subscription (YYYY-MM-DD)</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="order_date">Recurring Order Creation Date</label>
</th>
<td>
<input type="text" id="order_date" name="order_date"
value="<?php echo esc_attr( $prev_order_date ); ?>"
required class="regular-text datepicker" />
<p class="description">The date when the recurring order is created (YYYY-MM-DD)</p>
</td>
</tr>
</table>
<?php submit_button( 'Calculate Delivery Date', 'primary', 'submit_test' ); ?>
</form>
</div>
<div class="card" style="max-width: 800px; margin-top: 20px;">
<h3>How it works</h3>
<p>This tool tests the <code>RecurringSlotCalculator::calculate_adjusted_delivery_date()</code> method using data from an actual subscription.</p>
<ul>
<li><strong>Subscription ID:</strong> The system will automatically extract the anchor day, period, and interval from this subscription</li>
<li><strong>Subscription Start Date:</strong> The first delivery date for the subscription (manually entered)</li>
<li><strong>Recurring Order Creation Date:</strong> The date when a new recurring order is created</li>
<li><strong>Anchor Day:</strong> Automatically retrieved from the subscription's parent order meta</li>
<li><strong>Period & Interval:</strong> Automatically retrieved from the subscription settings</li>
</ul>
</div>
</div>
<script>
jQuery(document).ready(function($) {
$('.datepicker').datepicker({
dateFormat: 'yy-mm-dd',
changeMonth: true,
changeYear: true
});
});
</script>
<style>
.card {
background: #fff;
border: 1px solid #ccd0d4;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
padding: 20px;
margin: 20px 0;
}
.result-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
border-radius: 4px;
padding: 15px;
margin: 20px 0;
}
.result-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
border-radius: 4px;
padding: 15px;
margin: 20px 0;
}
</style>
<?php
}
/**
* Display calculation result.
*/
private function display_result() {
if ( ! isset( $_GET['calculated'] ) ) {
return;
}
$result = get_transient( 'recurring_slot_test_result' );
if ( ! $result ) {
return;
}
// Delete the result transient after displaying, but keep form values.
delete_transient( 'recurring_slot_test_result' );
if ( $result['success'] ) {
?>
<div class="result-success">
<h3>✅ Calculation Result</h3>
<?php if ( $result['delivery_date'] ) : ?>
<p><strong>Calculated Delivery Date:</strong> <?php echo esc_html( $result['delivery_date'] ); ?></p>
<?php else : ?>
<p><strong>Result:</strong> No delivery date could be calculated (returned null)</p>
<?php endif; ?>
<details>
<summary>Input Parameters</summary>
<ul>
<li><strong>Subscription ID:</strong> <?php echo esc_html( $result['subscription_id'] ); ?></li>
<li><strong>Start Date:</strong> <?php echo esc_html( $result['start_date'] ); ?></li>
<li><strong>Order Date:</strong> <?php echo esc_html( $result['order_date'] ); ?></li>
<li><strong>Anchor Day:</strong> <?php echo esc_html( $result['input_data']['anchor_day'] ); ?> (from subscription)</li>
<li><strong>Period:</strong> <?php echo esc_html( $result['input_data']['period'] ); ?> (from subscription)</li>
<li><strong>Interval:</strong> <?php echo esc_html( $result['input_data']['interval'] ); ?> (from subscription)</li>
</ul>
</details>
</div>
<?php
} else {
?>
<div class="result-error">
<h3>❌ Error</h3>
<p><?php echo esc_html( $result['error'] ); ?></p>
</div>
<?php
}
}
}
// Initialize the plugin.
add_action( 'plugins_loaded', array( 'RecurringSlotTest\RecurringSlotTest', 'instance' ) );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment