Is it possible to offer free shipping only when a product discount or order discount created in the application is applied?
I would like to add a free shipping option to my product discounts and order discounts.
I tried this problem by rewriting shopify.extension.toml as follows but it did not solve the problem.
You can currently only have one target for discount functions. Your use case is not technically feasible with functions. One alternative is to automatically add a manual shipping code at checkout with an extension (our application Stackable now does this!).
Thanks for sharing my article; however, it is now outdated. Now that Shopify has released a new Discount Functions API, which allows multiple discount classes on the same Function instance, it should be possible to combine product/shipping discounts into the same code.
@tobebuilds I just encountered an issue with the new Discount Function API. It was working fine last week, but today when I tried to create a new one, it returned a product-discount instead.
When I checked the functions, I noticed that it only includes PRODUCT classes. I believe this issue started last Saturday or Sunday, because everything was still working correctly on Friday’s morning.
Shopify’s Discount Function API processes discounts in a specific order during cart evaluation:
Cart Lines Discounts (cart_lines_discounts_generate_run): This function applies discounts to individual cart lines (products) or the order subtotal. It outputs discount operations that Shopify applies to the cart.
Delivery Discounts (cart_delivery_options_discounts_generate_run): This function applies discounts to shipping options. It runs after cart line discounts are calculated and can access the cart’s state, including any applied discounts via the discountAllocations field.
The input to cart_delivery_options_discounts_generate_run includes the cart’s current state, which reflects discounts applied by cart_lines_discounts_generate_run. Specifically, the discountAllocations field in the cart or cart lines will contain details about discounts applied to products or the order subtotal.
using this you can decide on providing free shipping
using before query for cart_delivery_options_discounts_generate_run will provide data
query RunInput {
cart {
lines {
id
discountAllocations {
discountedAmount {
amount
}
}
}
cost {
subtotalAmount {
amount
}
discountAllocations {
discountedAmount {
amount
}
}
}
deliveryGroups {
id
deliveryOptions {
handle
cost {
amount
}
}
}
}
}
I am still searching for the answer to this question.
I want to apply free shipping discounts only when discounts are applied to products or orders. Is this possible?
@Ankit_Shrivastava
In Rust, it is not possible to implement the shipping discount logic depending on the return type of cart.lines.discounts.generate.run, but is it possible in JavaScript?
I cannot add DeliveryOperation::DeliveryDiscountsAdd to the CartLinesDiscountsGenerateRunResult type.
cart_lines_discounts_generate_run.rs
use crate::schema::CartLineTarget;
use crate::schema::CartLinesDiscountsGenerateRunResult;
use crate::schema::CartOperation;
use crate::schema::DiscountClass;
use crate::schema::OrderDiscountCandidate;
use crate::schema::OrderDiscountCandidateTarget;
use crate::schema::OrderDiscountCandidateValue;
use crate::schema::OrderDiscountSelectionStrategy;
use crate::schema::OrderDiscountsAddOperation;
use crate::schema::OrderSubtotalTarget;
use crate::schema::Percentage;
use crate::schema::ProductDiscountCandidate;
use crate::schema::ProductDiscountCandidateTarget;
use crate::schema::ProductDiscountCandidateValue;
use crate::schema::ProductDiscountSelectionStrategy;
use crate::schema::ProductDiscountsAddOperation;
use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;
#[shopify_function]
fn cart_lines_discounts_generate_run(
input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<CartLinesDiscountsGenerateRunResult> {
let max_cart_line = input
.cart()
.lines()
.iter()
.max_by(|a, b| {
a.cost()
.subtotal_amount()
.amount()
.partial_cmp(b.cost().subtotal_amount().amount())
.unwrap_or(std::cmp::Ordering::Equal)
})
.ok_or("No cart lines found")?;
let has_order_discount_class = input
.discount()
.discount_classes()
.contains(&DiscountClass::Order);
let has_product_discount_class = input
.discount()
.discount_classes()
.contains(&DiscountClass::Product);
if !has_order_discount_class && !has_product_discount_class {
return Ok(CartLinesDiscountsGenerateRunResult { operations: vec![] });
}
let mut operations = vec![];
// Check if the discount has the ORDER class
if has_order_discount_class {
operations.push(CartOperation::OrderDiscountsAdd(
OrderDiscountsAddOperation {
selection_strategy: OrderDiscountSelectionStrategy::First,
candidates: vec![OrderDiscountCandidate {
targets: vec![OrderDiscountCandidateTarget::OrderSubtotal(
OrderSubtotalTarget {
excluded_cart_line_ids: vec![],
},
)],
message: Some("10% OFF ORDER".to_string()),
value: OrderDiscountCandidateValue::Percentage(Percentage {
value: Decimal(10.0),
}),
conditions: None,
associated_discount_code: None,
}],
},
));
}
// Check if the discount has the PRODUCT class
if has_product_discount_class {
operations.push(CartOperation::ProductDiscountsAdd(
ProductDiscountsAddOperation {
selection_strategy: ProductDiscountSelectionStrategy::First,
candidates: vec![ProductDiscountCandidate {
targets: vec![ProductDiscountCandidateTarget::CartLine(CartLineTarget {
id: max_cart_line.id().clone(),
quantity: None,
})],
message: Some("20% OFF PRODUCT".to_string()),
value: ProductDiscountCandidateValue::Percentage(Percentage {
value: Decimal(20.0),
}),
associated_discount_code: None,
}],
},
));
}
Ok(CartLinesDiscountsGenerateRunResult { operations })
}