Files
2meet-data-optimizer-woocom…/tests/integration/WCOrdersInterceptorTest.php
T
wpdev 50989396a8 fix(2meet-data-optimizer-woocommerce-addon): recompute commission amounts on every tracked status change and refund
- the commission upsert replaces subtotal, commission, vendor_payout and
  commission_rate on conflict (it kept the first amounts, so a partial refund
  before completion never reached the row)
- woocommerce_order_refunded recomputes a tracked or already recorded order;
  a fully refunded order keeps its row at zero, untracked orders get none
- Verify: WCOrdersInterceptorTest (5 new), integration 27, unit 11, phpcs;
  dev36 flow_wc partial refund -> completed
- PR: PR-092
2026-09-27 16:29:28 +08:00

222 lines
7.7 KiB
PHP

<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
// ── Minimal WooCommerce stand-ins: only what the interceptor calls ─────────
if ( ! class_exists( 'TMDO_Test_WC_Order_Item' ) ) {
final class TMDO_Test_WC_Order_Item {
public function __construct( private int $product_id ) {}
public function get_product_id(): int {
return $this->product_id;
}
}
}
if ( ! class_exists( 'TMDO_Test_WC_Order' ) ) {
final class TMDO_Test_WC_Order {
/**
* @param array<string, mixed> $meta
*/
public function __construct(
private array $meta,
private float $total,
private float $tax,
private float $refunded,
private int $product_id,
private string $status = 'processing'
) {}
public function get_status(): string {
return $this->status;
}
public function get_meta( string $key ) {
return $this->meta[ $key ] ?? '';
}
public function get_total(): float {
return $this->total;
}
public function get_total_tax(): float {
return $this->tax;
}
public function get_total_refunded(): float {
return $this->refunded;
}
public function get_items(): array {
return array( new TMDO_Test_WC_Order_Item( $this->product_id ) );
}
}
}
if ( ! function_exists( 'wc_get_order' ) ) {
function wc_get_order( $order_id ) {
return $GLOBALS['_tmdo_test_orders'][ (int) $order_id ] ?? false;
}
}
if ( ! function_exists( 'get_post_field' ) ) {
function get_post_field( string $field, $post ) {
return $GLOBALS['_tmdo_test_post_fields'][ (int) $post ][ $field ] ?? '';
}
}
/**
* Integration test: commission rows follow hivepress-marketplace's real order meta.
*
* Marketplace writes `hp_vendor` (vendor post ID), `hp_commission_rate` (vendor
* share, e.g. 0.9 for a 10% commission) and `hp_commission_fee` (flat fee), and
* pays vendors net_total * rate - fee (HivePress\Components\Marketplace::get_order_profit()).
*/
class WCOrdersInterceptorTest extends TestCase {
private const ORDER_ID = 9001;
private const VENDOR_POST = 7;
private const VENDOR_USER = 42;
private const PRODUCT_ID = 900;
private const LISTING_ID = 555;
public static function setUpBeforeClass(): void {
TMDO_Installer::install_v2_tables();
}
protected function setUp(): void {
global $wpdb;
$wpdb->prefix = 'wp_itest_';
$wpdb->options = 'wp_itest_options';
$wpdb->query( 'DELETE FROM `wp_itest_wpdo_wc_commissions` WHERE wc_order_id = ' . self::ORDER_ID );
delete_option( 'hp_vendor_include_taxes' );
TMDO_Feature_Flags::set( 'wc_orders', 'dual_write' );
$GLOBALS['_tmdo_test_post_fields'] = array(
self::VENDOR_POST => array( 'post_author' => self::VENDOR_USER ),
self::PRODUCT_ID => array( 'post_parent' => self::LISTING_ID ),
);
}
protected function tearDown(): void {
TMDO_Feature_Flags::set( 'wc_orders', 'idle' );
unset( $GLOBALS['_tmdo_test_orders'], $GLOBALS['_tmdo_test_post_fields'] );
}
private function place_order( array $meta, float $total, float $tax = 0.0, float $refunded = 0.0 ): void {
$GLOBALS['_tmdo_test_orders'][ self::ORDER_ID ] = new TMDO_Test_WC_Order( $meta, $total, $tax, $refunded, self::PRODUCT_ID );
( new TMDO_WC_Orders_Interceptor() )->action_order_status_changed( self::ORDER_ID, 'pending', 'processing' );
}
/** The same order after a refund (and, for a full refund, a new status). */
private function refund( float $refunded, string $status ): void {
$GLOBALS['_tmdo_test_orders'][ self::ORDER_ID ] = new TMDO_Test_WC_Order( $this->marketplace_meta(), 110.0, 10.0, $refunded, self::PRODUCT_ID, $status );
}
private function commission_row(): ?array {
global $wpdb;
return $wpdb->get_row( 'SELECT * FROM `wp_itest_wpdo_wc_commissions` WHERE wc_order_id = ' . self::ORDER_ID, ARRAY_A );
}
private function marketplace_meta(): array {
return array(
'hp_vendor' => self::VENDOR_POST,
'hp_commission_rate' => 0.9,
'hp_commission_fee' => 5,
);
}
public function test_records_commission_from_marketplace_meta(): void {
$this->place_order( $this->marketplace_meta(), 110.0, 10.0 );
$row = $this->commission_row();
$this->assertNotNull( $row );
$this->assertSame( self::VENDOR_USER, (int) $row['vendor_id'] );
$this->assertSame( self::LISTING_ID, (int) $row['listing_id'] );
$this->assertEqualsWithDelta( 100.0, (float) $row['subtotal'], 0.0001 );
$this->assertEqualsWithDelta( 85.0, (float) $row['vendor_payout'], 0.0001 );
$this->assertEqualsWithDelta( 15.0, (float) $row['commission'], 0.0001 );
$this->assertEqualsWithDelta( 10.0, (float) $row['commission_rate'], 0.0001 );
}
public function test_includes_taxes_when_marketplace_setting_is_on(): void {
update_option( 'hp_vendor_include_taxes', '1' );
$this->place_order( $this->marketplace_meta(), 110.0, 10.0 );
$row = $this->commission_row();
$this->assertEqualsWithDelta( 110.0, (float) $row['subtotal'], 0.0001 );
$this->assertEqualsWithDelta( 94.0, (float) $row['vendor_payout'], 0.0001 );
$this->assertEqualsWithDelta( 16.0, (float) $row['commission'], 0.0001 );
}
public function test_skips_orders_without_marketplace_vendor(): void {
$this->place_order( array(), 110.0 );
$this->assertNull( $this->commission_row() );
}
public function test_vendor_payout_never_goes_negative(): void {
$this->place_order( $this->marketplace_meta(), 3.0 );
$row = $this->commission_row();
$this->assertEqualsWithDelta( 0.0, (float) $row['vendor_payout'], 0.0001 );
$this->assertEqualsWithDelta( 3.0, (float) $row['commission'], 0.0001 );
}
public function test_partial_refund_then_completion_records_the_refunded_amounts(): void {
$this->place_order( $this->marketplace_meta(), 110.0, 10.0 );
$this->refund( 50.0, 'processing' );
$interceptor = new TMDO_WC_Orders_Interceptor();
$interceptor->action_order_status_changed( self::ORDER_ID, 'processing', 'completed' );
$interceptor->action_order_status_changed( self::ORDER_ID, 'processing', 'completed' ); // Duplicate event.
global $wpdb;
$this->assertSame( '1', (string) $wpdb->get_var( 'SELECT COUNT(*) FROM `wp_itest_wpdo_wc_commissions` WHERE wc_order_id = ' . self::ORDER_ID ) );
$row = $this->commission_row();
$this->assertSame( 'completed', $row['status'] );
$this->assertEqualsWithDelta( 50.0, (float) $row['subtotal'], 0.0001, '110 - 50 refunded - 10 tax' );
$this->assertEqualsWithDelta( 40.0, (float) $row['vendor_payout'], 0.0001 );
$this->assertEqualsWithDelta( 10.0, (float) $row['commission'], 0.0001 );
}
public function test_a_refund_recomputes_the_commission_without_a_status_change(): void {
$this->place_order( $this->marketplace_meta(), 110.0, 10.0 );
$this->refund( 50.0, 'processing' );
( new TMDO_WC_Orders_Interceptor() )->action_order_refunded( self::ORDER_ID, 1 );
$this->assertEqualsWithDelta( 50.0, (float) $this->commission_row()['subtotal'], 0.0001 );
}
public function test_a_full_refund_zeroes_a_recorded_commission(): void {
$this->place_order( $this->marketplace_meta(), 110.0, 10.0 );
$this->refund( 110.0, 'refunded' );
( new TMDO_WC_Orders_Interceptor() )->action_order_refunded( self::ORDER_ID, 1 );
$row = $this->commission_row();
$this->assertSame( 'refunded', $row['status'] );
$this->assertEqualsWithDelta( 0.0, (float) $row['vendor_payout'], 0.0001 );
}
public function test_a_refund_of_an_untracked_order_records_nothing(): void {
$this->refund( 50.0, 'pending' );
( new TMDO_WC_Orders_Interceptor() )->action_order_refunded( self::ORDER_ID, 1 );
$this->assertNull( $this->commission_row() );
}
public function test_refunds_are_hooked(): void {
$interceptor = new TMDO_WC_Orders_Interceptor();
$interceptor->register_hooks();
$this->assertContains( array( $interceptor, 'action_order_refunded' ), $GLOBALS['_wp_filter_callbacks']['woocommerce_order_refunded'] ?? array() );
}
}