Skip to content

Commit 36d534e

Browse files
committed
Add exact ratio distribution for deterministic coupon and refund generation
- Pre-generate exact counts of coupons/refunds based on ratios - Ensures 100 orders at 0.5 ratio = exactly 50 coupons (not 45-55) - Refunds split: 50% full, 25% partial, 25% multi-partial - Maintains backwards compatibility with single order generation - Only affects batch mode for predictable test data
1 parent e17884b commit 36d534e

File tree

1 file changed

+135
-30
lines changed

1 file changed

+135
-30
lines changed

includes/Generator/Order.php

Lines changed: 135 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@ class Order extends Generator {
3333
*/
3434
const SECOND_REFUND_MAX_DAYS = 30;
3535

36+
/**
37+
* Pre-generated coupon flags for exact ratio distribution in batch mode.
38+
* Each element is a boolean: true = apply coupon, false = skip.
39+
*
40+
* @var array|null
41+
*/
42+
protected static $batch_coupon_flags = null;
43+
44+
/**
45+
* Pre-generated refund flags for exact ratio distribution in batch mode.
46+
* Each element is a string: 'none', 'full', 'partial', or 'multi'.
47+
*
48+
* @var array|null
49+
*/
50+
protected static $batch_refund_flags = null;
51+
3652
/**
3753
* Return a new order.
3854
*
@@ -121,20 +137,26 @@ public static function generate( $save = true, $assoc_args = array() ) {
121137

122138
// Handle --coupon-ratio parameter
123139
if ( isset( $assoc_args['coupon-ratio'] ) ) {
124-
$coupon_ratio = floatval( $assoc_args['coupon-ratio'] );
140+
// Use exact ratio flag if in batch mode
141+
if ( null !== self::$batch_coupon_flags && ! empty( self::$batch_coupon_flags ) ) {
142+
$include_coupon = array_shift( self::$batch_coupon_flags );
143+
} else {
144+
// Fall back to probabilistic approach for single order generation
145+
$coupon_ratio = floatval( $assoc_args['coupon-ratio'] );
125146

126-
// Validate ratio is between 0.0 and 1.0
127-
if ( $coupon_ratio < 0.0 || $coupon_ratio > 1.0 ) {
128-
$coupon_ratio = max( 0.0, min( 1.0, $coupon_ratio ) );
129-
}
147+
// Validate ratio is between 0.0 and 1.0
148+
if ( $coupon_ratio < 0.0 || $coupon_ratio > 1.0 ) {
149+
$coupon_ratio = max( 0.0, min( 1.0, $coupon_ratio ) );
150+
}
130151

131-
// Apply coupon based on ratio
132-
if ( $coupon_ratio >= 1.0 ) {
133-
$include_coupon = true;
134-
} elseif ( $coupon_ratio > 0 && wp_rand( 1, 100 ) <= ( $coupon_ratio * 100 ) ) {
135-
$include_coupon = true;
136-
} else {
137-
$include_coupon = false;
152+
// Apply coupon based on ratio
153+
if ( $coupon_ratio >= 1.0 ) {
154+
$include_coupon = true;
155+
} elseif ( $coupon_ratio > 0 && wp_rand( 1, 100 ) <= ( $coupon_ratio * 100 ) ) {
156+
$include_coupon = true;
157+
} else {
158+
$include_coupon = false;
159+
}
138160
}
139161
}
140162

@@ -180,29 +202,43 @@ public static function generate( $save = true, $assoc_args = array() ) {
180202

181203
// Handle --refund-ratio parameter for completed orders
182204
if ( isset( $assoc_args['refund-ratio'] ) && 'completed' === $status ) {
183-
$refund_ratio = floatval( $assoc_args['refund-ratio'] );
205+
$refund_type = 'none';
184206

185-
// Validate ratio is between 0.0 and 1.0
186-
if ( $refund_ratio < 0.0 || $refund_ratio > 1.0 ) {
187-
$refund_ratio = max( 0.0, min( 1.0, $refund_ratio ) );
188-
}
207+
// Use exact ratio flag if in batch mode
208+
if ( null !== self::$batch_refund_flags && ! empty( self::$batch_refund_flags ) ) {
209+
$refund_type = array_shift( self::$batch_refund_flags );
210+
} else {
211+
// Fall back to probabilistic approach for single order generation
212+
$refund_ratio = floatval( $assoc_args['refund-ratio'] );
189213

190-
$should_refund = false;
214+
// Validate ratio is between 0.0 and 1.0
215+
if ( $refund_ratio < 0.0 || $refund_ratio > 1.0 ) {
216+
$refund_ratio = max( 0.0, min( 1.0, $refund_ratio ) );
217+
}
191218

192-
if ( $refund_ratio >= 1.0 ) {
193-
// Always refund if ratio is 1.0 or higher
194-
$should_refund = true;
195-
} elseif ( $refund_ratio > 0 && wp_rand( 1, 100 ) <= ( $refund_ratio * 100 ) ) {
196-
// Use random chance for ratios between 0 and 1
197-
$should_refund = true;
219+
if ( $refund_ratio >= 1.0 ) {
220+
// Always refund if ratio is 1.0 or higher
221+
$refund_type = 'full';
222+
} elseif ( $refund_ratio > 0 && wp_rand( 1, 100 ) <= ( $refund_ratio * 100 ) ) {
223+
// Use random chance for ratios between 0 and 1
224+
// Split evenly between full and partial
225+
$refund_type = (bool) wp_rand( 0, 1 ) ? 'full' : 'partial';
226+
227+
// 25% chance for multi-partial
228+
if ( 'partial' === $refund_type && wp_rand( 1, 100 ) <= self::SECOND_REFUND_PROBABILITY ) {
229+
$refund_type = 'multi';
230+
}
231+
}
198232
}
199233

200-
if ( $should_refund ) {
201-
// Create first refund with date within 2 months of completion
202-
$first_refund = self::create_refund( $order );
203-
204-
// Some partial refunds get a second refund (always partial)
205-
if ( $first_refund && is_object( $first_refund ) && wp_rand( 1, 100 ) <= self::SECOND_REFUND_PROBABILITY ) {
234+
// Process refund based on type
235+
if ( 'full' === $refund_type ) {
236+
self::create_refund( $order );
237+
} elseif ( 'partial' === $refund_type ) {
238+
self::create_refund( $order, true );
239+
} elseif ( 'multi' === $refund_type ) {
240+
$first_refund = self::create_refund( $order, true );
241+
if ( $first_refund && is_object( $first_refund ) ) {
206242
self::create_refund( $order, true, $first_refund );
207243
}
208244
}
@@ -236,6 +272,9 @@ public static function batch( $amount, array $args = array() ) {
236272
return $amount;
237273
}
238274

275+
// Initialize exact ratio flags for deterministic distribution
276+
self::init_ratio_flags( $amount, $args );
277+
239278
$order_ids = array();
240279

241280
for ( $i = 1; $i <= $amount; $i ++ ) {
@@ -247,6 +286,9 @@ public static function batch( $amount, array $args = array() ) {
247286
$order_ids[] = $order->get_id();
248287
}
249288

289+
// Clear ratio flags after batch generation
290+
self::clear_ratio_flags();
291+
250292
return $order_ids;
251293
}
252294

@@ -790,4 +832,67 @@ protected static function calculate_refund_date( $order, $previous_refund = null
790832
$random_days = wp_rand( 0, $max_days );
791833
return date( 'Y-m-d H:i:s', strtotime( $base_date->date( 'Y-m-d H:i:s' ) ) + ( $random_days * DAY_IN_SECONDS ) );
792834
}
835+
836+
/**
837+
* Initialize exact ratio flags for batch generation.
838+
* Creates pre-generated arrays for exact distribution of coupons and refunds.
839+
*
840+
* @param int $count Number of orders to generate.
841+
* @param array $args Arguments containing ratio parameters.
842+
* @return void
843+
*/
844+
protected static function init_ratio_flags( $count, $args ) {
845+
// Initialize coupon flags if coupon-ratio is set
846+
if ( isset( $args['coupon-ratio'] ) ) {
847+
$coupon_ratio = floatval( $args['coupon-ratio'] );
848+
$coupon_ratio = max( 0.0, min( 1.0, $coupon_ratio ) );
849+
850+
$num_with_coupons = (int) round( $count * $coupon_ratio );
851+
$num_without = $count - $num_with_coupons;
852+
853+
// Create array with exact counts
854+
self::$batch_coupon_flags = array_merge(
855+
array_fill( 0, $num_with_coupons, true ),
856+
array_fill( 0, $num_without, false )
857+
);
858+
859+
// Shuffle for randomness
860+
shuffle( self::$batch_coupon_flags );
861+
}
862+
863+
// Initialize refund flags if refund-ratio is set
864+
if ( isset( $args['refund-ratio'] ) && isset( $args['status'] ) && 'completed' === $args['status'] ) {
865+
$refund_ratio = floatval( $args['refund-ratio'] );
866+
$refund_ratio = max( 0.0, min( 1.0, $refund_ratio ) );
867+
868+
$total_refunds = (int) round( $count * $refund_ratio );
869+
870+
// Split refunds: 50% full, 25% single partial, 25% multi-partial
871+
$num_full = (int) round( $total_refunds * 0.5 );
872+
$num_partial = (int) round( $total_refunds * 0.25 );
873+
$num_multi = $total_refunds - $num_full - $num_partial; // Remainder goes to multi
874+
$num_none = $count - $total_refunds;
875+
876+
// Create array with exact counts
877+
self::$batch_refund_flags = array_merge(
878+
array_fill( 0, $num_full, 'full' ),
879+
array_fill( 0, $num_partial, 'partial' ),
880+
array_fill( 0, $num_multi, 'multi' ),
881+
array_fill( 0, $num_none, 'none' )
882+
);
883+
884+
// Shuffle for randomness
885+
shuffle( self::$batch_refund_flags );
886+
}
887+
}
888+
889+
/**
890+
* Clear ratio flags after batch generation is complete.
891+
*
892+
* @return void
893+
*/
894+
protected static function clear_ratio_flags() {
895+
self::$batch_coupon_flags = null;
896+
self::$batch_refund_flags = null;
897+
}
793898
}

0 commit comments

Comments
 (0)