I have created the following functions to override the product price and make a max discount for WooCommerce. However, I can't seem to figure out why the woocommerce_coupon_get_discount_amount filter is applying to all coupons.
The way I am trying to get it to function is if the coupon is a percent coupon, it will cap the total discount amount for that coupon. If the coupon is a fixed_cart type and it has the custom field I added _price_per_product_amt filled in, then it will change the price of the product.
Both of these functions work if I have only one coupon in the cart. If I have the fixed_cart_coupon in the cart, it will change the price and set the coupon amount to $0 (the coupon is set to have $0 for its amount). However, if I add a percent coupon as well, the function works for the percent coupon, but then it also adds a discount amount to the fixed_cart_coupon. It should only be modifying the discount amount for percent coupons in the cart.
// filter to change discount if over max coupon amount
function filter_woocommerce_coupon_get_discount_amount( $discount, $discounting_amount, $cart_item, $single, $instance ) {
$cartCoupons = WC()->cart->get_applied_coupons();
foreach ($cartCoupons as $key => $appliedCoupon) {
$coupon = new WC_Coupon($appliedCoupon);
$couponType = get_post_meta( $coupon->get_id(), 'discount_type', true );
if ($couponType == 'percent') {
$maxCouponAmount = get_post_meta( $coupon->get_id(), '_max_discount', true );
$excludedProducts = explode(",", get_post_meta( $coupon->get_id(), 'exclude_product_ids', true ));
$cartLines = count(WC()->cart->get_cart());
$cartLineItems = WC()->cart->get_cart();
foreach ($cartLineItems as $cartItem){
$cartProductID[] = $cartItem['product_id'];
if (!empty($excludedProducts)) {
$cartLinesWithoutExcluded = array_intersect($cartProductID,$excludedProducts);
} else {
$cartLinesWithoutExcluded = $cartProductID;
}
$cartLinesWithoutExcluded = count($cartLinesWithoutExcluded);
$totalCartItems = $cartLines - $cartLinesWithoutExcluded;
$discount = $maxCouponAmount / $totalCartItems;
}
} else {
$discount = 0.00;
}
return $discount;
}
}
// apply the coupon whether it is max discount or a product price adjustment
function apply_max_amount_or_product_price_adjustment(){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
if ( !is_admin() && !wp_is_json_request() ) {
global $wp, $woocommerce;
$cartCoupons = WC()->cart->get_applied_coupons();
foreach ($cartCoupons as $key => $appliedCoupon) {
$coupon = new WC_Coupon($appliedCoupon);
$maxCouponAmount = get_post_meta( $coupon->get_id(), '_max_discount', true );
$excludedProducts = explode(",", get_post_meta( $coupon->get_id(), 'exclude_product_ids', true ));
$couponType = get_post_meta( $coupon->get_id(), 'discount_type', true );
// $fixedProductPrice = get_post_meta( $coupon->get_id(), '_adjust_price', true );
$couponAmount = WC()->cart->get_coupon_discount_amount( $appliedCoupon );
if (!empty($maxCouponAmount) && $couponType == 'percent' && ($couponAmount > $maxCouponAmount || $couponAmount == $maxCouponAmount)) {
add_filter( 'woocommerce_coupon_get_discount_amount', 'filter_woocommerce_coupon_get_discount_amount', 10, 5 );
}
if ($couponType == 'fixed_cart'){
$cart = WC()->cart->get_cart();
$couponProducts = explode(',',get_post_meta( $coupon->get_id(), 'product_ids', true ));
$fixedPricePerProduct = get_post_meta( $coupon->get_id(), '_price_per_product_amt', true );
foreach( $cart as $cart_item ) {
if (in_array($cart_item['data']->get_parent_id(), $couponProducts)) {
$cart_item['data']->set_price( $fixedPricePerProduct );
}
}
}
}
}
}
add_action('woocommerce_before_calculate_totals', 'apply_max_amount_or_product_price_adjustment', 10, 1);

That's because when you add your filter 'filter_woocommerce_coupon_get_discount_amount' from the function 'apply_max_amount_or_product_price_adjustment', it is added to all coupons. This filter is fired later so your condition to apply it or not will not work (since it had been added). You should remove the part adding the filter and set your filter outside this function.
When you add any filter, even inside a condition, since it is added, it is added for every time until it is removed by remove_filter( ... ). In your case, you can't remove_filter in 'apply_max_amount_or_product_price_adjustment' because it is to early and will remove it for all coupons event "percent" type.
That's because you are looping all the coupons in your filter 'filter_woocommerce_coupon_get_discount_amount' and always returning value from the first coupon. This filter fires every time there is $coupon->get_discount_amount(), so it will fire on each coupon applied on your cart in checkout. You should remove the loop and check only the current coupon. For better understanding you should rename argument '$instance' to '$coupon' which is the current WC_Coupon object.
You can have a look to the WC_Coupon class and search filter 'woocommerce_coupon_get_discount_amount' ( ctrl + f to find it in the page ) here : https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/includes/class-wc-coupon.php
Also, be carefull in your loop :
because it will always return $discount for the last cart item only.
Try this code (not tested) :
Please test and edit code if necessary. Sorry if my explainations seems messy, I'm writing at middle of the night.