|null */ private $cache = null; /** * Shared repository for this request. * * @var OrderRiskRepository|null */ private $repository = null; /** * Register column hooks. * * @return void */ public function register() { // HPOS orders table. add_filter( 'woocommerce_shop_order_list_table_columns', array( $this, 'add_column' ) ); add_action( 'woocommerce_shop_order_list_table_custom_column', array( $this, 'render_hpos_column' ), 10, 2 ); add_action( 'woocommerce_order_list_table_extra_tablenav', array( $this, 'prefetch_hpos' ), 5, 2 ); // Legacy CPT orders. add_filter( 'manage_edit-shop_order_columns', array( $this, 'add_column' ) ); add_action( 'manage_shop_order_posts_custom_column', array( $this, 'render_legacy_column' ), 10, 2 ); add_filter( 'the_posts', array( $this, 'prefetch_legacy' ), 10, 2 ); } /** * Prefetch risk rows for HPOS list (after prepare_items; items are public on WP_List_Table). * * @param string $order_type Order type. * @param string $which top|bottom. * @return void */ public function prefetch_hpos( $order_type, $which ) { if ( ! Capabilities::current_user_can_manage() ) { return; } if ( 'top' !== $which || 'shop_order' !== $order_type || null !== $this->cache ) { return; } if ( ! function_exists( 'wc_get_container' ) ) { return; } try { $table = wc_get_container()->get( \Automattic\WooCommerce\Internal\Admin\Orders\ListTable::class ); } catch ( \Throwable $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch return; } if ( ! $table || empty( $table->items ) || ! is_array( $table->items ) ) { return; } $ids = array(); foreach ( $table->items as $order ) { if ( $order instanceof \WC_Order ) { $ids[] = (int) $order->get_id(); } } $this->warm( $ids ); } /** * Prefetch risk rows for legacy shop_order list query. * * @param array $posts Posts. * @param \WP_Query $query Query. * @return array */ public function prefetch_legacy( $posts, $query ) { if ( ! Capabilities::current_user_can_manage() ) { return $posts; } if ( null !== $this->cache || ! is_admin() || ! $query instanceof \WP_Query ) { return $posts; } if ( empty( $query->query_vars['post_type'] ) || 'shop_order' !== $query->query_vars['post_type'] ) { return $posts; } if ( ! is_array( $posts ) || empty( $posts ) ) { return $posts; } $ids = array(); foreach ( $posts as $post ) { if ( is_object( $post ) && isset( $post->ID ) ) { $ids[] = (int) $post->ID; } } $this->warm( $ids ); return $posts; } /** * @param array $columns Columns. * @return array */ public function add_column( $columns ) { // Defense-in-depth: hide Promo Risk column from non-merchant roles. // WooCommerce order screens are not strictly role-gated for all logged-in users. if ( ! Capabilities::current_user_can_manage() ) { return $columns; } $new = array(); foreach ( $columns as $key => $label ) { $new[ $key ] = $label; if ( 'order_status' === $key ) { $new['aksmb_promo_risk'] = __( 'Promo Risk', 'marginbeacon' ); } } if ( ! isset( $new['aksmb_promo_risk'] ) ) { $new['aksmb_promo_risk'] = __( 'Promo Risk', 'marginbeacon' ); } return $new; } /** * @param string $column Column key. * @param \WC_Order $order Order. * @return void */ public function render_hpos_column( $column, $order ) { if ( 'aksmb_promo_risk' !== $column || ! Capabilities::current_user_can_manage() ) { return; } $this->render_badge( (int) $order->get_id() ); } /** * @param string $column Column. * @param int $post_id Post ID. * @return void */ public function render_legacy_column( $column, $post_id ) { if ( 'aksmb_promo_risk' !== $column || ! Capabilities::current_user_can_manage() ) { return; } $this->render_badge( (int) $post_id ); } /** * @param int[] $order_ids Order IDs. * @return void */ private function warm( array $order_ids ) { $order_ids = array_values( array_unique( array_filter( array_map( 'intval', $order_ids ) ) ) ); if ( empty( $order_ids ) ) { $this->cache = array(); return; } $this->cache = $this->repo()->get_current_for_orders( $order_ids ); } /** * @param int $order_id Order ID. * @return array|null */ private function row_for( $order_id ) { $order_id = (int) $order_id; if ( null === $this->cache ) { $this->warm( array( $order_id ) ); } elseif ( ! array_key_exists( $order_id, $this->cache ) ) { $extra = $this->repo()->get_current_for_orders( array( $order_id ) ); $this->cache[ $order_id ] = isset( $extra[ $order_id ] ) ? $extra[ $order_id ] : null; } return isset( $this->cache[ $order_id ] ) ? $this->cache[ $order_id ] : null; } /** * @return OrderRiskRepository */ private function repo() { if ( null === $this->repository ) { $this->repository = new OrderRiskRepository(); } return $this->repository; } /** * @param int $order_id Order ID. * @return void */ private function render_badge( $order_id ) { $row = $this->row_for( $order_id ); if ( ! $row ) { echo '' . esc_html__( 'Not evaluated', 'marginbeacon' ) . ''; return; } $presentation = $row['presentation_state']; if ( 'pending' === $presentation ) { echo '' . esc_html__( 'Pending', 'marginbeacon' ) . ''; return; } if ( 'no_promotion' === $presentation ) { echo '' . esc_html__( 'No promotion', 'marginbeacon' ) . ''; return; } if ( 'unsupported' === $presentation ) { echo '' . esc_html__( 'Unsupported', 'marginbeacon' ) . ''; return; } if ( 'failed' === $presentation ) { echo '' . esc_html__( 'Failed', 'marginbeacon' ) . ''; return; } $badge = $row['risk_badge'] ? $row['risk_badge'] : 'unknown'; $label = Severity::label( (string) $badge ); $coverage = array(); if ( ! empty( $row['coverage_json'] ) ) { $decoded = json_decode( (string) $row['coverage_json'], true ); if ( is_array( $decoded ) ) { $coverage = $decoded; } } if ( 'safe' === $badge && FreeShippingScope::coverage_excludes_shipping( $coverage ) ) { $label = FreeShippingScope::safe_label_short(); } $dual = DualContextPresenter::from_row( $row ); $summary = $dual['summary']; $refunded = OrderRefundLifecycle::should_show_label( $row, null ); echo ''; printf( '%2$s', esc_attr( $badge ), esc_html( $label ) ); if ( $summary ) { printf( '%2$s', esc_attr( $summary ), esc_html( $summary ) ); } if ( $refunded ) { printf( '%2$s', esc_attr( OrderRefundLifecycle::label() ), esc_html__( 'Refunded', 'marginbeacon' ) ); } if ( ! empty( $row['evaluation_origin'] ) && 'recent_preview' === $row['evaluation_origin'] ) { echo '' . esc_html__( 'Preview', 'marginbeacon' ) . ''; } if ( PaymentResultState::is_preliminary( PaymentResultState::from_row( $row ) ) ) { printf( '%2$s', esc_attr( PaymentResultState::preliminary_note() ), esc_html( PaymentResultState::preliminary_note() ) ); } echo ''; } }