| @@ -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, |
| @@ -171,8 +195,20 @@ | ||
| 171 | 195 | ); |
| 172 | 196 | |
| 173 | 197 | register_rest_route( |
| 174 | 198 | self::NS, |
| 199 | + '/plans/product-user-cancel/(?P<id>\d+)', | |
| 200 | + array( | |
| 201 | + array( | |
| 202 | + 'methods' => WP_REST_Server::EDITABLE, | |
| 203 | + 'callback' => array( $this, 'save_product_user_cancel' ), | |
| 204 | + 'permission_callback' => $perm, | |
| 205 | + ), | |
| 206 | + ) | |
| 207 | + ); | |
| 208 | + | |
| 209 | + register_rest_route( | |
| 210 | + self::NS, | |
| 175 | 211 | '/plans/group-products/(?P<id>\d+)', |
| 176 | 212 | array( |
| 177 | 213 | array( |
| 178 | 214 | 'methods' => WP_REST_Server::READABLE, |
| @@ -364,8 +400,44 @@ | ||
| 364 | 400 | return rest_ensure_response( PlanRepository::get_group_tree( $id ) ); |
| 365 | 401 | } |
| 366 | 402 | |
| 367 | 403 | /** |
| 404 | + * PUT /plans/groups/{id}/order - save the admin display order of the | |
| 405 | + * group's durations and/or products (drag-and-drop on the detail page). | |
| 406 | + * | |
| 407 | + * Stored in the group's `data` JSON as `term_order` / `product_order` | |
| 408 | + * (lists of ids), merged so the rest of `data` is left as it was. | |
| 409 | + * | |
| 410 | + * @param WP_REST_Request $request Request. | |
| 411 | + * | |
| 412 | + * @return \WP_REST_Response|WP_Error | |
| 413 | + */ | |
| 414 | + public function update_group_order( WP_REST_Request $request ) { | |
| 415 | + $id = (int) $request['id']; | |
| 416 | + $group = PlanRepository::get_group( $id ); | |
| 417 | + | |
| 418 | + if ( ! $group ) { | |
| 419 | + return $this->not_found(); | |
| 420 | + } | |
| 421 | + | |
| 422 | + $params = $this->read_params( $request ); | |
| 423 | + $data = is_array( $group['data'] ) ? $group['data'] : array(); | |
| 424 | + | |
| 425 | + foreach ( array( | |
| 426 | + 'terms' => 'term_order', | |
| 427 | + 'products' => 'product_order', | |
| 428 | + ) as $param => $key ) { | |
| 429 | + if ( isset( $params[ $param ] ) && is_array( $params[ $param ] ) ) { | |
| 430 | + $data[ $key ] = array_values( array_unique( array_filter( array_map( 'absint', $params[ $param ] ) ) ) ); | |
| 431 | + } | |
| 432 | + } | |
| 433 | + | |
| 434 | + PlanRepository::update_group( $id, array( 'data' => $data ) ); | |
| 435 | + | |
| 436 | + return rest_ensure_response( array( 'success' => true ) ); | |
| 437 | + } | |
| 438 | + | |
| 439 | + /** | |
| 368 | 440 | * DELETE /plans/groups/{id} - delete group + cascade. |
| 369 | 441 | * |
| 370 | 442 | * @param WP_REST_Request $request Request. |
| 371 | 443 | * |
| @@ -583,8 +655,101 @@ | ||
| 583 | 655 | 'deleted' => true, |
| 584 | 656 | 'id' => $id, |
| 585 | 657 | ) |
| 586 | 658 | ); |
| 659 | + } | |
| 660 | + | |
| 661 | + /** | |
| 662 | + * PUT /plans/product-user-cancel/{id} - save whether customers may cancel. | |
| 663 | + * | |
| 664 | + * Writes the same `_subscrpt_user_cancel` meta the product editor saves, as | |
| 665 | + * an explicit "yes" / "no", which checkout copies onto each new subscription. | |
| 666 | + * | |
| 667 | + * Body: { enabled: bool }. | |
| 668 | + * | |
| 669 | + * @param WP_REST_Request $request Request. | |
| 670 | + * | |
| 671 | + * @return \WP_REST_Response|WP_Error | |
| 672 | + */ | |
| 673 | + public function save_product_user_cancel( WP_REST_Request $request ) { | |
| 674 | + $product = function_exists( 'wc_get_product' ) ? wc_get_product( (int) $request['id'] ) : null; | |
| 675 | + | |
| 676 | + if ( ! $product ) { | |
| 677 | + return $this->not_found(); | |
| 678 | + } | |
| 679 | + | |
| 680 | + $params = $this->read_params( $request ); | |
| 681 | + $product->update_meta_data( '_subscrpt_user_cancel', empty( $params['enabled'] ) ? 'no' : 'yes' ); | |
| 682 | + $product->save(); | |
| 683 | + | |
| 684 | + return rest_ensure_response( array( 'saved' => true ) ); | |
| 685 | + } | |
| 686 | + | |
| 687 | + /** | |
| 688 | + * POST /plans/bulk-price - set one price on many product/duration relations. | |
| 689 | + * | |
| 690 | + * Upserts the regular (and optional sale) price onto the existing relations | |
| 691 | + * for each selected product across each selected duration. Only relations | |
| 692 | + * that already exist are touched, so bulk pricing never attaches a product to | |
| 693 | + * a term it was not already on. Free is simple-only: variation targets | |
| 694 | + * (vid != 0) are skipped unless Pro is active. | |
| 695 | + * | |
| 696 | + * Body: { plan_ids: int[], products: [{ oid, vid }], regular_price, sale_price? }. | |
| 697 | + * | |
| 698 | + * @param WP_REST_Request $request Request. | |
| 699 | + * | |
| 700 | + * @return \WP_REST_Response|WP_Error | |
| 701 | + */ | |
| 702 | + public function bulk_price( WP_REST_Request $request ) { | |
| 703 | + $params = $this->read_params( $request ); | |
| 704 | + | |
| 705 | + $plan_ids = isset( $params['plan_ids'] ) && is_array( $params['plan_ids'] ) ? array_filter( array_map( 'absint', $params['plan_ids'] ) ) : []; | |
| 706 | + $products = isset( $params['products'] ) && is_array( $params['products'] ) ? $params['products'] : []; | |
| 707 | + | |
| 708 | + if ( empty( $plan_ids ) ) { | |
| 709 | + return new WP_Error( 'subscrpt_bulk_no_terms', __( 'Select at least one duration.', 'subscription' ), [ 'status' => 400 ] ); | |
| 710 | + } | |
| 711 | + | |
| 712 | + if ( empty( $products ) ) { | |
| 713 | + return new WP_Error( 'subscrpt_bulk_no_products', __( 'Select at least one product.', 'subscription' ), [ 'status' => 400 ] ); | |
| 714 | + } | |
| 715 | + | |
| 716 | + if ( ! isset( $params['regular_price'] ) || '' === $params['regular_price'] ) { | |
| 717 | + return new WP_Error( 'subscrpt_bulk_no_price', __( 'Enter a regular price.', 'subscription' ), [ 'status' => 400 ] ); | |
| 718 | + } | |
| 719 | + | |
| 720 | + $data = [ | |
| 721 | + 'regular_price' => (string) wc_format_decimal( $params['regular_price'] ), | |
| 722 | + 'sale_price' => isset( $params['sale_price'] ) && '' !== $params['sale_price'] ? (string) wc_format_decimal( $params['sale_price'] ) : '', | |
| 723 | + ]; | |
| 724 | + | |
| 725 | + $updated = 0; | |
| 726 | + | |
| 727 | + foreach ( $products as $product ) { | |
| 728 | + $oid = isset( $product['oid'] ) ? absint( $product['oid'] ) : 0; | |
| 729 | + $vid = isset( $product['vid'] ) ? absint( $product['vid'] ) : 0; | |
| 730 | + | |
| 731 | + if ( ! $oid ) { | |
| 732 | + continue; | |
| 733 | + } | |
| 734 | + | |
| 735 | + if ( is_wp_error( $this->guard_simple_only( [ 'vid' => $vid ] ) ) ) { | |
| 736 | + continue; | |
| 737 | + } | |
| 738 | + | |
| 739 | + foreach ( $plan_ids as $plan_id ) { | |
| 740 | + $relation_id = PlanRepository::find_relation( $plan_id, $oid, $vid ); | |
| 741 | + | |
| 742 | + if ( ! $relation_id ) { | |
| 743 | + continue; | |
| 744 | + } | |
| 745 | + | |
| 746 | + PlanRepository::update_relation( $relation_id, [ 'data' => $data ] ); | |
| 747 | + ++$updated; | |
| 748 | + } | |
| 749 | + } | |
| 750 | + | |
| 751 | + return rest_ensure_response( [ 'updated' => $updated ] ); | |
| 587 | 752 | } |
| 588 | 753 | |
| 589 | 754 | /** |
| 590 | 755 | * PUT /plans/product-onetime/{id} - save a product's one-time purchase. |