| @@ -147,8 +147,20 @@ | ||
| 147 | 147 | ); |
| 148 | 148 | |
| 149 | 149 | register_rest_route( |
| 150 | 150 | self::NS, |
| 151 | + '/plans/bulk-price', | |
| 152 | + [ | |
| 153 | + [ | |
| 154 | + 'methods' => WP_REST_Server::CREATABLE, | |
| 155 | + 'callback' => [ $this, 'bulk_price' ], | |
| 156 | + 'permission_callback' => $perm, | |
| 157 | + ], | |
| 158 | + ] | |
| 159 | + ); | |
| 160 | + | |
| 161 | + register_rest_route( | |
| 162 | + self::NS, | |
| 151 | 163 | '/plans/products', |
| 152 | 164 | array( |
| 153 | 165 | array( |
| 154 | 166 | 'methods' => WP_REST_Server::READABLE, |
| @@ -583,8 +595,75 @@ | ||
| 583 | 595 | 'deleted' => true, |
| 584 | 596 | 'id' => $id, |
| 585 | 597 | ) |
| 586 | 598 | ); |
| 599 | + } | |
| 600 | + | |
| 601 | + /** | |
| 602 | + * POST /plans/bulk-price - set one price on many product/duration relations. | |
| 603 | + * | |
| 604 | + * Upserts the regular (and optional sale) price onto the existing relations | |
| 605 | + * for each selected product across each selected duration. Only relations | |
| 606 | + * that already exist are touched, so bulk pricing never attaches a product to | |
| 607 | + * a term it was not already on. Free is simple-only: variation targets | |
| 608 | + * (vid != 0) are skipped unless Pro is active. | |
| 609 | + * | |
| 610 | + * Body: { plan_ids: int[], products: [{ oid, vid }], regular_price, sale_price? }. | |
| 611 | + * | |
| 612 | + * @param WP_REST_Request $request Request. | |
| 613 | + * | |
| 614 | + * @return \WP_REST_Response|WP_Error | |
| 615 | + */ | |
| 616 | + public function bulk_price( WP_REST_Request $request ) { | |
| 617 | + $params = $this->read_params( $request ); | |
| 618 | + | |
| 619 | + $plan_ids = isset( $params['plan_ids'] ) && is_array( $params['plan_ids'] ) ? array_filter( array_map( 'absint', $params['plan_ids'] ) ) : []; | |
| 620 | + $products = isset( $params['products'] ) && is_array( $params['products'] ) ? $params['products'] : []; | |
| 621 | + | |
| 622 | + if ( empty( $plan_ids ) ) { | |
| 623 | + return new WP_Error( 'subscrpt_bulk_no_terms', __( 'Select at least one duration.', 'subscription' ), [ 'status' => 400 ] ); | |
| 624 | + } | |
| 625 | + | |
| 626 | + if ( empty( $products ) ) { | |
| 627 | + return new WP_Error( 'subscrpt_bulk_no_products', __( 'Select at least one product.', 'subscription' ), [ 'status' => 400 ] ); | |
| 628 | + } | |
| 629 | + | |
| 630 | + if ( ! isset( $params['regular_price'] ) || '' === $params['regular_price'] ) { | |
| 631 | + return new WP_Error( 'subscrpt_bulk_no_price', __( 'Enter a regular price.', 'subscription' ), [ 'status' => 400 ] ); | |
| 632 | + } | |
| 633 | + | |
| 634 | + $data = [ | |
| 635 | + 'regular_price' => (string) wc_format_decimal( $params['regular_price'] ), | |
| 636 | + 'sale_price' => isset( $params['sale_price'] ) && '' !== $params['sale_price'] ? (string) wc_format_decimal( $params['sale_price'] ) : '', | |
| 637 | + ]; | |
| 638 | + | |
| 639 | + $updated = 0; | |
| 640 | + | |
| 641 | + foreach ( $products as $product ) { | |
| 642 | + $oid = isset( $product['oid'] ) ? absint( $product['oid'] ) : 0; | |
| 643 | + $vid = isset( $product['vid'] ) ? absint( $product['vid'] ) : 0; | |
| 644 | + | |
| 645 | + if ( ! $oid ) { | |
| 646 | + continue; | |
| 647 | + } | |
| 648 | + | |
| 649 | + if ( is_wp_error( $this->guard_simple_only( [ 'vid' => $vid ] ) ) ) { | |
| 650 | + continue; | |
| 651 | + } | |
| 652 | + | |
| 653 | + foreach ( $plan_ids as $plan_id ) { | |
| 654 | + $relation_id = PlanRepository::find_relation( $plan_id, $oid, $vid ); | |
| 655 | + | |
| 656 | + if ( ! $relation_id ) { | |
| 657 | + continue; | |
| 658 | + } | |
| 659 | + | |
| 660 | + PlanRepository::update_relation( $relation_id, [ 'data' => $data ] ); | |
| 661 | + ++$updated; | |
| 662 | + } | |
| 663 | + } | |
| 664 | + | |
| 665 | + return rest_ensure_response( [ 'updated' => $updated ] ); | |
| 587 | 666 | } |
| 588 | 667 | |
| 589 | 668 | /** |
| 590 | 669 | * PUT /plans/product-onetime/{id} - save a product's one-time purchase. |