meta key => amount. * * @var array> */ private static $pending = array(); /** * Whether changes are being gathered. * * @var bool */ private static $buffering = false; /** * Start gathering changes (a step starts). * * @since 1.0.0 * @return void */ public static function buffer(): void { self::$buffering = true; } /** * Add to a counter of a test product or coupon (never on other objects). * * @since 1.0.0 * @param int $id Post ID. * @param string $key Marker::SALES or Marker::USAGE. * @param int $by Amount (negative to take off). * @return void */ public static function add( int $id, string $key, int $by ): void { if ( $id <= 0 || 0 === $by ) { return; } self::$pending[ $id ][ $key ] = ( self::$pending[ $id ][ $key ] ?? 0 ) + $by; if ( ! self::$buffering ) { self::flush(); } } /** * Write the gathered changes and stop gathering. * * @since 1.0.0 * @return void */ public static function flush(): void { $pending = self::$pending; self::$pending = array(); self::$buffering = false; foreach ( $pending as $id => $keys ) { if ( '' === (string) get_post_meta( $id, Marker::META, true ) ) { continue; // Only test objects carry our counters. } foreach ( $keys as $key => $by ) { update_post_meta( $id, $key, max( 0, (int) get_post_meta( $id, $key, true ) + $by ) ); } } } /** * `woocommerce_recorded_sales`: WooCommerce just recorded (or took back) an order's sales; when * the order is a signed test order, do the same on our count, so staff cancelling a paid test * order doesn't hide a later real sale. * * @since 1.0.0 * @param mixed $order_id Order ID. * @return void */ public static function recorded_sales( $order_id ): void { $order = wc_get_order( (int) $order_id ); if ( ! $order instanceof \WC_Order ) { return; } // Every paid order on the store passes here: read the loaded order's own meta first. $batch = (string) $order->get_meta( Marker::META ); if ( ! Marker::valid_batch( $batch ) || ! Marker::valid_signature( 'order', (int) $order->get_id(), $batch, $order->get_meta( Marker::SIG ) ) ) { return; } $sign = $order->get_data_store()->get_recorded_sales( $order ) ? 1 : -1; foreach ( $order->get_items() as $item ) { if ( $item instanceof \WC_Order_Item_Product && $item->get_product_id() ) { self::add( (int) $item->get_product_id(), Marker::SALES, $sign * (int) $item->get_quantity() ); } } } }