45, 'processing' => 20, 'on-hold' => 8, 'pending' => 7, 'cancelled' => 7, 'failed' => 5, 'refunded' => 5, ); /** * Statuses that count as paid (date paid, sales recorded, paying customer). */ const PAID = array( 'processing', 'completed', 'refunded' ); /** * Statuses whose sales WooCommerce records (on-hold too: stock is held). */ const SALES = array( 'processing', 'completed', 'on-hold', 'refunded' ); /** * Share of completed orders that get a partial refund. */ const PARTIAL_REFUND = 5; /** * Share of orders that use a coupon (when there are coupons). */ const COUPON_SHARE = 20; /** * Most variations per variable product (and the hard maximum). */ const MAX_VARIATIONS = 12; const HARD_VARIATIONS = 25; /** * Status weights after a filter: only core statuses with a positive whole weight; the * defaults when nothing usable is left. * * @since 1.0.0 * @param mixed $filtered Output of the `apwc_test_data_order_statuses` filter. * @return array */ public static function statuses( $filtered ): array { if ( ! is_array( $filtered ) ) { return self::STATUSES; } $out = array(); foreach ( $filtered as $status => $weight ) { $status = is_string( $status ) ? preg_replace( '/^wc-/', '', $status ) : ''; if ( isset( self::STATUSES[ $status ] ) && is_numeric( $weight ) && (int) $weight > 0 ) { $out[ $status ] = min( 1000000, (int) $weight ); } } return $out ? $out : self::STATUSES; } /** * Whether product number $index is variable, spreading the share evenly over the run * (share 40 → items 2, 4, 7, 9 … of every ten-ish are variable). * * @since 1.0.0 * @param int $index Product index (0-based). * @param int $share Percent variable, 0–100. * @return bool */ public static function is_variable( int $index, int $share ): bool { $share = max( 0, min( 100, $share ) ); return intdiv( ( $index + 1 ) * $share, 100 ) > intdiv( $index * $share, 100 ); } /** * A timestamp between two times, from a roll in [0, 1). * * @since 1.0.0 * @param float $roll Roll. * @param int $from Earliest. * @param int $to Latest (earliest is used when it's before). * @return int */ public static function date_in( float $roll, int $from, int $to ): int { if ( $to <= $from ) { return $from; } $roll = max( 0.0, min( 1.0, $roll ) ); return $from + (int) floor( $roll * ( $to - $from ) ); } /** * A money amount in a range, rounded to the store's decimals. * * @since 1.0.0 * @param Random $random Generator. * @param float $min Lowest. * @param float $max Highest. * @param int $decimals Decimals (0–4). * @return float */ public static function money( Random $random, float $min, float $max, int $decimals ): float { $decimals = max( 0, min( 4, $decimals ) ); $value = round( $min + $random->float() * ( $max - $min ), $decimals ); return max( $min, min( $max, $value ) ); } /** * A product. * * @since 1.0.0 * @param Random $random Generator. * @param int $index Product index. * @param int $share Percent variable. * @param int $decimals Price decimals. * @return array */ public static function product( Random $random, int $index, int $share, int $decimals ): array { $price = self::money( $random, 5, 200, $decimals ); $sale = $random->chance( 30 ) ? round( $price * ( 0.7 + 0.2 * $random->float() ), max( 0, min( 4, $decimals ) ) ) : null; $stock = $random->chance( 70 ); $qty = $stock ? ( $random->chance( 15 ) ? $random->int( 0, 3 ) : $random->int( 4, 100 ) ) : null; $ship = $random->chance( 80 ); $plan = array( 'variable' => self::is_variable( $index, $share ), 'price' => $price, 'sale' => null !== $sale && $sale < $price && $sale > 0 ? $sale : null, 'manage' => $stock, 'stock' => $qty, 'virtual' => $random->chance( 10 ), 'weight' => $ship ? round( 0.1 + $random->float() * 4.9, 2 ) : null, 'dimensions' => $ship ? array( $random->int( 5, 60 ), $random->int( 5, 60 ), $random->int( 2, 40 ) ) : null, 'variations' => $random->int( 2, self::MAX_VARIATIONS ), 'age_days' => $random->int( 1, 30 ), ); if ( $plan['virtual'] ) { $plan['weight'] = null; $plan['dimensions'] = null; } return $plan; } /** * A customer. * * @since 1.0.0 * @param Random $random Generator. * @param int $from Window start. * @param int $to Window end. * @param int $base_share Percent at the store's base country. * @return array */ public static function customer( Random $random, int $from, int $to, int $base_share = 70 ): array { return array( 'registered' => self::date_in( $random->float(), $from, $to ), 'base_country' => $random->chance( $base_share ), 'ship_elsewhere' => $random->chance( 15 ), 'company' => $random->chance( 20 ), ); } /** * An order. * * Context keys: `statuses` (weights), `guest_share` (0–100), `customers`, `products`, * `coupons` (pool sizes). * * @since 1.0.0 * @param Random $random Generator. * @param array $ctx Context. * @return array */ public static function order( Random $random, array $ctx ): array { $statuses = is_array( $ctx['statuses'] ?? null ) && $ctx['statuses'] ? $ctx['statuses'] : self::STATUSES; $status = $random->weighted( $statuses ); $status = '' === $status ? 'completed' : $status; $customers = max( 0, (int) ( $ctx['customers'] ?? 0 ) ); $products = max( 0, (int) ( $ctx['products'] ?? 0 ) ); $coupons = max( 0, (int) ( $ctx['coupons'] ?? 0 ) ); $guest = 0 === $customers || $random->chance( (float) ( $ctx['guest_share'] ?? 30 ) ); $items = array(); $lines = $products ? $random->int( 1, min( 4, $products ) ) : 0; $used = array(); for ( $i = 0; $i < $lines; $i++ ) { $pick = $random->int( 0, $products - 1 ); // Distinct products per order where possible (a few tries, then accept a repeat). for ( $try = 0; isset( $used[ $pick ] ) && $try < 3; $try++ ) { $pick = $random->int( 0, $products - 1 ); } $used[ $pick ] = true; $items[] = array( 'product' => $pick, 'qty' => $random->int( 1, 3 ), 'variation' => $random->float(), ); } $refund = ''; if ( 'refunded' === $status ) { $refund = 'full'; } elseif ( 'completed' === $status && $random->chance( self::PARTIAL_REFUND ) ) { $refund = 'partial'; } return array( 'status' => $status, 'guest' => $guest, 'customer' => $guest ? -1 : $random->int( 0, $customers - 1 ), 'date_roll' => $random->float(), 'items' => $items, 'coupon' => $coupons && $random->chance( self::COUPON_SHARE ) ? $random->int( 0, $coupons - 1 ) : -1, 'free_shipping' => $random->chance( 30 ), 'shipping' => $random->int( 5, 15 ), 'payment' => (string) $random->pick( array( 'bacs', 'cheque', 'cod' ) ), 'refund' => $refund, 'refund_share' => $random->int( 10, 50 ), 'refund_days' => $random->int( 1, 10 ), 'paid_minutes' => $random->int( 1, 120 ), 'done_days' => $random->int( 1, 5 ), ); } /** * A review. * * @since 1.0.0 * @param Random $random Generator. * @return array */ public static function review( Random $random ): array { return array( 'rating' => (int) $random->weighted( array( '5' => 45, '4' => 30, '3' => 12, '2' => 6, '1' => 7, ) ), 'approved' => $random->chance( 90 ), 'date_roll' => $random->float(), ); } }