hardening 2.0: 2meet-data-optimizer-woocommerce-addon (staged rollout, dev22 first) #1
@@ -6,8 +6,9 @@
|
|||||||
* `wp_wpdo_wc_commissions` table. HPOS-aware (uses `wc_get_order()`).
|
* `wp_wpdo_wc_commissions` table. HPOS-aware (uses `wc_get_order()`).
|
||||||
*
|
*
|
||||||
* Trigger: `woocommerce_order_status_changed` → on `processing`/`completed`
|
* Trigger: `woocommerce_order_status_changed` → on `processing`/`completed`
|
||||||
* read order's vendor meta (`_hp_vendor`, `_hp_commission`, etc.)
|
* read hivepress-marketplace's order meta (`hp_vendor`,
|
||||||
* and INSERT/UPDATE `wp_wpdo_wc_commissions`.
|
* `hp_commission_rate`, `hp_commission_fee`) and INSERT/UPDATE
|
||||||
|
* `wp_wpdo_wc_commissions`.
|
||||||
*
|
*
|
||||||
* Reads from order via `wc_get_order()` — works for both HPOS and legacy.
|
* Reads from order via `wc_get_order()` — works for both HPOS and legacy.
|
||||||
*
|
*
|
||||||
@@ -76,7 +77,7 @@ class TMDO_WC_Orders_Interceptor extends TMDO_Interceptor_Base {
|
|||||||
/**
|
/**
|
||||||
* INSERT/UPDATE wp_wpdo_wc_commissions for the given order.
|
* INSERT/UPDATE wp_wpdo_wc_commissions for the given order.
|
||||||
*
|
*
|
||||||
* Skips orders without a `_hp_vendor` meta — only marketplace orders count.
|
* Skips orders without an `hp_vendor` meta — only marketplace orders count.
|
||||||
*
|
*
|
||||||
* @param int $wc_order_id WooCommerce order ID.
|
* @param int $wc_order_id WooCommerce order ID.
|
||||||
* @param string $status Order status.
|
* @param string $status Order status.
|
||||||
@@ -91,18 +92,30 @@ class TMDO_WC_Orders_Interceptor extends TMDO_Interceptor_Base {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$vendor_id = (int) $order->get_meta( '_hp_vendor' );
|
$vendor_post_id = (int) $order->get_meta( 'hp_vendor' );
|
||||||
if ( $vendor_id <= 0 ) {
|
if ( $vendor_post_id <= 0 ) {
|
||||||
// Not a marketplace order — skip.
|
// Not a marketplace order — skip.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$listing_id = (int) $order->get_meta( '_hp_listing' );
|
// hivepress-marketplace stores the vendor's share (0.9 for a 10% commission) and
|
||||||
$subtotal = (float) ( $order->get_meta( '_hp_subtotal' ) ?: 0 );
|
// a flat fee, and pays vendors net_total * share - fee (Marketplace::get_order_profit()).
|
||||||
$commission = (float) ( $order->get_meta( '_hp_commission' ) ?: 0 );
|
$vendor_share = (float) $order->get_meta( 'hp_commission_rate' );
|
||||||
$vendor_payout = (float) ( $order->get_meta( '_hp_vendor_payout' ) ?: 0 );
|
$fee = (float) $order->get_meta( 'hp_commission_fee' );
|
||||||
$commission_rate = (float) ( $order->get_meta( '_hp_commission_rate' ) ?: 0 );
|
$subtotal = (float) $order->get_total() - (float) $order->get_total_refunded();
|
||||||
$hpos_enabled = class_exists( 'TMDO_WooCommerce' ) && TMDO_WooCommerce::is_hpos_enabled() ? 1 : 0;
|
if ( ! get_option( 'hp_vendor_include_taxes' ) ) {
|
||||||
|
$subtotal -= (float) $order->get_total_tax();
|
||||||
|
}
|
||||||
|
$vendor_payout = max( 0.0, $subtotal * $vendor_share - $fee );
|
||||||
|
$commission = $subtotal - $vendor_payout;
|
||||||
|
$commission_rate = round( ( 1 - $vendor_share ) * 100, 2 ); // Platform percentage, as set in Marketplace.
|
||||||
|
|
||||||
|
// vendor_id holds the vendor's user ID (the admin screen resolves it with get_userdata());
|
||||||
|
// the listing is the product's parent, as in Marketplace::get_product_listing().
|
||||||
|
$vendor_id = (int) get_post_field( 'post_author', $vendor_post_id );
|
||||||
|
$item = current( $order->get_items() );
|
||||||
|
$listing_id = $item ? (int) get_post_field( 'post_parent', $item->get_product_id() ) : 0;
|
||||||
|
$hpos_enabled = class_exists( 'TMDO_WooCommerce' ) && TMDO_WooCommerce::is_hpos_enabled() ? 1 : 0;
|
||||||
|
|
||||||
$table = TMDO_DB::table( 'wpdo_wc_commissions' );
|
$table = TMDO_DB::table( 'wpdo_wc_commissions' );
|
||||||
$now = TMDO_DB::now();
|
$now = TMDO_DB::now();
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<?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
|
||||||
|
) {}
|
||||||
|
|
||||||
|
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' );
|
||||||
|
}
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user