PluginProbe
Tiered Pricing Table for WooCommerce / 8.0.2
Tiered Pricing Table for WooCommerce v8.0.2
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 2.3.3 All 91 releases
tier-pricing-table / src / PricingTable.php

PricingTable.php in Tiered Pricing Table for WooCommerce 8.0.2, at src/PricingTable.php

617 lines 22.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php namespace TierPricingTable;
2
3 use TierPricingTable\Admin\ProductPage\AdvanceOptionsForVariableProduct;
4 use TierPricingTable\Admin\ProductPage\TieredPricingTab;
5 use TierPricingTable\Core\ServiceContainerTrait;
6 use TierPricingTable\Settings\Sections\GeneralSection\GeneralSection;
7 use WC_Product;
8 use WC_Product_Data_Store_CPT;
9 use WC_Product_Variable;
10 use WC_Product_Variation;
11
12 class PricingTable {
13
14 use ServiceContainerTrait;
15
16 private static $instance;
17
18 private function __construct() {}
19
20 public static function getInstance(): self {
21 if ( is_null( self::$instance ) ) {
22 self::$instance = new self();
23 }
24
25 return self::$instance;
26 }
27
28 /**
29 * Main function for rendering pricing table for product
30 *
31 * @param int $parentProductID
32 * @param int $variationID
33 * @param array $settings
34 */
35 public function renderPricingTable( $parentProductID = 0, $variationID = null, array $settings = array() ) {
36
37 if ( apply_filters( 'tiered_pricing_table/should_render_pricing_table', true, $parentProductID, $variationID,
38 $settings ) === false ) {
39 return;
40 }
41
42 $parentProduct = wc_get_product( $parentProductID );
43
44 if ( ! $parentProduct ) {
45 return;
46 }
47
48 $settings = wp_parse_args( $settings, $this->getDefaultSettings( $parentProductID ) );
49
50 // If the product is variable, but no specific variation is passed - check for default
51 if ( ! $variationID && TierPricingTablePlugin::isVariableProductSupported( $parentProduct ) ) {
52 $variationID = $this->getDefaultVariation( $parentProduct );
53 }
54
55 $ajaxVariationsThreshold = apply_filters( 'tiered_pricing_table/ajax_variations_threshold', 10 );
56
57 // Use variation if exists, otherwise the parent product is used
58 $productId = $variationID ? $variationID : $parentProduct->get_id();
59 $product = wc_get_product( $productId );
60
61 $supportedTypes = array_merge( TierPricingTablePlugin::getSupportedSimpleProductTypes(),
62 TierPricingTablePlugin::getSupportedVariableProductTypes() );
63
64 // Exit if product is not valid
65 if ( ! $product || ! in_array( $parentProduct->get_type(), $supportedTypes ) ) {
66 return;
67 }
68
69 $settings = apply_filters( 'tiered_pricing_table/display_settings', $settings, $productId );
70
71 if ( 'tooltip' === $settings['display_type'] ) {
72 wp_enqueue_script( 'jquery-ui-tooltip' );
73 }
74
75 $hidden = ( 'tooltip' === $settings['display_type'] || ! $settings['display'] );
76
77 do_action( 'tiered_pricing_table/before_rendering_tiered_pricing', $parentProduct, $variationID, $settings );
78
79 $loadVariationByAjax = true;
80
81 ?>
82 <div class="clear"></div>
83 <div class="tpt__tiered-pricing <?php echo esc_attr( $hidden ? 'tpt__hidden' : '' ); ?>"
84 data-settings="<?php echo esc_attr( json_encode( $settings ) ); ?>"
85 data-display-context="<?php echo esc_attr( $settings['display_context'] ); ?>"
86 data-display-type="<?php echo esc_attr( $settings['display_type'] ); ?>"
87 data-product-id="<?php echo esc_attr( $parentProduct->get_id() ); ?>"
88 data-product-type="<?php echo esc_attr( $parentProduct->get_type() ); ?>"
89
90 <?php if ( TierPricingTablePlugin::isVariableProductSupported( $parentProduct ) ) : ?>
91 <?php
92 $loadVariationByAjax = count( $parentProduct->get_children() ) > $ajaxVariationsThreshold;
93 $variableProductSamePrices = AdvanceOptionsForVariableProduct::isVariableProductSamePrices( $parentProduct->get_id() );
94 ?>
95 data-load-variation-by-ajax="<?php echo esc_attr( wc_bool_to_string( $loadVariationByAjax ) ); ?>"
96 data-variable-product-same-prices="<?php echo esc_attr( wc_bool_to_string( $variableProductSamePrices ) ); ?>"
97 <?php endif; ?>>
98
99 <?php $this->renderPricingTableHTML( $parentProduct, $product, $settings ); ?>
100 </div>
101
102 <?php if ( TierPricingTablePlugin::isVariableProductSupported( $parentProduct ) && ! $loadVariationByAjax ) : ?>
103 <div class="tpt__tiered-pricing-preloaded-variations" style="display:none">
104 <?php foreach ( $parentProduct->get_available_variations( 'objects' ) as $childProduct ) : ?>
105 <div class="tiered-pricing-preloaded-variation"
106 data-variation-id="<?php echo esc_attr( $childProduct->get_id() ); ?>"
107 data-display-context="<?php echo esc_attr( $settings['display_context'] ); ?>">
108 <?php $this->renderPricingTableHTML( $parentProduct, $childProduct, $settings ); ?>
109 </div>
110 <?php endforeach; ?>
111 </div>
112 <?php endif; ?>
113 <?php
114 }
115
116 /**
117 * Render pricing table without wrapper
118 *
119 * @param WC_Product $parentProduct
120 * @param WC_Product $product
121 * @param array $settings
122 */
123 public function renderPricingTableHTML(
124 WC_Product $parentProduct,
125 WC_Product $product,
126 array $settings = array()
127 ) {
128
129 $settings = wp_parse_args( $settings, $this->getDefaultSettings( $parentProduct->get_id() ) );
130
131 // Don't get rules from variable product when variation is empty
132 if ( in_array( $parentProduct->get_type(),
133 TierPricingTablePlugin::getSupportedVariableProductTypes() ) && ! ( $product instanceof WC_Product_Variation ) ) {
134 // Empty rule
135 $priceRule = new PricingRule( $product->get_id() );
136 } else {
137 $priceRule = PriceManager::getPricingRule( $product->get_id() );
138 }
139
140 // If rules are not empty
141 if ( ! empty( $priceRule->getRules() ) ) {
142
143 $displayType = $settings['display_type'];
144 $availableTemplates = TierPricingTablePlugin::getAvailablePricingLayouts();
145
146 $templateType = array_key_exists( $settings['display_type'], $availableTemplates ) ? $displayType : 'table';
147
148 if ( 'tooltip' === $displayType ) {
149 $templateType = 'table';
150 }
151
152 if ( 'table' === $displayType ) {
153 $style = $settings['table_style'];
154
155 if ( $style !== 'default' ) {
156 $templateType = 'table-' . $style;
157 } else {
158 $templateType = 'table';
159 }
160 }
161
162 if ( 'blocks' === $displayType ) {
163 $style = $settings['blocks_style'];
164
165 if ( $style !== 'default' ) {
166 $templateType = 'blocks-' . $style;
167 } else {
168 $templateType = 'blocks';
169 }
170 }
171
172 if ( 'options' === $displayType ) {
173 $style = $settings['options_style'];
174
175 if ( $style !== 'default' ) {
176 $templateType = 'options-' . $style;
177 } else {
178 $templateType = 'options';
179 }
180 }
181
182 // layouts with a single style option: dropdown, plain text
183 $styledLayouts = array(
184 'dropdown' => 'dropdown_style',
185 'plain-text' => 'plain_text_style',
186 );
187
188 if ( isset( $styledLayouts[ $displayType ] ) ) {
189 $style = $settings[ $styledLayouts[ $displayType ] ] ?? 'default';
190 $templateType = 'default' !== $style && '' !== $style ? $displayType . '-' . $style : $displayType;
191 }
192
193 $template = "tiered-pricing-$templateType.php";
194
195 do_action( 'tiered_pricing_table/before_rendering_tiered_pricing/inner', $priceRule, $product, $settings );
196
197 $this->checkForDuplicateQuantities( $priceRule );
198
199 $this->getContainer()->getFileManager()->includeTemplate( 'frontend/' . $template, array(
200 'pricing_rule' => $priceRule,
201 'price_rules' => $priceRule->getRules(),
202 'real_price' => $product->get_price(),
203 'product_name' => $product->get_name(),
204 'product_id' => $product->get_id(),
205 'product' => $product,
206 'minimum' => $priceRule->getMinimum( true ),
207 'settings' => $settings,
208 'pricing_type' => $priceRule->getType(),
209 'id' => $this->getUniqueTieredPricingId(),
210 ) );
211
212 $this->renderMoreTiersLink( $parentProduct );
213 } else {
214 // Provide fallback data for products without pricing rules,
215 // which allows the frontend JS (e.g. dynamic totals, you save, etc.)
216 // to function for all products via the 'tiered_price_update' event.
217 $price_incl_taxes = wc_get_price_including_tax( $product, array(
218 'price' => $product->get_price(),
219 ) );
220
221 $price_excl_taxes = wc_get_price_excluding_tax( $product, array(
222 'price' => $product->get_price(),
223 ) );
224
225 $sale_price = $product->get_sale_price();
226
227 if ( $sale_price ) {
228 $sale_price = wc_get_price_to_display( $product, array(
229 'price' => $sale_price,
230 ) );
231 }
232
233 $regular_price = wc_get_price_to_display( $product, array(
234 'price' => $product->get_regular_price(),
235 ) );
236
237 $price = wc_get_price_to_display( $product, array(
238 'price' => $product->get_price(),
239 ) );
240
241 ?>
242 <div style="display:none;"
243 class="tiered-pricing-fallback-data"
244 data-product-id="<?php echo esc_attr( $product->get_id() ); ?>"
245 data-price-rules="{}"
246 data-minimum="<?php echo esc_attr( $priceRule->getMinimum( true ) ); ?>"
247 data-product-name="<?php echo esc_attr( $product->get_name() ); ?>"
248 data-regular-price="<?php echo esc_attr( $regular_price ); ?>"
249 data-sale-price="<?php echo esc_attr( $sale_price ); ?>"
250 data-price="<?php echo esc_attr( $price ); ?>"
251 data-product-price-suffix="<?php echo esc_attr( $product->get_price_suffix() ); ?>"
252 data-tiered-price="<?php echo esc_attr( $price ); ?>"
253 data-tiered-price-exclude-taxes="<?php echo esc_attr( $price_excl_taxes ); ?>"
254 data-tiered-price-include-taxes="<?php echo esc_attr( $price_incl_taxes ); ?>"
255 ></div>
256 <?php
257 }
258 }
259
260 /**
261 * Discount of a tier as the store shows it: a percentage, the amount saved per item, or both.
262 *
263 * @param float $percent Discount in percent, as the templates compute it.
264 * @param PricingRule $pricingRule
265 * @param WC_Product $product
266 * @param int|null $quantity Tier quantity, or null for the base tier (its discount is the sale price).
267 * @param array $settings Renderer settings.
268 * @param string $style 'plain' ("10%", "$4.50", "10% ($4.50)") or 'off' ("10% off", "$4.50 off", "10% off, save $4.50").
269 *
270 * @return string HTML
271 */
272 public static function formatDiscount(
273 float $percent,
274 PricingRule $pricingRule,
275 WC_Product $product,
276 ?int $quantity,
277 array $settings,
278 string $style = 'plain'
279 ): string {
280 $format = $settings['discount_format'] ?? 'percentage';
281 $format = in_array( $format, array( 'percentage', 'amount', 'both' ), true ) ? $format : 'percentage';
282
283 $percentLabel = round( $percent, 2 ) . '%';
284 $amountLabel = '';
285
286 if ( 'percentage' !== $format ) {
287 $saving = 0;
288
289 if ( null === $quantity ) {
290 if ( $product->is_on_sale() ) {
291 $saving = (float) $product->get_regular_price() - (float) $product->get_sale_price();
292 }
293 } else {
294 $base = CalculationLogic::calculateDiscountBasedOnRegularPrice() ? $product->get_regular_price() : $product->get_price();
295 $saving = (float) $base - (float) $pricingRule->getTierPrice( $quantity, false );
296 }
297
298 $amountLabel = wc_price( wc_get_price_to_display( $product, array( 'price' => max( 0, $saving ) ) ) );
299 }
300
301 if ( 'off' === $style ) {
302 switch ( $format ) {
303 case 'amount':
304 // translators: %s: amount saved per item
305 return sprintf( __( '%s off', 'tier-pricing-table' ), $amountLabel );
306 case 'both':
307 // translators: 1: discount percentage, 2: amount saved per item
308 return sprintf( __( '%1$s off, save %2$s', 'tier-pricing-table' ), $percentLabel, $amountLabel );
309 default:
310 // translators: %s: discount percentage
311 return sprintf( __( '%s off', 'tier-pricing-table' ), $percentLabel );
312 }
313 }
314
315 switch ( $format ) {
316 case 'amount':
317 return $amountLabel;
318 case 'both':
319 return $percentLabel . ' (' . $amountLabel . ')';
320 default:
321 return $percentLabel;
322 }
323 }
324
325 /**
326 * The "Spacing" and "Cell padding" options as a style attribute for a layout's root element: CSS
327 * variables the stylesheet reads for the gap between tiers and the padding inside table cells.
328 * Empty when neither option is set (each style keeps its own spacing and padding).
329 */
330 public static function layoutStyleAttribute( array $settings ): string {
331 $variables = array();
332
333 foreach ( array( 'layout_spacing' => '--tiered-pricing-gap', 'cell_padding' => '--tiered-pricing-cell-padding' ) as $option => $variable ) {
334 $value = $settings[ $option ] ?? '';
335
336 if ( '' === $value || null === $value || ! is_numeric( $value ) ) {
337 continue;
338 }
339
340 $variables[] = $variable . ': ' . max( 0, min( 80, (int) $value ) ) . 'px';
341 }
342
343 return $variables ? ' style="' . esc_attr( implode( '; ', $variables ) ) . '"' : '';
344 }
345
346 /**
347 * @deprecated 7.1.9 Use layoutStyleAttribute().
348 */
349 public static function spacingAttribute( array $settings ): string {
350 return self::layoutStyleAttribute( $settings );
351 }
352
353 /**
354 * Tiers the last rendered layout left out because of the "tiers per grid item" limit.
355 */
356 protected static $hiddenTiers = 0;
357
358 /**
359 * Tier rows in the configured order (ascending by quantity, or the biggest discount first), cut to
360 * the "tiers per grid item" limit when one is set.
361 *
362 * @param string[] $rows Rendered rows, ascending.
363 * @param array $settings Renderer settings.
364 *
365 * @return string HTML (the rows were escaped when rendered)
366 */
367 public static function orderTiers( array $rows, array $settings ): string {
368 if ( 'desc' === ( $settings['tiers_order'] ?? 'asc' ) ) {
369 $rows = array_reverse( $rows );
370 }
371
372 $limit = (int) ( $settings['tiers_limit'] ?? 0 );
373 self::$hiddenTiers = 0;
374
375 if ( $limit > 0 && count( $rows ) > $limit ) {
376 self::$hiddenTiers = count( $rows ) - $limit;
377 $rows = array_slice( $rows, 0, $limit );
378 }
379
380 return implode( '', $rows );
381 }
382
383 /**
384 * The "+N more tiers" link to the product page, after a layout the limit shortened.
385 */
386 protected function renderMoreTiersLink( WC_Product $parentProduct ) {
387 $hidden = self::$hiddenTiers;
388 self::$hiddenTiers = 0;
389
390 if ( $hidden < 1 ) {
391 return;
392 }
393
394 /* translators: %d: number of tiers not listed */
395 $label = sprintf( _n( '+%d more tier', '+%d more tiers', $hidden, 'tier-pricing-table' ), $hidden );
396
397 echo '<a class="tiered-pricing-more-tiers" href="' . esc_url( $parentProduct->get_permalink() ) . '">' . esc_html( $label ) . '</a>';
398 }
399
400 protected function getDefaultSettings( $productId = false ): array {
401 $hasRules = true;
402
403 if ( $productId ) {
404 $product = wc_get_product( $productId );
405 if ( $product ) {
406 $hasRules = $this->productHasPricingRules( $product );
407 }
408 }
409
410 $showTotalPrice = $hasRules ? $this->getContainer()->getSettings()->get( 'show_total_price',
411 'no' ) === 'yes' : $this->getContainer()->getSettings()->get( 'show_total_price_non_tiered',
412 'no' ) === 'yes';
413
414 $settings = array(
415 'display_context' => 'product-page',
416 'display' => GeneralSection::isAutomaticDisplayEnabled(),
417 'display_type' => $this->getContainer()->getSettings()->get( 'display_type', 'table' ),
418 'title' => $this->getContainer()->getSettings()->get( 'table_title', '' ),
419 'table_class' => $this->getContainer()->getSettings()->get( 'table_css_class', '' ),
420 'quantity_column_title' => $this->getContainer()->getSettings()->get( 'head_quantity_text',
421 __( 'Quantity', 'tier-pricing-table' ) ),
422 'price_column_title' => $this->getContainer()->getSettings()->get( 'head_price_text',
423 __( 'Price', 'tier-pricing-table' ) ),
424 'discount_column_title' => $this->getContainer()->getSettings()->get( 'head_discount_text',
425 __( 'Discount (%)', 'tier-pricing-table' ) ),
426 'quantity_type' => $this->getContainer()->getSettings()->get( 'quantity_type', 'range' ),
427 'show_discount_column' => $this->getContainer()->getSettings()->get( 'show_discount_column',
428 'yes' ) === 'yes',
429 'clickable_rows' => $this->getContainer()->getSettings()->get( 'clickable_table_rows',
430 'yes' ) === 'yes',
431 'active_tier_color' => $this->getContainer()->getSettings()->get( 'selected_quantity_color',
432 '#3858e9' ),
433 'discount_format' => $this->getContainer()->getSettings()->get( 'discount_format', 'percentage' ),
434 'layout_spacing' => $this->getContainer()->getSettings()->get( 'layout_spacing', '' ),
435 'cell_padding' => $this->getContainer()->getSettings()->get( 'cell_padding', '' ),
436 'discount_badge_color' => $this->getContainer()->getSettings()->get( 'discount_badge_color', '' ),
437 'active_tier_border' => $this->getContainer()->getSettings()->get( 'table_active_border', 'yes' ),
438 'tiers_order' => $this->getContainer()->getSettings()->get( 'tiers_order', 'asc' ),
439 'tooltip_border' => $this->getContainer()->getSettings()->get( 'tooltip_border',
440 'yes' ) === 'yes',
441
442 'table_style' => GeneralSection::getPricingTableStyle(),
443 'compact_layout' => $this->getContainer()->getSettings()->get( 'compact_layout', 'no' ),
444 'blocks_style' => GeneralSection::getPricingBlocksStyle(),
445 'options_style' => GeneralSection::getPricingOptionsStyle(),
446
447 'dropdown_style' => GeneralSection::getPricingDropdownStyle(),
448 'plain_text_style' => GeneralSection::getPricingPlainTextStyle(),
449
450 'options_show_total' => GeneralSection::isShowOptionTotal(),
451 'options_show_original_product_price' => GeneralSection::isShowOriginalProductPrice(),
452 'options_show_default_option' => GeneralSection::isDefaultOptionEnabled(),
453
454 'options_default_option_text' => GeneralSection::getDefaultOptionText(),
455 'options_option_text' => GeneralSection::getOptionText(),
456
457 'plain_text_show_default_option' => GeneralSection::isPlainTextFirstTierEnabled(),
458 'plain_text_option_text' => GeneralSection::getPlainTextTemplate(),
459 'plain_text_default_option_text' => GeneralSection::getPlainTextFirstTierTemplate(),
460
461 'update_price_on_product_page' => $this->getContainer()->getSettings()->get( 'update_price_on_product_page',
462 'yes' ) === 'yes',
463 'show_tiered_price_as_discount' => $this->getContainer()->getSettings()->get( 'show_tiered_price_as_discount',
464 'yes' ) === 'yes',
465 'show_total_price' => $showTotalPrice,
466 );
467
468 $default_quantity_measurement = array(
469 'singular' => '',
470 'plural' => '',
471 );
472
473 $quantity_measurement = $default_quantity_measurement;
474
475 if ( in_array( $settings['display_type'], array( 'table', 'horizontal-table', 'tooltip' ) ) ) {
476 $quantity_measurement = $this->getContainer()->getSettings()->get( 'table_quantity_measurement',
477 $default_quantity_measurement );
478 }
479
480 if ( 'blocks' === $settings['display_type'] ) {
481 $quantity_measurement = $this->getContainer()->getSettings()->get( 'blocks_quantity_measurement', array(
482 'singular' => _n( 'piece', 'pieces', 1, 'tier-pricing-table' ),
483 'plural' => _n( 'piece', 'pieces', 2, 'tier-pricing-table' ),
484 ) );
485 }
486
487 $settings['quantity_measurement_singular'] = $quantity_measurement['singular'];
488 $settings['quantity_measurement_plural'] = $quantity_measurement['plural'];
489
490 if ( $productId ) {
491 $template = TieredPricingTab::getProductTemplate( $productId );
492
493 if ( 'default' !== $template ) {
494 $settings['display_type'] = $template;
495 }
496
497 $baseUnitName = TieredPricingTab::getProductBaseUnitName( $productId );
498
499 if ( $baseUnitName['singular'] && $baseUnitName['plural'] ) {
500 $settings['quantity_measurement_singular'] = $baseUnitName['singular'];
501 $settings['quantity_measurement_plural'] = $baseUnitName['plural'];
502 }
503 }
504
505 return $settings;
506 }
507
508 protected function getUniqueTieredPricingId() {
509 return preg_replace( '/[0-9]+/', '', strtolower( wp_generate_password( 20, false ) ) );
510 }
511
512 protected function getDefaultVariation( WC_Product_Variable $product ): ?int {
513
514 $defaultVariation = AdvanceOptionsForVariableProduct::getDefaultVariation( $product->get_id() );
515
516 if ( $defaultVariation ) {
517 return $defaultVariation->get_id();
518 }
519
520 $defaultAttributes = $product->get_default_attributes();
521
522 if ( ! empty( $defaultAttributes ) ) {
523
524 $defaultAttributesKeys = array_map( function ( $attribute ) {
525 return 'attribute_' . $attribute;
526 }, array_keys( $defaultAttributes ) );
527
528 $defaultAttributes = array_combine( $defaultAttributesKeys, $defaultAttributes );
529
530 return ( new WC_Product_Data_Store_CPT() )->find_matching_product_variation( $product, $defaultAttributes );
531 }
532
533 return null;
534 }
535
536 public function productHasPricingRules( WC_Product $product ): bool {
537
538 if ( TierPricingTablePlugin::isSimpleProductSupported( $product ) ) {
539 $pricingRule = PriceManager::getPricingRule( $product->get_id() );
540
541 return ! empty( $pricingRule->getRules() );
542 }
543
544 if ( TierPricingTablePlugin::isVariableProductSupported( $product ) && $product instanceof WC_Product_Variable ) {
545
546 // Do not check if product has tiered pricing for variable product by default.
547 // If a variable product has many variations, it can take many resources to check every variation for rules existing.
548 // The downside is that the plugin will send an AJAX request for each variant selection on the product page even if a product does not have any tiered pricing
549 if ( ! apply_filters( 'tiered_pricing_table/check_if_variable_product_has_rules', false, $product ) ) {
550 return true;
551 }
552
553 $hasTieredPricing = $this->getContainer()->getCache()->getProductData( $product, 'product_has_rules' );
554
555 if ( 'no' === $hasTieredPricing ) {
556 return false;
557 }
558
559 if ( 'yes' === $hasTieredPricing ) {
560 return true;
561 }
562
563 foreach ( $product->get_available_variations() as $productVariation ) {
564 $pricingRule = PriceManager::getPricingRule( $productVariation['variation_id'] );
565
566 if ( ! empty( $pricingRule->getRules() ) ) {
567 $this->getContainer()->getCache()->setProductData( $product, 'product_has_rules', 'yes' );
568
569 return true;
570 }
571 }
572
573 $this->getContainer()->getCache()->setProductData( $product, 'product_has_rules', 'no' );
574 }
575
576 return false;
577 }
578
579 public function checkForDuplicateQuantities( PricingRule $pricingRule ) {
580 // Show notice only for admin users
581 if ( ! current_user_can( 'manage_options' ) ) {
582 return;
583 }
584 $minimum = $pricingRule->getMinimum( true );
585 $pricingRules = $pricingRule->getRules();
586 $firstRuleQuantity = ! empty( $pricingRules ) ? array_keys( $pricingRules )[0] : false;
587
588 if ( $firstRuleQuantity && $firstRuleQuantity <= $minimum ) {
589 ?>
590
591 <div style="font-size: .8em;">
592 <div class="woocommerce-error" role="alert" style="margin-bottom: 0">
593 <?php esc_html_e( 'Pricing Conflict Detected: Your Minimum Order Quantity is equal to or greater than your first pricing tier.',
594 'tier-pricing-table' ); ?>
595 </div>
596 <div style="color: #555;
597 padding: 10px 12px;
598 border: 1px solid #b32c2e;
599 background: #fff5f5;">
600 <p style="padding: 0; margin: 0 0 10px 0;">
601 <?php esc_html_e( 'To fix this, please ensure your first tiered pricing rule starts at a higher quantity than your Minimum Order Quantity.',
602 'tier-pricing-table' ); ?>
603 </p>
604 <p style="padding: 0; margin: 0">
605 <strong><?php esc_html_e( 'Why?',
606 'tier-pricing-table' ); ?></strong> <?php esc_html_e( 'The plugin automatically creates a base tier using your standard WooCommerce product price. This base tier covers any quantities ordered between the Minimum Order Quantity and your first discounted tier.',
607 'tier-pricing-table' ); ?>
608 </p>
609 <br>
610 <b><?php esc_html_e( 'This notice is only visible to store administrators.',
611 'tier-pricing-table' ); ?></b>
612 </div>
613 </div>
614 <?php
615 }
616 }
617 }