PluginProbe
Tiered Pricing Table for WooCommerce / trunk
Tiered Pricing Table for WooCommerce vtrunk
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 2.3.4 All 90 releases
tier-pricing-table / src / PriceManager.php

PriceManager.php in Tiered Pricing Table for WooCommerce trunk, at src/PriceManager.php

298 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TierPricingTable;
4
5 use WC_Product;
6 class PriceManager {
7 protected static $pricingRules = array();
8
9 public static function getFixedPriceRules( $productId, string $context = 'view' ) : array {
10 return self::getPriceRules( $productId, 'fixed', $context );
11 }
12
13 public static function getPercentagePriceRules( $productId, string $context = 'view' ) : array {
14 return self::getPriceRules( $productId, 'percentage', $context );
15 }
16
17 public static function getPriceRules( $productId, ?string $type = null, string $context = 'view' ) : array {
18 $type = ( $type ? $type : self::getPricingType( $productId, 'fixed', $context ) );
19 if ( 'fixed' === $type ) {
20 $rules = (array) get_post_meta( $productId, '_fixed_price_rules', true );
21 } else {
22 $rules = (array) get_post_meta( $productId, '_percentage_price_rules', true );
23 }
24 $rules = array_map( function ( $price ) {
25 return floatval( $price );
26 }, $rules );
27 $rules = ( !empty( $rules ) ? array_filter( $rules ) : array() );
28 ksort( $rules );
29 if ( 'edit' !== $context ) {
30 $rules = apply_filters(
31 'tiered_pricing_table/price/product_price_rules',
32 $rules,
33 $productId,
34 $type
35 );
36 }
37 return array_filter( $rules, function ( $quantity ) {
38 return intval( $quantity ) > 1;
39 }, ARRAY_FILTER_USE_KEY );
40 }
41
42 /**
43 * Get price by product quantity
44 *
45 * @param int $quantity
46 * @param int $productId
47 * @param ?string $context
48 * @param ?string $place
49 * @param bool $withTaxes
50 * @param ?PricingRule $pricingRule
51 * @param bool $roundPrice
52 *
53 * @return bool|float|int
54 */
55 public static function getPriceByRules(
56 $quantity,
57 $productId,
58 $context = 'view',
59 $place = 'shop',
60 bool $withTaxes = true,
61 ?PricingRule $pricingRule = null,
62 ?bool $roundPrice = null
63 ) {
64 $pricingRule = ( $pricingRule ? $pricingRule : self::getPricingRule( $productId ) );
65 $roundPrice = ( !is_null( $roundPrice ) ? $roundPrice : CalculationLogic::roundPrice() );
66 foreach ( array_reverse( $pricingRule->getRules(), true ) as $_amount => $price ) {
67 if ( $_amount <= $quantity ) {
68 if ( $pricingRule->isPercentage() ) {
69 $product = wc_get_product( $productId );
70 if ( $product ) {
71 $productPrice = self::getProductPriceWithPercentageDiscount( $product, $price, $context );
72 }
73 } else {
74 $productPrice = $price;
75 }
76 if ( 'view' === $context && $withTaxes ) {
77 $product = wc_get_product( $productId );
78 $productPrice = self::getPriceToDisplay( $productPrice, $product, $place );
79 }
80 break;
81 }
82 }
83 $productPrice = $productPrice ?? false;
84 if ( $productPrice && apply_filters( 'tiered_pricing_table/price/round_price', $roundPrice ) ) {
85 $roundPrecision = (int) apply_filters( 'tiered_pricing_table/price/round_precision', max( 2, wc_get_price_decimals() ) );
86 $productPrice = round( $productPrice, $roundPrecision );
87 }
88 if ( 'edit' !== $context ) {
89 return apply_filters(
90 'tiered_pricing_table/price/price_by_rules',
91 $productPrice,
92 $quantity,
93 $productId,
94 $context,
95 $place,
96 $pricingRule
97 );
98 }
99 return $productPrice;
100 }
101
102 /**
103 * Calculate displayed price depend on taxes
104 *
105 * @param float $price
106 * @param WC_Product $product
107 * @param ?string $displayContext
108 *
109 * @return ?float
110 */
111 public static function getPriceToDisplay( $price, WC_Product $product, ?string $displayContext = 'shop' ) : ?float {
112 if ( wc_tax_enabled() ) {
113 $price = wc_get_price_to_display( $product, array(
114 'price' => $price,
115 'qty' => 1,
116 'display_context' => $displayContext,
117 ) );
118 }
119 return floatval( $price );
120 }
121
122 /**
123 * Calculate price using percentage discount
124 *
125 * @param float $price
126 * @param float $discount
127 *
128 * @return bool|float
129 */
130 public static function getPriceByPercentDiscount( $price, $discount ) {
131 if ( $price > 0 && $discount <= 100 ) {
132 $discount_amount = $price / 100 * $discount;
133 return $price - $discount_amount;
134 }
135 return false;
136 }
137
138 public static function getProductPriceWithPercentageDiscount( WC_Product $product, $discount, $context = 'view' ) {
139 $discount = floatval( $discount );
140 $productPrice = ( CalculationLogic::calculateDiscountBasedOnRegularPrice() ? $product->get_regular_price( $context ) : $product->get_price( $context ) );
141 return self::getPriceByPercentDiscount( $productPrice, $discount );
142 }
143
144 public static function getPricingType( $productId, string $default = 'fixed', string $context = 'view' ) : string {
145 if ( !tpt_fs()->can_use_premium_code() ) {
146 return 'fixed';
147 }
148 $type = get_post_meta( $productId, '_tiered_price_rules_type', true );
149 $type = ( in_array( $type, array('fixed', 'percentage') ) ? $type : $default );
150 if ( 'edit' !== $context ) {
151 return apply_filters( 'tiered_pricing_table/price/type', $type, $productId );
152 }
153 return $type;
154 }
155
156 /**
157 * Update product pricing type
158 *
159 * @param int $productId
160 * @param string $type
161 */
162 public static function updatePriceRulesType( $productId, string $type ) {
163 if ( in_array( $type, array('percentage', 'fixed') ) ) {
164 update_post_meta( $productId, '_tiered_price_rules_type', $type );
165 }
166 }
167
168 /**
169 * Get product minimum quantity
170 *
171 * @param int $productId
172 * @param ?string $context
173 *
174 * @return int
175 */
176 public static function getProductQtyMin( $productId, ?string $context = 'view' ) : ?int {
177 return null;
178 }
179
180 public static function isProductMixAndMatch( $productId ) {
181 $mixAndMatch = get_post_meta( $productId, '_tiered_price_mix_and_match_minimum', true );
182 if ( '' !== $mixAndMatch ) {
183 return 'yes' === $mixAndMatch;
184 }
185 $product = wc_get_product( $productId );
186 if ( $product && $product->is_type( 'variation' ) ) {
187 $parentMixAndMatch = get_post_meta( $product->get_parent_id(), '_tiered_price_mix_and_match_minimum', true );
188 if ( '' !== $parentMixAndMatch ) {
189 return 'yes' === $parentMixAndMatch;
190 }
191 }
192 return CalculationLogic::isMixAndMatchMinimumEnabled();
193 }
194
195 /**
196 * Update product minimum quantity
197 *
198 * @param int $productId
199 * @param ?int $minimum
200 */
201 public static function updateProductMinimumQuantity( $productId, ?int $minimum ) {
202 if ( !$minimum ) {
203 delete_post_meta( $productId, '_tiered_price_minimum_qty' );
204 } else {
205 update_post_meta( $productId, '_tiered_price_minimum_qty', $minimum );
206 }
207 }
208
209 public static function calculateDiscount( $originalPrice, $currentPrice ) {
210 if ( $currentPrice >= $originalPrice ) {
211 return 0;
212 }
213 return 100 * ($originalPrice - $currentPrice) / $originalPrice;
214 }
215
216 /**
217 * Main function to get pricing information for a product.
218 *
219 * @param int $productId
220 * @param ?string $tieredPricingType - 'percentage' or 'fixed'. Leave null to use default.
221 *
222 * @return PricingRule
223 */
224 public static function getPricingRule( $productId, ?string $tieredPricingType = null ) : PricingRule {
225 /****************************************
226 *
227 * Object cache
228 *
229 ****************************************/
230 if ( array_key_exists( $productId, self::$pricingRules ) && !$tieredPricingType ) {
231 return self::$pricingRules[$productId];
232 }
233 /****************************************
234 *
235 * Initialize variables
236 *
237 ****************************************/
238 $product = null;
239 $pricingRule = new PricingRule($productId);
240 $tieredPricingType = ( $tieredPricingType ? $tieredPricingType : self::getPricingType( $productId ) );
241 /****************************************
242 *
243 * Set tiered pricing for the product
244 *
245 ****************************************/
246 $tieredPricingRules = self::getPriceRules( $productId, $tieredPricingType );
247 // If product does not have rules, check parent product
248 if ( empty( $tieredPricingRules ) ) {
249 $product = wc_get_product( $productId );
250 if ( $product && TierPricingTablePlugin::isVariationProductSupported( $product ) ) {
251 $tieredPricingType = self::getPricingType( $product->get_parent_id() );
252 $tieredPricingRules = self::getPriceRules( $product->get_parent_id(), $tieredPricingType );
253 if ( !empty( $tieredPricingRules ) ) {
254 $pricingRule->logPricingModification( 'Using parent product pricing rules' );
255 }
256 }
257 }
258 $pricingRule->setRules( $tieredPricingRules );
259 $pricingRule->setType( $tieredPricingType );
260 /****************************************
261 *
262 * Set minimum quantity for the product
263 *
264 ****************************************/
265 $minimum = self::getProductQtyMin( $productId );
266 $isMixAndMatch = self::isProductMixAndMatch( $productId );
267 // If product does not have minimum quantity, check parent product
268 if ( !$minimum ) {
269 if ( $product && TierPricingTablePlugin::isVariationProductSupported( $product ) ) {
270 $parentIsMixAndMatch = $isMixAndMatch;
271 if ( !$parentIsMixAndMatch ) {
272 $minimum = self::getProductQtyMin( $product->get_parent_id() );
273 if ( $minimum ) {
274 $pricingRule->logPricingModification( 'Using parent product minimum quantity' );
275 }
276 }
277 }
278 }
279 $pricingRule->setMinimum( $minimum );
280 $pricingRule->setMixAndMatchMinQuantity( $isMixAndMatch );
281 /*****************************************
282 *
283 * Services that modify pricing rule
284 *
285 * @hooked QuantityManager - 1: Added maximum and quantity step information.
286 * @hooked CategoryTierAddon - 10: Filter with category-based rules
287 * @hooked RoleBasedPricingAddon - 20: Filter with role-based rules
288 * @hooked RoleBasedPricingAddon - 25: Filter with customer-based rules
289 * @hooked GlobalPricingService - 30: Filter with global rules
290 *
291 *****************************************/
292 $pricingRule = apply_filters( 'tiered_pricing_table/price/pricing_rule', $pricingRule, $productId );
293 self::$pricingRules[$productId] = $pricingRule;
294 return $pricingRule;
295 }
296
297 }
298