@@ -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