\d+)/items', [ 'methods' => 'GET', 'callback' => [ $this, 'get_items' ], 'permission_callback' => [ self::class, 'check_permission' ], ] ); // POST one batch of split / unable lines register_rest_route( self::NS, '/orders/(?P\d+)/split', [ 'methods' => 'POST', 'callback' => [ $this, 'split' ], 'permission_callback' => [ self::class, 'check_permission' ], ] ); } /** * Permission for every /orders/{id}/… route: the user must be allowed to * edit that specific order. A missing order gets the 404 from the callback * only if the user could split orders at all. */ public static function check_permission( \WP_REST_Request $request ): bool { if ( ! Settings::current_user_can_split() ) return false; $order = wc_get_order( absint( $request['id'] ) ); if ( ! $order ) return true; return Settings::current_user_can_edit_order( $order ); } // ─── GET items ──────────────────────────────────────────────────────────── public function get_items( \WP_REST_Request $request ): \WP_REST_Response { $order = wc_get_order( absint( $request['id'] ) ); if ( ! $order || $order->get_type() !== 'shop_order' ) { return $this->error( 'pwsplit_no_order', __( 'Order not found.', 'patchwork-order-split' ), 404 ); } $items = []; $backorder = Backorders::units( $order ); foreach ( $order->get_items( 'line_item' ) as $item_id => $item ) { $product = $item->get_product(); $qty = (int) $item->get_quantity(); $total = (float) $item->get_total() + (float) $item->get_total_tax(); $items[] = [ 'order_item_id' => (int) $item_id, 'title' => $item->get_name(), 'sku' => $product ? $product->get_sku() : '', 'qty' => $qty, 'price' => $total, 'unit_price' => $qty > 0 ? $total / $qty : 0, 'refunded' => (int) $order->get_qty_refunded_for_item( $item_id ) !== 0 || $order->get_total_refunded_for_item( $item_id ) > 0, 'backorder' => (int) ( $backorder[ $item_id ] ?? 0 ), ]; } $shipping_total = (float) $order->get_shipping_total() + (float) $order->get_shipping_tax(); $problem = Splitter::order_problem( $order ); return new \WP_REST_Response( [ 'success' => true, 'order_id' => $order->get_id(), 'order_number' => $order->get_order_number(), 'status' => $order->get_status(), 'paid' => Splitter::is_paid( $order ), 'payment_method' => $order->get_payment_method_title(), 'items' => $items, 'shipping_total' => $shipping_total, 'currency' => html_entity_decode( get_woocommerce_currency_symbol( $order->get_currency() ) ), 'blocked' => $problem ? $problem->get_error_message() : '', ], 200 ); } // ─── POST split ─────────────────────────────────────────────────────────── public function split( \WP_REST_Request $request ): \WP_REST_Response { $order = wc_get_order( absint( $request['id'] ) ); if ( ! $order || $order->get_type() !== 'shop_order' ) { return $this->error( 'pwsplit_no_order', __( 'Order not found.', 'patchwork-order-split' ), 404 ); } $params = (array) $request->get_json_params(); $lines = isset( $params['lines'] ) && is_array( $params['lines'] ) ? $params['lines'] : []; $args = array_intersect_key( $params, array_flip( [ 'shipping', 'child_status', 'child_date', 'notify_customer', 'refund_via_gateway' ] ) ); $result = ( new Splitter() )->split( $order, $lines, $args ); if ( is_wp_error( $result ) ) { return $this->error( $result->get_error_code(), $result->get_error_message(), 400 ); } $result['success'] = true; $result['parent_url'] = $order->get_edit_order_url(); $result['child_url'] = $result['child_id'] ? wc_get_order( $result['child_id'] )->get_edit_order_url() : ''; $result['unable_url'] = $result['unable_id'] ? wc_get_order( $result['unable_id'] )->get_edit_order_url() : ''; return new \WP_REST_Response( $result, 200 ); } private function error( string $code, string $message, int $status ): \WP_REST_Response { return new \WP_REST_Response( [ 'success' => false, 'code' => $code, 'message' => $message ], $status ); } }