Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Kyle Eck 130 posts 280 karma points
    Dec 13, 2021 @ 17:21
    Kyle Eck
    0

    Calculate Shipping in Cart with Custom Shipping Calculator

    Matt,

    A client site only ships via UPS Ground in the United States, and has set shipping rates for their products.

    We have gone through and created a custom shipping calculator that overrides the default one like so:

    public override Price CalculateShippingMethodPrice(ShippingMethodReadOnly shippingMethod, Guid currencyId, Guid countryId, Guid? regionId, TaxRate taxRate, ShippingCalculatorContext context)
        {
            var shippingPrice = PublishedContentExtensions.CalculateShippingCostForOrder(context.Order.SubtotalPrice);
    
            var price = new Price(shippingPrice, taxRate, currencyId);   
    
            //var shippingPrice = _shippingService.GetShippingPrice(context.Order, currencyId);
    
            if (price == null)
            {
                price = base.CalculateShippingMethodPrice(shippingMethod, currencyId, countryId, regionId, taxRate, context);
            }
    
            return price;
        }
    

    This is then Registered in a composition that fires on application start up like this:

    [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
    [ComposeAfter(typeof(VendrComposer))]
    public class MyCustomShippingComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            composition.RegisterUnique<IShippingCalculator, PreenShippingCalculator>();
        }
    }
    

    What I would like to understand is how to call

     CalculateShippingMethodPrice()
    

    In our controller of which renders our cart totals. My understanding is that the method we have written here overrides the way shipping is calculated for an order. How would we tell the order to go out and actually calculate the shipping cost in the cart?

    Ideally we would want to calculate this in our controller that renders the cart totals etc.

    Furthermore, my understanding is that overriding this method would put the value we determine into the following property on an order:

    @Model.CurrentOrder.ShippingInfo.TotalPrice
    

    I just cant put what seems to be the last piece of the puzzle together here.

    Thanks.

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Dec 14, 2021 @ 09:14
    Matt Brailsford
    0

    Hi Kyle,

    So the shipping calculator would really only be called from within the order calculation process and is calculated based upon the shipping method assigned to the order so it would require the shipping method to be assigned to the order and then the order recalculated and then yes, the orders shipping info would get updated with the relevant price.

    Hope this helps

    Matt

  • Kyle Eck 130 posts 280 karma points
    Dec 14, 2021 @ 11:13
    Kyle Eck
    0

    So if we setup a shipping method with no price on it because we determine the price, the shipping rate would be recalculated automatically?

    Right now we have it working doing just that, but I ask just to confirm it is in fact the proper way.

    Right our shipping price is one step behind, as we use Ajax to add, remove, and update a mini cart in the header and also the cart page. Just to ask, does GetOrCreateCurrentOrder always get the current order of you have a cart cookie or something set? I’m trying to determine what calls our shipping price update so many times.

    We use a unit of work provider and call save order once maybe twice through the process.

    We are also going to use Vendr Checkout, does this have any implications or special cases we must account for in their that you could think of?

    Thanks.

  • Kyle Eck 130 posts 280 karma points
    Dec 14, 2021 @ 11:19
    Kyle Eck
    0

    Thanks for your help recently Matt it’s been great.

    To elaborate further, If we wanted to calculate shipping, then tax in that order in the cart because we know the customers zip code would we need to adjust the order calculation pipeline?

    I’m a bit fuzzy on this. It looks like currently shipping and tax are only calculated during checkout, however we have some situations where we can calculate that in the cart then simply use it from the order during checkout as long as we would get the order right in the calculations timing.

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Dec 14, 2021 @ 11:33
    Matt Brailsford
    0

    Hi Kyle,

    I believe we calculate the tax as one of the first things (or at least the tax rates). I do think we need to improve this though, as whilst this works in Europe where most tax can be known beforehand I know this isn't the case in the US where it would probably make sense to calculate the unit prices and then calculate the tax and totals afterwards. The best way I've seen to work around that at the moment is to zero rate everything in Vendr, bypassing Vendr's own tax bits, and then create a custom price adjuster that applies a price adjustment that has a price only with a tax element. I'm not quite sure if this is what you are saying you need though or not?

    RE GetOrCreateCurrentOrder, yes, this will get the current order if the Vendr cookie exists. And I believe we also force calculation every time a save occurs, just incase there are any changes that haven't triggered a recalc yet (we don't re-calc straight away, rather we set a flag to say recalc is needed and if any property is accessed that depends on figures, this triggers the recalc, especially useful if you have a bunch of changes to apply).

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Dec 14, 2021 @ 11:43
    Matt Brailsford
    0

    PS If you are using Vendr Checkout, that is configured to explicitly clear the billing / shipping countries at the various checkout steps until you hit the relevant sections of the checkout flow as that ensures that the cart never shows an incorrect value (ie, you don't want someone to say they are shipping to Denmark, then go back and update the cart with something that can't be shipped to DK and so the cart total is then wrong).

    If you don't want this functionality, you'll have to un-register the event handlers Vendr Checkout adds here https://github.com/vendrhub/vendr-checkout/blob/v2/dev/src/Vendr.Checkout/Extensions/CompositionExtensions.cs#L23-L39 , you can do this in your own builder extension / composer that runs after Vendr's calling RemoveHandler to remove the respective handlers.

  • Kyle Eck 130 posts 280 karma points
    Dec 14, 2021 @ 12:08
    Kyle Eck
    0

    Thanks Matt,

    Re: Shipping and Tax

    It’s best if we create zero based configurations for everything then use a price adjustment? Let’s say we are using an automated tax calculation, we have an api we call to get a tax rate.

    We should make a price adjustment to calculate shipping and then calculate tax and then that price adjustment will put those values into the order?

    Sorry but confused about the proper way, a price adjustment vs overriding the Calculation using a composer.

    To be simple, the easiest way to calculate shipping, then tax, then save to an order is ultimately what we’re after. I know the US is a pain sometimes.

    RE Price Adjustment

    The price adjustment fires every time an order is created/a cart is created? As far as saving an order, if we don’t save the order in a unit of work provider can we hold the current values in that order between controller methods? Saving the order at the very last method call we have?

    Hope that makes sense? My understanding was that we needed to call save order at the end of every uow provider otherwise we would lose the order state essentially.

    Thanks!

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Dec 14, 2021 @ 13:40
    Matt Brailsford
    0

    Hi Kyle,

    So the default workflow when calculating an order is that Vendr calculates all the tax rates for the order + order lines + shipping etc and stores those, then goes through the order calculating all the pre/post tax prices using the tax rates it previously calculated.

    If you can live with this flow, then you should just have to set the shipping country + shipping method and as part of this calculation process Vendr will use the tax rate defined on the shipping method to work out the relevant shipping price.

    If you need to override the shipping price, then you would use the shipping calculator for this (however, note that the method for calculating the tax rate will still execute as one of the first tasks and so you won't have the relevant order lines prices etc if you need to work out shipping tax rates from items in the cart).

    If you need to calculate tax based on the items in the order + the shipping location, then these would currently need to be done as a price adjustment. This will likely end up being a single TAX price adjustment that applies to the order total price (there wouldn't be any break down). Ordinarily these price adjustments would be calculated on every recalc of the order, but obviously if you are accessing a third party service you want to limit the amount of times you call this. Way's I've suggested in the past is to have people store an order property with some kind of hash to flag that there is no need to recalc and just use the previously calculated value.

    I'm aware this is all far from ideal though and I will take a look in the new year at whether there is a better workflow for all this, such that the tax rates can be calculated AFTER the unit / pre tax prices have all been calculated and thus allows for smarter calculations.

    RE unit of work, yes, you must persist your order before the end of the uow in order for those changes to be persisted, otherwise they will be rolled back.

    Another important thing to remember (I'm not sure if you are doing this, but it's worth me mentioning) is that a read only order is a snapshot of an order at a given point in time, so when you call AsWritable it converts that snapshot into a writable object. If you run multiple units of work, and in them call AsWritable you need to note that in the second unit of work, you'll have converted the original snapshot to writable and so if you persist this object, you'll likely reset whatever changes you made in the first uow. If you need to use multiple uow's, you should ALWAYS re-fetch the read only order before you make it writable for a second time, in order to retrieve an up to date snapshot.

    Hope this all makes sense.

    Matt

  • Kyle Eck 130 posts 280 karma points
    Dec 14, 2021 @ 15:55
    Kyle Eck
    0

    Matt see the question from earlier below:

    The best way I've seen to work around that at the moment is to zero rate everything in Vendr, bypassing Vendr's own tax bits, and then create a custom price adjuster that applies a price adjustment that has a price only with a tax element. I'm not quite sure if this is what you are saying you need though or not?

    Regarding this statement. Ultimately what we need to do is this:

    1. Calculate the subtotal such that after we increment or decrement the quantity we are working with the updated line item totals.

    2. From the updated subtotal calculate the shipping price for the order

    3. Then calculate the tax for the entire order

    So far we have our shipping being calculated in a custom ShippingCalculator like this:

            public override Price CalculateShippingMethodPrice(ShippingMethodReadOnly shippingMethod, Guid currencyId, Guid countryId, Guid? regionId, TaxRate taxRate, ShippingCalculatorContext context)
        {
            var shippingPrice = new Decimal();
    
            var allLineItemsPrice = new Decimal();
    
            foreach(var lineItem in context.OrderCalculation.OrderLines)
            {
                allLineItemsPrice += lineItem.Value.TotalPrice.Value.WithoutTax;
            }
    
            shippingPrice = PublishedContentExtensions.CalculateShippingCostForOrder(allLineItemsPrice);
    
    
            var price = new Price(shippingPrice, taxRate, currencyId);   
    
            //var shippingPrice = _shippingService.GetShippingPrice(context.Order, currencyId);
    
            if (price == null)
            {
                price = base.CalculateShippingMethodPrice(shippingMethod, currencyId, countryId, regionId, taxRate, context);
            }
    
            return price;
        }
    

    Where you can see we loop over the OrderCalulation line items to get the current subtotal indefinitely. I feel like there is a "Vendr" way of doing this.

    How should we properly calculate the shipping like this and then from their calculate tax?

    Is it adjusting the pipeline order? If so How exactly do we do that per se?

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Dec 14, 2021 @ 16:48
    Matt Brailsford
    100

    Hi Kyle,

    So yea, I’d say your calculator is correct and what is responsible for calculating the shipping.

    I’d then say to create a price adjuster for the order total price which is then responsible for calculating the tax. This should return a price with just a tax value to it.

    Implementing both of these should already execute in the correct order.

    Hope this helps.

  • Immanuel 1 post 71 karma points
    Dec 15, 2021 @ 04:07
    Immanuel
    0

    Calculate their shipping fees before going to the checkout page. First, they are required to fill out the country, state, city, and postcode form fields. After that, the customer needs to click on “Update Totals” to calculate the shipping.

Please Sign in or register to post replies

Write your reply to:

Draft