container = $this->getContainer(); $this->cart = $this->cart(); $this->customer = $this->customer(); $this->session = $this->session(); $this->puFactory = $this->container->get( 'api.factory.purchase-unit' ); assert($this->puFactory instanceof PurchaseUnitFactory); add_filter('woocommerce_get_base_location', function () { return 'AQ'; }); } public function tearDown(): void { foreach ($this->postIds as $id) { wp_delete_post($id); } $this->cart->empty_cart(); parent::tearDown(); } /** * @dataProvider orderData */ public function testOrder(array $orderData, array $expectedAmount) { $wcOrder = $this->createWcOrder($orderData); $pu = $this->puFactory->from_wc_order($wcOrder); $puData = $pu->to_array(); self::assertEquals($expectedAmount, $puData['amount']); } /** * @dataProvider cartData */ public function testCart(array $cartData, array $expectedAmount) { $this->fillWcCart($this->cart, $this->customer, $this->session, $cartData); $pu = $this->puFactory->from_wc_cart($this->cart); $puData = $pu->to_array(); self::assertEquals($expectedAmount, $puData['amount']); } protected function createWcOrder(array $data): WC_Order { $wcOrder = new WC_Order(); $wcOrder->set_currency( $data['currency'] ?? self::CURRENCY); $wcOrder->set_prices_include_tax($data['prices_include_tax'] ?? true); foreach ($data['items'] as $itemData) { $item = new WC_Order_Item_Product(); $item->set_name($itemData['name'] ?? 'Test product'); $item->set_quantity($itemData['quantity'] ?? 1); $item->set_total((string) ($itemData['price'] * $itemData['quantity'] ?? 1)); if (isset($itemData['product'])) { $product = $this->createWcProduct($itemData['product']); $item->set_product($product); } $wcOrder->add_item($item); } $wcOrder->set_address(array_merge([ 'first_name' => 'John', 'last_name' => 'Doe', 'company' => '', 'email' => 'jd@example.com', 'phone' => '1234567890', 'address_1' => '123 st', 'address_2' => '', 'city' => 'city0', 'state' => 'state0', 'country' => 'AQ', 'postcode' => '12345', ], $data['billing'] ?? [])); if (isset($data['shipping'])) { $shipping = new WC_Order_Item_Shipping(); $shipping->set_total((string) $data['shipping']['total']); $wcOrder->add_item($shipping); } foreach ($data['fees'] ?? [] as $ind => $feeData) { $fee = new WC_Order_Item_Fee(); $fee->set_name("Test fee $ind"); $fee->set_amount((string) $feeData['amount']); $fee->set_total((string) $feeData['amount']); $fee->set_tax_class(''); $fee->set_tax_status('taxable'); $wcOrder->add_item($fee); } $wcOrder->calculate_totals(); $wcOrder->save(); $this->postIds[] = $wcOrder->get_id(); foreach ($data['coupons'] ?? [] as $couponData) { $coupon = $this->createWcCoupon($couponData); $ret = $wcOrder->apply_coupon($coupon); if (is_wp_error($ret)) { throw new Exception('Incorrect coupon. ' . $ret->get_error_message()); } } $wcOrder->calculate_totals(); $wcOrder->save(); return $wcOrder; } protected function fillWcCart(WC_Cart $cart, WC_Customer $customer, WC_Session $session, array $data): void { $cart->empty_cart(); foreach ($data['products'] as $productData) { $product = $this->createWcProduct($productData); $cart->add_to_cart($product->get_id(), $productData['quantity'] ?? 1); } foreach ($data['coupons'] ?? [] as $couponData) { $coupon = $this->createWcCoupon($couponData); $cart->apply_coupon($coupon->get_code()); } $customer->set_billing_country($data['billing']['country'] ?? 'AQ'); if (isset($data['billing']['city'])) { $customer->set_billing_city($data['billing']['city']); } $cart->calculate_totals(); } protected function createWcCoupon(array $data): WC_Coupon { $coupon = new WC_Coupon(); $coupon->set_amount($data['amount']); $coupon->set_discount_type($data['type']); $coupon->set_code(uniqid()); $coupon->set_virtual(true); $coupon->save(); $this->postIds[] = $coupon->get_id(); return $coupon; } protected function createWcProduct(array $data): WC_Product { $product = new WC_Product_Simple(); $product->set_name('Test product ' . rand()); $product->set_status( 'publish'); $product->set_regular_price((string) $data['price']); $product->set_tax_status('taxable'); $product->set_tax_class(''); $product->set_virtual(true); $product->save(); $this->postIds[] = $product->get_id(); return $product; } public function orderData() { yield [ [ 'items' => [ ['price' => 11.99, 'quantity' => 1], ], 'shipping' => ['total' => 4.99], 'billing' => ['city' => 'city1'], ], self::adaptAmountFormat([ 'value' => 18.44, 'breakdown' => [ 'item_total' => 11.99, 'tax_total' => 1.46, 'shipping' => 4.99, ], ]), ]; yield [ [ 'items' => [ ['price' => 11.99, 'quantity' => 3], ], 'shipping' => ['total' => 4.99], 'billing' => ['city' => 'city1'], ], self::adaptAmountFormat([ 'value' => 44.49, 'breakdown' => [ 'item_total' => 35.97, 'tax_total' => 3.53, 'shipping' => 4.99, ], ]), ]; yield [ [ 'items' => [ ['price' => 18.0, 'quantity' => 1], ], 'shipping' => ['total' => 4.99], 'billing' => ['city' => 'city1'], ], self::adaptAmountFormat([ 'value' => 24.97, 'breakdown' => [ 'item_total' => 18.0, 'tax_total' => 1.98, 'shipping' => 4.99, ], ]), ]; yield [ [ 'items' => [ ['price' => 18.0, 'quantity' => 3], ], 'shipping' => ['total' => 4.99], 'billing' => ['city' => 'city1'], ], self::adaptAmountFormat([ 'value' => 64.08, 'breakdown' => [ 'item_total' => 54.0, 'tax_total' => 5.09, 'shipping' => 4.99, ], ]), ]; yield [ [ 'items' => [ ['price' => 11.25, 'quantity' => 3], ], 'billing' => ['city' => 'city2'], ], self::adaptAmountFormat([ 'value' => 53.99, 'breakdown' => [ 'item_total' => 33.75, 'tax_total' => 20.24, 'shipping' => 0.0, ], ]), ]; yield [ [ 'items' => [ ['price' => 11.99, 'quantity' => 3, 'product' => ['price' => 11.99]], ], 'shipping' => ['total' => 4.99], 'billing' => ['city' => 'city1'], 'coupons' => [ ['amount' => 2.39, 'type' => 'fixed_cart'], ['amount' => 7.33, 'type' => 'percent'], ], ], self::adaptAmountFormat([ 'value' => 39.25, 'breakdown' => [ 'item_total' => 35.97, 'tax_total' => 3.12, 'shipping' => 4.99, 'discount' => 4.83, ], ]), ]; yield [ [ 'items' => [ ['price' => 5.99, 'quantity' => 1], ], 'billing' => ['city' => 'city1'], 'fees' => [ ['amount' => 2.89], ['amount' => 7.13], ] ], self::adaptAmountFormat([ 'value' => 17.39, 'breakdown' => [ 'item_total' => 16.01, 'tax_total' => 1.38, 'shipping' => 0.0, ], ]), ]; yield 'no decimals currency' => [ [ 'currency' => 'JPY', 'items' => [ ['price' => 18.0, 'quantity' => 2], ], 'shipping' => ['total' => 5.0], 'billing' => ['city' => 'city2'], ], self::adaptAmountFormat([ 'value' => 66, 'breakdown' => [ 'item_total' => 36, 'tax_total' => 25, // 24.60 'shipping' => 5, ], ], 'JPY'), ]; yield [ [ 'items' => [ ['price' => 5.345, 'quantity' => 2], ], 'billing' => ['city' => 'city0'], ], self::adaptAmountFormat([ 'value' => 10.69, 'breakdown' => [ 'item_total' => 10.69, 'tax_total' => 0, 'shipping' => 0, ], ]), ]; } public function cartData() { yield [ [ 'products' => [ ['price' => 11.99, 'quantity' => 3], ], 'billing' => ['city' => 'city1'], ], self::adaptAmountFormat([ 'value' => 39.07, 'breakdown' => [ 'item_total' => 35.97, 'tax_total' => 3.10, 'shipping' => 0.00, ], ], get_woocommerce_currency()), ]; yield [ [ 'products' => [ ['price' => 11.25, 'quantity' => 3], ], 'billing' => ['city' => 'city2'], ], self::adaptAmountFormat([ 'value' => 53.99, 'breakdown' => [ 'item_total' => 33.75, 'tax_total' => 20.24, 'shipping' => 0.0, ], ], get_woocommerce_currency()), ]; yield [ [ 'products' => [ ['price' => 11.99, 'quantity' => 3], ], 'billing' => ['city' => 'city1'], 'coupons' => [ ['amount' => 2.39, 'type' => 'fixed_cart'], ['amount' => 7.33, 'type' => 'percent'], ], ], self::adaptAmountFormat([ 'value' => 33.83, 'breakdown' => [ 'item_total' => 35.97, 'tax_total' => 2.69, 'shipping' => 0.00, 'discount' => 4.83, ], ], get_woocommerce_currency()), ]; yield [ [ 'products' => [ ['price' => 5.345, 'quantity' => 2], ], 'billing' => ['city' => 'city0'], ], self::adaptAmountFormat([ 'value' => 10.69, 'breakdown' => [ 'item_total' => 10.69, 'tax_total' => 0, 'shipping' => 0, ], ], get_woocommerce_currency()), ]; } private static function adaptAmountFormat(array $data, string $currency = null): array { if (!$currency) { $currency = self::CURRENCY; } $data['currency_code'] = $currency; if (isset($data['breakdown'])) { foreach ($data['breakdown'] as $key => $value) { $data['breakdown'][$key] = [ 'currency_code' => $currency, 'value' => $value, ]; } } return $data; } }