|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 '';
}
}