PluginProbe
Tiered Pricing Table for WooCommerce / 8.1.0
Tiered Pricing Table for WooCommerce v8.1.0
8.1.0 8.0.2 7.1.7 7.1.5 7.1.4 7.1.1 6.5.0 6.4.0 6.1.0 trunk 1.0 1.1 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.3.0 2.3.1 2.3.2 All 92 releases
tier-pricing-table / src / Addons / GlobalTieredPricing / CPT / GlobalTieredPricingCPT.php

GlobalTieredPricingCPT.php in Tiered Pricing Table for WooCommerce 8.1.0, at src/Addons/GlobalTieredPricing/CPT/GlobalTieredPricingCPT.php

567 lines 21.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TierPricingTable\Addons\GlobalTieredPricing\CPT;
4
5 use Automattic\WooCommerce\Admin\PageController;
6 use TierPricingTable\Addons\GlobalTieredPricing\CPT\Actions\DuplicateAction;
7 use TierPricingTable\Addons\GlobalTieredPricing\CPT\Actions\ReactivateAction;
8 use TierPricingTable\Addons\GlobalTieredPricing\CPT\Actions\SuspendAction;
9 use TierPricingTable\Addons\GlobalTieredPricing\CPT\Columns\Pricing;
10 use TierPricingTable\Addons\GlobalTieredPricing\CPT\Columns\Settings;
11 use TierPricingTable\Addons\GlobalTieredPricing\CPT\Form\Form;
12 use TierPricingTable\Addons\GlobalTieredPricing\GlobalPricingRule;
13 use TierPricingTable\Addons\GlobalTieredPricing\PricingRule\RuleSettings;
14 use TierPricingTable\Core\ServiceContainerTrait;
15 use TierPricingTable\Forms\MinimumOrderQuantityForm;
16 use TierPricingTable\Forms\RegularPricingForm;
17 use TierPricingTable\Forms\TieredPricingRulesForm;
18 use TierPricingTable\Addons\GlobalTieredPricing\CPT\Columns\AppliesTo;
19 use TierPricingTable\Addons\GlobalTieredPricing\CPT\Columns\Rule;
20 use TierPricingTable\Core\AdminNotifier;
21 use WP_Post;
22 class GlobalTieredPricingCPT {
23 use ServiceContainerTrait;
24 const SLUG = 'tpt-global-rule';
25
26 /**
27 * Pricing rules
28 *
29 * @var GlobalPricingRule
30 */
31 private $pricingRuleInstance;
32
33 /**
34 * Table columns
35 *
36 * @var array
37 */
38 private $columns;
39
40 protected static $globalRules = null;
41
42 public function __construct() {
43 new Form();
44 add_action( 'init', array($this, 'register') );
45 add_action( 'manage_posts_extra_tablenav', array($this, 'renderBlankState') );
46 add_filter( 'woocommerce_navigation_screen_ids', array($this, 'addPageToWooCommerceScreen') );
47 add_filter( 'woocommerce_screen_ids', array($this, 'addPageToWooCommerceScreen') );
48 add_action( 'save_post_' . self::SLUG, array($this, 'savePricingRule') );
49 add_filter( 'manage_edit-' . self::SLUG . '_columns', function ( $columns ) {
50 // the Rule column replaces WordPress's title column (title, status, priority, window, overlaps)
51 $list = array(
52 'cb' => ( isset( $columns['cb'] ) ? $columns['cb'] : '<input type="checkbox" />' ),
53 );
54 foreach ( $this->getColumns() as $key => $column ) {
55 $list[$key] = $column->getName();
56 }
57 return $list;
58 }, 999 );
59 add_filter(
60 'list_table_primary_column',
61 function ( $default, $screen ) {
62 return ( 'edit-' . self::SLUG === $screen ? 'rule' : $default );
63 },
64 10,
65 2
66 );
67 add_filter( 'manage_edit-' . self::SLUG . '_sortable_columns', function ( $columns ) {
68 $columns['rule'] = 'title';
69 return $columns;
70 } );
71 add_action( 'admin_head-edit.php', array($this, 'printListStyles') );
72 add_filter( 'manage_' . self::SLUG . '_posts_custom_column', function ( $column ) {
73 global $post;
74 $globalRule = GlobalPricingRule::build( $post->ID );
75 if ( array_key_exists( $column, $this->getColumns() ) ) {
76 $this->getColumns()[$column]->render( $globalRule );
77 do_action( 'tiered_pricing_table/global_pricing/table/after_tab_render', $column, $globalRule );
78 }
79 return $column;
80 }, 999 );
81 add_filter(
82 'post_row_actions',
83 function ( $actions, WP_Post $post ) {
84 if ( self::SLUG !== $post->post_type ) {
85 return $actions;
86 }
87 unset($actions['inline hide-if-no-js']);
88 return $actions;
89 },
90 10,
91 2
92 );
93 add_filter(
94 'disable_months_dropdown',
95 function ( $state, $postType ) {
96 if ( self::SLUG === $postType ) {
97 return true;
98 }
99 return $state;
100 },
101 10,
102 2
103 );
104 // Refresh cache for variable product pricing
105 add_action( 'save_post_' . self::SLUG, function () {
106 wc_delete_product_transients();
107 } );
108 $this->initInlineActions();
109 new ListOrder();
110 new StatusViews();
111 new StatusBox();
112 }
113
114 public function initInlineActions() {
115 new SuspendAction();
116 new ReactivateAction();
117 new DuplicateAction();
118 }
119
120 public function getColumns() : array {
121 if ( is_null( $this->columns ) ) {
122 $this->columns = array(
123 'rule' => new Rule(),
124 'applies' => new AppliesTo(),
125 'pricing' => new Pricing(),
126 'settings' => new Settings(),
127 );
128 $this->columns = apply_filters( 'tiered_pricing_table/global_pricing/columns', $this->columns );
129 }
130 return $this->columns;
131 }
132
133 /**
134 * Styles for the rules list (badges, chips, column widths).
135 */
136 public function printListStyles() {
137 $screen = get_current_screen();
138 if ( !$screen || self::SLUG !== $screen->post_type ) {
139 return;
140 }
141 ?>
142 <style>
143 .post-type-<?php
144 echo esc_attr( self::SLUG );
145 ?> .wp-list-table .column-rule { width: 26%; }
146 .post-type-<?php
147 echo esc_attr( self::SLUG );
148 ?> .wp-list-table .column-applies { width: 26%; }
149 .post-type-<?php
150 echo esc_attr( self::SLUG );
151 ?> .wp-list-table .column-pricing { width: 32%; }
152 .post-type-<?php
153 echo esc_attr( self::SLUG );
154 ?> .wp-list-table .column-settings { width: 16%; }
155
156 .tpt-rules-list__badge {
157 display: table; /* its own line above the title, as wide as its text */
158 margin: 0 0 6px;
159 padding: 1px 8px;
160 border-radius: 999px;
161 font-size: 11px;
162 font-weight: 600;
163 line-height: 1.5;
164 text-transform: uppercase;
165 letter-spacing: .02em;
166 vertical-align: 1px;
167 white-space: nowrap;
168 }
169
170 .tpt-rules-list__badge--active { background: #edfaef; color: #007017; }
171 .tpt-rules-list__badge--suspended,
172 .tpt-rules-list__badge--skipped { background: #fcf9e8; color: #996800; }
173 .tpt-rules-list__badge--scheduled { background: #f0f6fc; color: #2271b1; }
174 .tpt-rules-list__badge--expired,
175 .tpt-rules-list__badge--draft { background: #f0f0f1; color: #50575e; }
176
177 .tpt-rules-list__meta {
178 margin-top: 4px;
179 font-size: 12px;
180 color: #646970;
181 }
182
183 .post-type-<?php
184 echo esc_attr( self::SLUG );
185 ?> .wp-list-table .column-rule .row-actions {
186 margin-top: 8px;
187 }
188
189 .tpt-rules-list__sep { margin: 0 2px; }
190 .tpt-rules-list__muted { color: #646970; }
191 .tpt-rules-list__warn { color: #996800; }
192
193 .tpt-rules-list__line {
194 display: flex;
195 align-items: flex-start;
196 gap: 6px;
197 margin: 0 0 6px;
198 line-height: 1.5;
199 }
200
201 .tpt-rules-list__line:last-child { margin-bottom: 0; }
202
203 .tpt-rules-list__line > .dashicons {
204 flex: 0 0 auto;
205 margin-top: 2px;
206 font-size: 16px;
207 width: 16px;
208 height: 16px;
209 color: #8c8f94;
210 }
211
212 .tpt-rules-list__line--chips {
213 display: block;
214 }
215
216 .tpt-rules-list__line--chips .tpt-rules-list__muted {
217 display: inline-block;
218 margin-right: 2px;
219 }
220
221 .tpt-rules-list__chip {
222 display: inline-block;
223 margin: 0 2px 3px 0;
224 padding: 0 7px;
225 border: 1px solid #dcdcde;
226 border-radius: 3px;
227 background: #f6f7f7;
228 font-size: 12px;
229 line-height: 1.7;
230 white-space: nowrap;
231 }
232
233 .tpt-rules-list__chip b { color: #0070bc; }
234 .tpt-rules-list__chip .amount { color: inherit; }
235
236 .tpt-rules-list__tiers {
237 width: 100%;
238 max-width: 320px;
239 margin: 2px 0 8px;
240 border: 1px solid #c3c4c7;
241 border-collapse: collapse;
242 background: #fff;
243 font-size: 12px;
244 text-align: left;
245 }
246
247 .tpt-rules-list__tiers th,
248 .tpt-rules-list__tiers td {
249 padding: 3px 8px;
250 border-bottom: 1px solid #e2e4e7;
251 line-height: 1.6;
252 }
253
254 .tpt-rules-list__tiers th {
255 background: #f0f0f1;
256 font-weight: 600;
257 }
258
259 .tpt-rules-list__tiers td:first-child {
260 width: 45%;
261 border-right: 1px solid #e2e4e7;
262 }
263
264 .tpt-rules-list__tiers tr:last-child td { border-bottom: 0; }
265 .tpt-rules-list__tiers td.is-tier { color: #0070bc; font-weight: 600; }
266 .tpt-rules-list__tiers td.is-tier .amount { color: inherit; }
267 .tpt-rules-list__tiers + .tpt-rules-list__line { margin-top: 2px; }
268
269 .tpt-rules-list__tag {
270 display: inline-block;
271 margin: 0 2px 3px 0;
272 padding: 1px 8px;
273 border: 1px solid #bae0ff;
274 border-radius: 3px;
275 background: #e0f0fa;
276 color: #0070bc;
277 font-size: 12px;
278 line-height: 1.6;
279 }
280 </style>
281 <?php
282 }
283
284 public function getPricingRuleInstance() : ?GlobalPricingRule {
285 if ( empty( $this->pricingRuleInstance ) ) {
286 global $post;
287 if ( $post ) {
288 $this->pricingRuleInstance = GlobalPricingRule::build( $post->ID );
289 } else {
290 return null;
291 }
292 }
293 return $this->pricingRuleInstance;
294 }
295
296 public function addPageToWooCommerceScreen( $ids ) {
297 $ids[] = self::SLUG;
298 $ids[] = 'edit-' . self::SLUG;
299 return $ids;
300 }
301
302 public function savePricingRule( $ruleId ) {
303 // Prevent wiping data when duplicating
304 if ( isset( $_GET['action'] ) && $_GET['action'] === DuplicateAction::ACTION ) {
305 return;
306 }
307 // Save pricing
308 if ( wp_verify_nonce( true, true ) ) {
309 // as phpcs comments at Woo are not available, we have to do such trash
310 $woo = 'Woo, please add ignoring comments to your phpcs checker';
311 }
312 $postedData = $_POST;
313 $tieredPricingData = TieredPricingRulesForm::getDataFromRequest(
314 null,
315 null,
316 '',
317 $postedData,
318 $ruleId
319 );
320 $regularPricingData = RegularPricingForm::getDataFromRequest( null, null, $postedData );
321 $minimumOrderQuantityData = MinimumOrderQuantityForm::getDataFromRequest( null, null, $postedData );
322 $pricingRule = GlobalPricingRule::build( $ruleId );
323 $applyingType = ( isset( $postedData['tpt_applying_type'] ) ? sanitize_text_field( $postedData['tpt_applying_type'] ) : 'individual' );
324 $pricingRule->setApplyingType( $applyingType );
325 $pricingRule->setFixedTieredPricingRules( $tieredPricingData['fixed_tiered_pricing_rules'] );
326 $includedCategoriesIds = ( isset( $postedData['tpt_included_categories'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_included_categories'] ) ) : array() );
327 $includedTagsIds = ( isset( $postedData['tpt_included_tags'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_included_tags'] ) ) : array() );
328 $includedBrandsIds = ( isset( $postedData['tpt_included_brands'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_included_brands'] ) ) : array() );
329 $includedProductsIds = ( isset( $postedData['tpt_included_products'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_included_products'] ) ) : array() );
330 $includedUsersRole = ( isset( $postedData['tpt_included_user_roles'] ) ? array_filter( (array) $postedData['tpt_included_user_roles'], array(GlobalPricingRule::class, 'isSelectableRole') ) : array() );
331 $includedUsers = ( isset( $postedData['tpt_included_users'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_included_users'] ) ) : array() );
332 $excludedCategoriesIds = ( isset( $postedData['tpt_excluded_categories'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_excluded_categories'] ) ) : array() );
333 $excludedTagsIds = ( isset( $postedData['tpt_excluded_tags'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_excluded_tags'] ) ) : array() );
334 $excludedBrandsIds = ( isset( $postedData['tpt_excluded_brands'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_excluded_brands'] ) ) : array() );
335 $excludedProductsIds = ( isset( $postedData['tpt_excluded_products'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_excluded_products'] ) ) : array() );
336 $excludedUsersRole = ( isset( $postedData['tpt_excluded_user_roles'] ) ? array_filter( (array) $postedData['tpt_excluded_user_roles'], array(GlobalPricingRule::class, 'isSelectableRole') ) : array() );
337 $excludedUsers = ( isset( $postedData['tpt_excluded_users'] ) ? array_filter( array_map( 'intval', (array) $postedData['tpt_excluded_users'] ) ) : array() );
338 $pricingRule->setIncludedProductCategories( $includedCategoriesIds );
339 $pricingRule->setIncludeSubcategories( isset( $postedData['tpt_include_subcategories'] ) );
340 // the checkbox is only posted when ticked
341 $pricingRule->setIncludedProductTags( $includedTagsIds );
342 $pricingRule->setIncludedProductBrands( $includedBrandsIds );
343 $pricingRule->setIncludedProducts( $includedProductsIds );
344 $pricingRule->setIncludedUsersRole( $includedUsersRole );
345 $pricingRule->setIncludedUsers( $includedUsers );
346 $pricingRule->setExcludedProductCategories( $excludedCategoriesIds );
347 $pricingRule->setExcludedProductTags( $excludedTagsIds );
348 $pricingRule->setExcludedProductBrands( $excludedBrandsIds );
349 $pricingRule->setExcludedProducts( $excludedProductsIds );
350 $pricingRule->setExcludedUsersRole( $excludedUsersRole );
351 $pricingRule->setExcludedUsers( $excludedUsers );
352 // Status, priority and schedule come from the Rule status box; other save paths keep the stored values
353 if ( isset( $postedData[StatusBox::MARKER] ) ) {
354 // the Suspend / Reactivate button submits the form with its value; "Update" leaves the state alone
355 $suspendAction = ( isset( $postedData['tpt_suspend_action'] ) ? sanitize_key( $postedData['tpt_suspend_action'] ) : '' );
356 if ( 'suspend' === $suspendAction ) {
357 $pricingRule->setIsSuspended( true );
358 $this->getContainer()->getAdminNotifier()->flash( __( 'The rule suspended successfully.', 'tier-pricing-table' ), AdminNotifier::SUCCESS, true );
359 } elseif ( 'reactivate' === $suspendAction ) {
360 $pricingRule->setIsSuspended( false );
361 $this->getContainer()->getAdminNotifier()->flash( __( 'The rule reactivated successfully.', 'tier-pricing-table' ), AdminNotifier::SUCCESS, true );
362 }
363 $pricingRule->setPriority( ( isset( $postedData['tpt_priority'] ) ? sanitize_text_field( $postedData['tpt_priority'] ) : GlobalPricingRule::DEFAULT_PRIORITY ) );
364 $pricingRule->setStartDate( ( isset( $postedData['tpt_start_date'] ) ? sanitize_text_field( $postedData['tpt_start_date'] ) : null ) );
365 $pricingRule->setEndDate( ( isset( $postedData['tpt_end_date'] ) ? sanitize_text_field( $postedData['tpt_end_date'] ) : null ) );
366 if ( $pricingRule->getStartDate() && $pricingRule->getEndDate() && $pricingRule->getEndDate() < $pricingRule->getStartDate() ) {
367 $pricingRule->setEndDate( null );
368 $this->getContainer()->getAdminNotifier()->flash( __( 'The end date was before the start date, so it was cleared.', 'tier-pricing-table' ), AdminNotifier::WARNING, true );
369 }
370 }
371 RuleSettings::updateFromPOST( $ruleId );
372 do_action( 'tiered_pricing_table/global_pricing/before_updating', $pricingRule, $ruleId );
373 $pricingRule->save();
374 $this->getContainer()->getCache()->purge();
375 }
376
377 public function renderBlankState( $which ) {
378 global $post_type;
379 if ( self::SLUG === $post_type && 'top' === $which ) {
380 $counts = (array) wp_count_posts( $post_type );
381 unset($counts['auto-draft']);
382 $count = array_sum( $counts );
383 if ( 0 < $count ) {
384 return;
385 }
386 ?>
387
388 <div class="tpt-blank-state">
389 <div class="tpt-blank-state__inner">
390 <img class="tpt-blank-state__image"
391 src="<?php
392 echo esc_attr( $this->getContainer()->getFileManager()->locateAsset( 'admin/pricing-logo.png' ) );
393 ?>"
394 alt="Tiered Pricing Logo">
395
396 <h2 class="tpt-blank-state__title">
397 <?php
398 esc_html_e( 'Create Your First Global Pricing Rule', 'tier-pricing-table' );
399 ?>
400 </h2>
401
402 <p class="tpt-blank-state__description">
403 <?php
404 esc_html_e( 'Global rules allow you to bulk-apply custom pricing, tiered discounts, and quantity limits to multiple products or categories simultaneously.', 'tier-pricing-table' );
405 ?>
406 </p>
407
408 <div class="tpt-blank-state__actions">
409 <a class="tpt-button-primary button button-primary button-large"
410 href="<?php
411 echo esc_url( admin_url( 'post-new.php?post_type=' . self::SLUG ) );
412 ?>">
413 <span style="line-height: 1; padding-top: 2px;"><?php
414 esc_html_e( 'Create Rule', 'tier-pricing-table' );
415 ?></span>
416 </a>
417 </div>
418 </div>
419 </div>
420
421 <style>
422 .tpt-blank-state {
423 background: #ffffff;
424 border: 1px solid #e2e4e7;
425 border-radius: 8px;
426 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
427 text-align: center;
428 margin: 40px auto;
429 max-width: 600px;
430 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
431 overflow: hidden;
432 }
433
434 .tpt-blank-state__inner {
435 padding: 50px 40px;
436 }
437
438 .tpt-blank-state__image {
439 width: 140px;
440 height: auto;
441 margin: 0 auto 24px;
442 display: block;
443 filter: drop-shadow(0px 8px 16px rgba(0, 0, 0, 0.08));
444 }
445
446 .tpt-blank-state__title {
447 font-size: 24px;
448 font-weight: 600;
449 color: #1d2327;
450 margin: 0 0 12px 0;
451 line-height: 1.3;
452 }
453
454 .tpt-blank-state__description {
455 font-size: 15px;
456 color: #646970;
457 line-height: 1.6;
458 margin: 0 0 32px 0;
459 max-width: 480px;
460 margin-left: auto;
461 margin-right: auto;
462 }
463
464 .tpt-button-primary {
465 display: inline-flex;
466 align-items: center;
467 justify-content: center;
468 padding: 0 24px !important;
469 height: 42px !important;
470 font-size: 14px !important;
471 font-weight: 600 !important;
472 border-radius: 4px !important;
473 transition: all 0.2s ease;
474 }
475
476 .tpt-button-primary:hover {
477 transform: translateY(-1px);
478 box-shadow: 0 4px 8px rgba(0, 112, 188, 0.2);
479 }
480
481 #posts-filter .wp-list-table,
482 #posts-filter .tablenav.bottom,
483 .tablenav.top .actions,
484 .wrap .subsubsub {
485 display: none;
486 }
487
488 #posts-filter .tablenav.top {
489 height: auto;
490 }
491 </style>
492 <?php
493 }
494 }
495
496 public function register() {
497 if ( class_exists( '\\Automattic\\WooCommerce\\Admin\\PageController' ) ) {
498 PageController::get_instance()->connect_page( array(
499 'id' => self::SLUG,
500 'title' => array('Tiered Pricing'),
501 'screen_id' => self::SLUG,
502 ) );
503 }
504 register_post_type( self::SLUG, array(
505 'labels' => array(
506 'name' => __( 'Pricing rule', 'tier-pricing-table' ),
507 'singular_name' => __( 'Pricing rule', 'tier-pricing-table' ),
508 'add_new' => __( 'Add Pricing Rule', 'tier-pricing-table' ),
509 'add_new_item' => __( 'Add Pricing Rule', 'tier-pricing-table' ),
510 'edit_item' => __( 'Edit Pricing Rule', 'tier-pricing-table' ),
511 'new_item' => __( 'New Pricing Rule', 'tier-pricing-table' ),
512 'view_item' => __( 'View Pricing Rule', 'tier-pricing-table' ),
513 'search_items' => __( 'Find Pricing Rule', 'tier-pricing-table' ),
514 'not_found' => __( 'No pricing rules ware found', 'tier-pricing-table' ),
515 'not_found_in_trash' => __( 'No pricing rule in trash', 'tier-pricing-table' ),
516 'parent_item_colon' => '',
517 'menu_name' => __( 'Pricing rules', 'tier-pricing-table' ),
518 ),
519 'public' => false,
520 'publicly_queryable' => false,
521 'show_ui' => true,
522 'show_in_menu' => 'woocommerce',
523 'query_var' => false,
524 'rewrite' => false,
525 'capability_type' => 'product',
526 'has_archive' => false,
527 'hierarchical' => false,
528 'menu_position' => null,
529 'supports' => array('title'),
530 ) );
531 }
532
533 /**
534 * Published, not suspended rules in the order the storefront checks them: lowest priority number
535 * first, newest first among equal numbers.
536 *
537 * @param bool $withValidPricing Drop rules that change neither prices nor quantity limits.
538 * @param bool $onlyWithinSchedule Drop rules whose start date is still ahead or whose end date has passed.
539 *
540 * @return GlobalPricingRule[]
541 */
542 public static function getGlobalRules( bool $withValidPricing = true, bool $onlyWithinSchedule = true ) : array {
543 if ( !is_null( self::$globalRules ) ) {
544 $rules = self::$globalRules;
545 } else {
546 $rulesIds = get_posts( GlobalRulesQuery::args( self::SLUG ) );
547 $rules = array_map( function ( $ruleId ) {
548 return GlobalPricingRule::build( $ruleId );
549 }, $rulesIds );
550 usort( $rules, array(GlobalPricingRule::class, 'compareOrder') );
551 self::$globalRules = $rules;
552 }
553 if ( $onlyWithinSchedule ) {
554 $rules = array_filter( $rules, function ( GlobalPricingRule $rule ) {
555 return $rule->isWithinSchedule();
556 } );
557 }
558 if ( $withValidPricing ) {
559 $rules = array_filter( $rules, function ( GlobalPricingRule $rule ) {
560 return $rule->isValidPricing();
561 } );
562 }
563 return array_values( $rules );
564 }
565
566 }
567