| @@ -84,8 +84,20 @@ | ||
| 84 | 84 | ); |
| 85 | 85 | |
| 86 | 86 | register_rest_route( |
| 87 | 87 | self::NS, |
| 88 | + '/plans/groups/(?P<id>\d+)/order', | |
| 89 | + array( | |
| 90 | + array( | |
| 91 | + 'methods' => WP_REST_Server::EDITABLE, | |
| 92 | + 'callback' => array( $this, 'update_group_order' ), | |
| 93 | + 'permission_callback' => $perm, | |
| 94 | + ), | |
| 95 | + ) | |
| 96 | + ); | |
| 97 | + | |
| 98 | + register_rest_route( | |
| 99 | + self::NS, | |
| 88 | 100 | '/plans/terms', |
| 89 | 101 | array( |
| 90 | 102 | array( |
| 91 | 103 | 'methods' => WP_REST_Server::CREATABLE, |
| @@ -147,8 +159,20 @@ | ||
| 147 | 159 | ); |
| 148 | 160 | |
| 149 | 161 | register_rest_route( |
| 150 | 162 | self::NS, |
| 163 | + '/plans/bulk-price', | |
| 164 | + [ | |
| 165 | + [ | |
| 166 | + 'methods' => WP_REST_Server::CREATABLE, | |
| 167 | + 'callback' => [ $this, 'bulk_price' ], | |
| 168 | + 'permission_callback' => $perm, | |
| 169 | + ], | |
| 170 | + ] | |
| 171 | + ); | |
| 172 | + | |
| 173 | + register_rest_route( | |
| 174 | + self::NS, | |
| 151 | 175 | '/plans/products', |
| 152 | 176 | array( |
| 153 | 177 | array( |
| 154 | 178 | 'methods' => WP_REST_Server::READABLE, |
| @@ -364,8 +388,44 @@ | ||
| 364 | 388 | return rest_ensure_response( PlanRepository::get_group_tree( $id ) ); |
| 365 | 389 | } |
| 366 | 390 | |
| 367 | 391 | /** |
| 392 | + * PUT /plans/groups/{id}/order - save the admin display order of the | |
| 393 | + * group's durations and/or products (drag-and-drop on the detail page). | |
| 394 | + * | |
| 395 | + * Stored in the group's `data` JSON as `term_order` / `product_order` | |
| 396 | + * (lists of ids), merged so the rest of `data` is left as it was. | |
| 397 | + * | |
| 398 | + * @param WP_REST_Request $request Request. | |
| 399 | + * | |
| 400 | + * @return \WP_REST_Response|WP_Error | |
| 401 | + */ | |
| 402 | + public function update_group_order( WP_REST_Request $request ) { | |
| 403 | + $id = (int) $request['id']; | |
| 404 | + $group = PlanRepository::get_group( $id ); | |
| 405 | + | |
| 406 | + if ( ! $group ) { | |
| 407 | + return $this->not_found(); | |
| 408 | + } | |
| 409 | + | |
| 410 | + $params = $this->read_params( $request ); | |
| 411 | + $data = is_array( $group['data'] ) ? $group['data'] : array(); | |
| 412 | + | |
| 413 | + foreach ( array( | |
| 414 | + 'terms' => 'term_order', | |
| 415 | + 'products' => 'product_order', | |
| 416 | + ) as $param => $key ) { | |
| 417 | + if ( isset( $params[ $param ] ) && is_array( $params[ $param ] ) ) { | |
| 418 | + $data[ $key ] = array_values( array_unique( array_filter( array_map( 'absint', $params[ $param ] ) ) ) ); | |
| 419 | + } | |
| 420 | + } | |
| 421 | + | |
| 422 | + PlanRepository::update_group( $id, array( 'data' => $data ) ); | |
| 423 | + | |
| 424 | + return rest_ensure_response( array( 'success' => true ) ); | |
| 425 | + } | |
| 426 | + | |
| 427 | + /** | |
| 368 | 428 | * DELETE /plans/groups/{id} - delete group + cascade. |
| 369 | 429 | * |
| 370 | 430 | * @param WP_REST_Request $request Request. |
| 371 | 431 | * |
| @@ -583,8 +643,75 @@ | ||
| 583 | 643 | 'deleted' => true, |
| 584 | 644 | 'id' => $id, |
| 585 | 645 | ) |
| 586 | 646 | ); |
| 647 | + } | |
| 648 | + | |
| 649 | + /** | |
| 650 | + * POST /plans/bulk-price - set one price on many product/duration relations. | |
| 651 | + * | |
| 652 | + * Upserts the regular (and optional sale) price onto the existing relations | |
| 653 | + * for each selected product across each selected duration. Only relations | |
| 654 | + * that already exist are touched, so bulk pricing never attaches a product to | |
| 655 | + * a term it was not already on. Free is simple-only: variation targets | |
| 656 | + * (vid != 0) are skipped unless Pro is active. | |
| 657 | + * | |
| 658 | + * Body: { plan_ids: int[], products: [{ oid, vid }], regular_price, sale_price? }. | |
| 659 | + * | |
| 660 | + * @param WP_REST_Request $request Request. | |
| 661 | + * | |
| 662 | + * @return \WP_REST_Response|WP_Error | |
| 663 | + */ | |
| 664 | + public function bulk_price( WP_REST_Request $request ) { | |
| 665 | + $params = $this->read_params( $request ); | |
| 666 | + | |
| 667 | + $plan_ids = isset( $params['plan_ids'] ) && is_array( $params['plan_ids'] ) ? array_filter( array_map( 'absint', $params['plan_ids'] ) ) : []; | |
| 668 | + $products = isset( $params['products'] ) && is_array( $params['products'] ) ? $params['products'] : []; | |
| 669 | + | |
| 670 | + if ( empty( $plan_ids ) ) { | |
| 671 | + return new WP_Error( 'subscrpt_bulk_no_terms', __( 'Select at least one duration.', 'subscription' ), [ 'status' => 400 ] ); | |
| 672 | + } | |
| 673 | + | |
| 674 | + if ( empty( $products ) ) { | |
| 675 | + return new WP_Error( 'subscrpt_bulk_no_products', __( 'Select at least one product.', 'subscription' ), [ 'status' => 400 ] ); | |
| 676 | + } | |
| 677 | + | |
| 678 | + if ( ! isset( $params['regular_price'] ) || '' === $params['regular_price'] ) { | |
| 679 | + return new WP_Error( 'subscrpt_bulk_no_price', __( 'Enter a regular price.', 'subscription' ), [ 'status' => 400 ] ); | |
| 680 | + } | |
| 681 | + | |
| 682 | + $data = [ | |
| 683 | + 'regular_price' => (string) wc_format_decimal( $params['regular_price'] ), | |
| 684 | + 'sale_price' => isset( $params['sale_price'] ) && '' !== $params['sale_price'] ? (string) wc_format_decimal( $params['sale_price'] ) : '', | |
| 685 | + ]; | |
| 686 | + | |
| 687 | + $updated = 0; | |
| 688 | + | |
| 689 | + foreach ( $products as $product ) { | |
| 690 | + $oid = isset( $product['oid'] ) ? absint( $product['oid'] ) : 0; | |
| 691 | + $vid = isset( $product['vid'] ) ? absint( $product['vid'] ) : 0; | |
| 692 | + | |
| 693 | + if ( ! $oid ) { | |
| 694 | + continue; | |
| 695 | + } | |
| 696 | + | |
| 697 | + if ( is_wp_error( $this->guard_simple_only( [ 'vid' => $vid ] ) ) ) { | |
| 698 | + continue; | |
| 699 | + } | |
| 700 | + | |
| 701 | + foreach ( $plan_ids as $plan_id ) { | |
| 702 | + $relation_id = PlanRepository::find_relation( $plan_id, $oid, $vid ); | |
| 703 | + | |
| 704 | + if ( ! $relation_id ) { | |
| 705 | + continue; | |
| 706 | + } | |
| 707 | + | |
| 708 | + PlanRepository::update_relation( $relation_id, [ 'data' => $data ] ); | |
| 709 | + ++$updated; | |
| 710 | + } | |
| 711 | + } | |
| 712 | + | |
| 713 | + return rest_ensure_response( [ 'updated' => $updated ] ); | |
| 587 | 714 | } |
| 588 | 715 | |
| 589 | 716 | /** |
| 590 | 717 | * PUT /plans/product-onetime/{id} - save a product's one-time purchase. |