PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.17
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.17
1.4.17 1.4.16 1.4.15 1.4.14 1.4.13 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.54 1.3.53 1.3.52 1.3.51 1.3.50 1.3.49 1.3.48 All 180 releases
← All changes | app/Intents/IntentHelper.php +81 -7 1.4.151.4.17 View file →
@@ -493,8 +493,37 @@
493 493 }
494 494
495 495 $discountable_units = $this->grouped_discount_budget( $rule, $total_qualifying_units );
496 496
497 + /**
498 + * A flat amount is charged for the pooled group as a whole.
499 + *
500 + * Pooling exists so the cart together can reach the tier minimum, so
501 + * the rule qualifies for the group rather than for each line that
502 + * contributed quantity. The Calc layer returns the rule value whole
503 + * for every line it is handed, because a flat amount does not scale
504 + * with units, so spreading it across lines multiplied the discount by
505 + * the line count: a flat 10 came out as 20 over two lines.
506 + *
507 + * It is applied to the first eligible line carrying the entire pooled
508 + * budget, which is what lets a recursive rule still charge once per
509 + * bundle: the Calc layer derives the bundle count from that quantity.
510 + * Per unit types keep every line, since their amount is meant to
511 + * scale with the units.
512 + */
513 + if ( in_array( $rule['discount_type'], array( 'fixed', 'fixed_price' ), true ) ) {
514 + $first_line = reset( $eligible_lines );
515 +
516 + if ( ! empty( $first_line['item'] ) && $discountable_units > 0 ) {
517 + $item = $first_line['item'];
518 + $item['disco_forced_qty'] = $discountable_units;
519 +
520 + $this->apply_rule_to_discount( $discounts, $campaign, $rule, $item, $cart, $total_applicable_qty );
521 + }
522 +
523 + continue;
524 + }
525 +
497 526 // Hand out the discountable units across the eligible lines in cart order.
498 527 foreach ( $eligible_lines as $eligible_line ) {
499 528 if ( $discountable_units <= 0 ) {
500 529 break;
@@ -663,13 +692,19 @@
663 692 if ( empty( $get_discounts ) ) {
664 693 continue;
665 694 }
666 695
667 - // Only stage campaigns whose discount actually applied to the cart.
668 - ( new UserLimit )->disco_start_session_on_checkout( $intent->campaign->id );
669 -
670 696 foreach ( $get_discounts as $item_id => $discount ) {
697 + /**
698 + * Keep the campaign alongside its amount at the same index.
699 + *
700 + * Staging cannot happen yet: several campaigns can offer an amount
701 + * for the same item and only one of them survives the min/max
702 + * reduction below. Crediting every campaign here would consume the
703 + * usage limit of campaigns whose discount never reached the cart.
704 + */
671 705 $discounts[ $item_id ]['discounts'][] = max( $discount['discounts'] );
706 + $discounts[ $item_id ]['campaigns'][] = (int) $intent->campaign->id;
672 707 }
673 708 }
674 709
675 710 // Check if the discounts are empty.
@@ -676,13 +711,27 @@
676 711 if ( empty( $discounts ) ) {
677 712 return false;
678 713 }
679 714
715 + $applied_campaign_ids = array();
716 +
680 717 // Get the min or max discount amount for each item.
681 718 foreach ( $discounts as $item_id => $discount ) {
682 - $discounts[ $item_id ] = $this->min_max_average( $discount['discounts'] );
719 + $winning_amount = $this->min_max_average( $discount['discounts'] );
720 + $winning_index = array_search( $winning_amount, $discount['discounts'], true );
721 +
722 + if ( false !== $winning_index && isset( $discount['campaigns'][ $winning_index ] ) ) {
723 + $applied_campaign_ids[ $discount['campaigns'][ $winning_index ] ] = true;
724 + }
725 +
726 + $discounts[ $item_id ] = $winning_amount;
683 727 }
684 728
729 + // Stage only the campaigns that actually won an item.
730 + foreach ( array_keys( $applied_campaign_ids ) as $applied_campaign_id ) {
731 + ( new UserLimit )->disco_start_session_on_checkout( $applied_campaign_id );
732 + }
733 +
685 734 return $discounts;
686 735 }
687 736
688 737 /**
@@ -761,9 +810,10 @@
761 810 if ( empty( $intents ) ) {
762 811 return false;
763 812 }
764 813
765 - $discounts = array();
814 + $discounts = array();
815 + $candidate_campaign_ids = array();
766 816
767 817 // Loop through the intents.
768 818 foreach ( $intents as $intent ) {
769 819 /**
@@ -781,10 +831,18 @@
781 831 if ( empty( $get_discounts ) ) {
782 832 continue;
783 833 }
784 834
785 - // Only stage campaigns whose discount actually applied to the cart.
786 - ( new UserLimit )->disco_start_session_on_checkout( $intent->campaign->id );
835 + /**
836 + * Collect the campaign now, stage it later.
837 + *
838 + * What this method finally grants is decided well below: the rewards
839 + * are unioned across campaigns and a BuyXGetY (products) campaign can
840 + * then override the whole result authoritatively. Crediting a campaign
841 + * here would consume its usage limit even when the final result grants
842 + * it nothing.
843 + */
844 + $candidate_campaign_ids[] = (int) $intent->campaign->id;
787 845
788 846 foreach ( $get_discounts as $item_id => $discount ) {
789 847 $discounts[ $item_id ]['discounts'][] = max( $discount['discounts'] ); // phpcs:ignore
790 848 $discounts[ $item_id ]['free'] = $discount['free']; // phpcs:ignore
@@ -871,8 +929,24 @@
871 929 $discounts['get_qty'] = empty( $reward_map ) ? 0 : max( $reward_map ); // phpcs:ignore
872 930 $discounts['free'] = ! empty( $reward_map ); // phpcs:ignore
873 931 $discounts['bogo_type'] = $bogo_type;
874 932 $discounts['free_item_selection'] = $intent->campaign->get_free_item_selection();
933 +
934 + /**
935 + * This branch is authoritative for the whole result, so the campaign
936 + * that owns it is the only one that can be credited.
937 + */
938 + $candidate_campaign_ids = array( (int) $intent->campaign->id );
939 + }
940 +
941 + /**
942 + * Stage the campaigns only once the result is final, and only when it
943 + * actually grants free items.
944 + */
945 + if ( ! empty( $discounts['free'] ) ) {
946 + foreach ( array_unique( $candidate_campaign_ids ) as $applied_campaign_id ) {
947 + ( new UserLimit )->disco_start_session_on_checkout( $applied_campaign_id );
948 + }
875 949 }
876 950
877 951 return $discounts;
878 952 }