Copied to clipboard

Flag this post as spam?

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


  • Jakob Pless 20 posts 60 karma points
    Mar 20, 2015 @ 12:34
    Jakob Pless
    0

    Inventory tracking

    When does the inventory get updates? And do I need to manage it in the checkout flow?

    Running Umbraco 7.2.2, Merchello 1.8.0. Shop is based of off Kitten Sample

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Mar 20, 2015 @ 21:10
    Rusty Swayne
    0

    At the moment item counts are decremented when they are added to a shipment. We have had one comment that it would be nice to make this configurable so that you have the option of setting this to when an order is created (so after an invoice has been paid).

    Do you have thoughts about that?

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Oct 26, 2016 @ 08:18
    Simon Dingley
    0

    Hi Rusty,

    Just to offer my 2p (or 2ยข) - I think this is definitely something that should be configurable. In my (limited) experience of small businesses entering into ecommerce they do not hold large volumes of stock and so decrementing stock at the point of fulfilment is too risky because they are potentially continuing to sell stock they no longer have as it is committed to unfulfilled orders.

    Cheers, Simon

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Oct 27, 2016 @ 15:25
    Rusty Swayne
    1

    Hey Simon,

    I think you saw the feature request in the issue tracker and will add that in V3.

  • Jakob Pless 20 posts 60 karma points
    Mar 21, 2015 @ 08:07
    Jakob Pless
    0

    It would be very nice to be able to configure it, we need it to either decrement when the order is created or at least reserved somehow so the same item doesn't get bought twice. The current shop I'm working on has a small storage so when ever the inventory is 0 it means we have to backorder items.

    I have looked into chains, would it be possible to create a new chain that decrements on order creation?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Mar 21, 2015 @ 15:48
    Rusty Swayne
    101

    You add a task to the order chain to do it there but then remove the task from the shipment chain.

  • Jakob Pless 20 posts 60 karma points
    Mar 23, 2015 @ 11:30
    Jakob Pless
    0

    Is it possible to get some help with the code? :)

  • Jakob Pless 20 posts 60 karma points
    Apr 10, 2015 @ 07:15
    Jakob Pless
    0

    I can't get the custom chain to take effect, can anybody help with a code example of how to propper implement it?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Apr 10, 2015 @ 19:17
    Rusty Swayne
    0

    Jakob,

    You are trying to add a task to a chain, not create your own chain right? Just want to know what I should post as an example =)

  • Jakob Pless 20 posts 60 karma points
    Apr 10, 2015 @ 20:50
    Jakob Pless
    0

    Yes as you suggested adding a task to the order chain and removing from shipment chain

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Apr 10, 2015 @ 23:45
    Rusty Swayne
    0

    Great.

    In the merchello.config file you should find a list of taskChains.

    First you would want to remove the task

                   <task type="Merchello.Core.Chains.ShipmentCreation.RemoveShipmentOrderItemsFromInventoryAndPersistShipmentTask, Merchello.Core" />
    

    Should look like this after

      <taskChain alias="OrderPreparationShipmentCreate">
          <tasks>
              <task type="Merchello.Core.Chains.ShipmentCreation.AddShippableOrderLineItemsToShipmentTask, Merchello.Core" />
              <task type="Merchello.Core.Chains.ShipmentCreation.SetOrderStatusTask, Merchello.Core" />
          </tasks>
      </taskChain>
    

    Then add a new task to the OrderPreparationOrderCreate chain. It has to go after the ConvertInvoiceItemsToOrderItemsTask so that the line items will be available.

     <taskChain alias="OrderPreparationOrderCreate">
        <tasks>
            <task type="Merchello.Core.Chains.OrderCreation.ConvertInvoiceItemsToOrderItemsTask, Merchello.Core" /> 
            <task type="MyInventoryTask, MyLibrary" />   
        </tasks>
    </taskChain>
    

    The MyInventoryTask need to sub-class the OrderCreationAttemptChainTaskBase class.

    /// <summary>
    /// The convert invoice items to order items task.
    /// </summary>
    public class MyInventoryTask : OrderCreationAttemptChainTaskBase
    {
    
        public MyInventoryTask(IInvoice invoice)
            : base(invoice)
        {            
        }
    
    
        public override Attempt<IOrder> PerformTask(IOrder value)
        {
              // THIS IS A COPY PASTE FROM THE REMOVED TASK (UNTESTED)
    
             var trackableItems = Order.InventoryTrackedItems().Where(x => KeysToShip.Contains(x.Key)).ToArray();
    
            var variants = _productVariantService.GetByKeys(trackableItems.Select(x => x.ExtendedData.GetProductVariantKey())).ToArray();
    
            if (variants.Any())
            {
                foreach (var item in trackableItems)
                {
                    var variant = variants.FirstOrDefault(x => x.Key == item.ExtendedData.GetProductVariantKey());
                    if (variant == null) return Attempt<IShipment>.Fail(new NullReferenceException("A ProductVariant reference in the order could not be found"));
    
                    var inventory = variant.CatalogInventories.FirstOrDefault(x => x.CatalogKey == item.ExtendedData.GetWarehouseCatalogKey());
                    if (inventory == null) return Attempt<IShipment>.Fail(new NullReferenceException("An inventory record could not be found for an order line item"));
    
                    inventory.Count -= item.Quantity;
                }
    
                if (trackableItems.Any()) _productVariantService.Save(variants);
            }
    
            return Attempt<IOrder>.Succeed(value);
        }
    }
    

    Let me know how it goes.

  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 15:59
    Tyler Brown
    0

    This is exactly what I would like to do, however I am unable to get this to work. Here is what I have:

     public class RemoveOrderItemsFromInventory : OrderCreationAttemptChainTaskBase
    {
    
        public RemoveOrderItemsFromInventory(IInvoice invoice)
            : base(invoice)
        {
        }
    
    
        /// <summary>
        /// The shipment service.
        /// </summary>
        private readonly IShipmentService _shipmentService;
    
        /// <summary>
        /// The order service.
        /// </summary>
        private readonly IOrderService _orderService;
    
        /// <summary>
        /// The product variant service.
        /// </summary>
        private readonly IProductVariantService _productVariantService;
    
    
    
        public override Attempt<IOrder> PerformTask(IOrder value)
        {
    
    
            var trackableItems = value.InventoryTrackedItems().ToArray();
    
            var variants = _productVariantService.GetByKeys(trackableItems.Select(x => x.ExtendedData.GetProductVariantKey())).ToArray();
    
            if (variants.Any())
            {
                foreach (var item in trackableItems)
                {
                    var variant = variants.FirstOrDefault(x => x.Key == item.ExtendedData.GetProductVariantKey());
                    if (variant == null) return Attempt<IOrder>.Fail(new NullReferenceException("A ProductVariant reference in the order could not be found"));
    
                    var inventory = variant.CatalogInventories.FirstOrDefault(x => x.CatalogKey == item.ExtendedData.GetWarehouseCatalogKey());
                    if (inventory == null) return Attempt<IOrder>.Fail(new NullReferenceException("An inventory record could not be found for an order line item"));
    
                    inventory.Count -= item.Quantity;
                }
    
                if (trackableItems.Any()) _productVariantService.Save(variants);
            }
    
            return Attempt<IOrder>.Succeed(value);
        }
    }
    

    The main thing I changed is that trackableItems is using the parameter 'value' since it is the IOrder. I then took out the Where clause since essentially all trackable items need to be tracked, not just Shippable items. Anyway, this is not working for me so am I not understanding?

    Thanks!

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 29, 2015 @ 16:07
    Rusty Swayne
    0

    @Tyler - does the code execute all the way through or is it failing. Also, are the inventory values changing in the database, just not showing in the UI?

  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 17:52
    Tyler Brown
    0

    The sale goes through. I don't think it's actually hitting my code. The inventory is not changing in the DB or the UI. This is how I'm registering the task, where myTasks is my namespace for for my class:

    <taskChain alias="OrderPreparationOrderCreate">
        <tasks>
            <task type="Merchello.Core.Chains.OrderCreation.ConvertInvoiceItemsToOrderItemsTask, Merchello.Core" />
            <task type="myTasks.RemoveOrderItemsFromInventory, myTasks" />
        </tasks>
    </taskChain>
    
  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 29, 2015 @ 17:55
    Rusty Swayne
    0

    Is the payment for the sale captured or just authorized?

    An order will not be created (by default) if the payment is just authorized.

    You can override this by changing the setting AlwaysApproveOrderCreation to "true" in the merchello.config

         <!-- 
    Overrides the Payment Method's IPaymentResponse ApproveOrderCreation indicating an order should always be created no matter
    if the payment has been collected or not. 
    -->
    <setting alias="AlwaysApproveOrderCreation" value="true" />
    
  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 17:58
    Tyler Brown
    0

    One thing to note is that I am dealing with non-physical products. Essentially, users are buying access to the site (access to courses and tests) and so my checkout controller is calling AuthorizeCapturePayment instead of AuthorizePayment so that the sale is made and the funds are captured at the same time (so users get instant access to courses and tests). Would cause an issue?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 29, 2015 @ 18:02
    Rusty Swayne
    0

    Can you look in the merchOrder table and see if orders are being created for your use case? They should have related items in the merchOrderItem table ...

  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 18:11
    Tyler Brown
    0

    There are no orders in the merchOrder table.

    I wrote my last post prior to seeing your mention of AlwaysApproveOrderCreation in the merchello.config. That is good to know!

    By not calling AuthorizePayment, but going straight to AuthorizeCapturePayment, am I screwing things up?

    I should have some orders though and I have none...

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 29, 2015 @ 18:17
    Rusty Swayne
    0

    No, it does not sound like anything is screwed up. The payment result from an AuthorizeCapturePayment should have ApproveOrderCreation = true if the payment was successful.

    What payment provider are you using?

  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 18:22
    Tyler Brown
    0

    I'm using the Stripe Payment Provider from Alex Lindgren

  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 18:24
    Tyler Brown
    0

    Merchello version 1.9.0 Umbraco 7.2

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 29, 2015 @ 18:32
    Rusty Swayne
    0

    What is the value of ApproveOrderCreation in the result returned from the stripe provider?

  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 18:48
    Tyler Brown
    0

    It is coming in as true

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 29, 2015 @ 19:08
    Rusty Swayne
    0

    I wonder if the Merchello dependencies on the stripe provider need to be updated. Perhaps what is going on is the event that creates the order is not being triggered ....

  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 19:46
    Tyler Brown
    0

    I know that the stripe provider is only confirmed to be compatible with Merchello 1.4.1 but I decided to try it since I need this version of Merchello and everything 'seemed' to be working...

    the provider is using Merchello 1.9.0. When does the event that creates the order fire?

  • Tyler Brown 62 posts 209 karma points
    Jul 29, 2015 @ 20:25
    Tyler Brown
    0

    I've made some progress... when debugging, it seemed that the ApproveOrderCreation was coming back as true, however, I hardcoded ApproveOrderCreation to 'true' by changing this line in my Stripe provider from this:

    return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, invoice.ShippingLineItems().Any());
    

    to this:

    return new PaymentResult(Attempt

    which has now allowed the creation of an order in the DB, but when my new task is enabled, I get an error:

    Value name cannot be null. Parameter name: task

    So apart from fixing my earlier issue with ApproveOrderCreation, any ideas on this error? Am I registering it correctly in the merchello.config?

    Thanks for the quick responses!

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 29, 2015 @ 23:10
    Rusty Swayne
    0

    I don't see anything wrong with the way you've registered the task. Are you able to step through your task, or are you saying it errors before ever hitting it?

  • Tyler Brown 62 posts 209 karma points
    Jul 30, 2015 @ 13:47
    Tyler Brown
    0

    It isn't getting to my task. It is getting as far as:

    Merchello.Core\Chains\AttemptChainTaskHandler.cs: line 16

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 30, 2015 @ 13:59
    Rusty Swayne
    0

    Ok - it is failing to construct your type.

    Some things to confirm:

    • The RemoveOrderItemsFromInventory class is in the namespace myTasks
    • There is a myTasks.dll in your bin directory
  • Tyler Brown 62 posts 209 karma points
    Jul 30, 2015 @ 15:06
    Tyler Brown
    0

    Ok! It's finally getting to my code. My dll was absent ยฏ_(ใƒ„)_/ยฏ

    Now I get an object reference error. The following variable ends up being an empty array:

    var trackableItems = value.InventoryTrackedItems().ToArray();
    

    I have confirmed that I am tracking this particular product and that the parameter value itself is not null.

    The difference between this code and RemoveShipmentOrderItemsFromInventoryAndPersistShipmentTask is that I'm using the passed IOrder 'value' to grab inventory items. any ideas?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 30, 2015 @ 15:15
    Rusty Swayne
    0

    You could get a collection of all variants in the order based off the product variant keys the same way as your doing after you filter and then filter those off of tracks inventory.

    Even with a large order the query will be just about the same as if you did it after the filter.

    This would be a good validation step anyway in off chance that a back office user changes a variant from tracks inventory = true or false after the product has been added to the basket but before it reaches this point in the flow.

    I actually have a to do that introduces this sort of validation throughout the checkout process.

  • Tyler Brown 62 posts 209 karma points
    Jul 30, 2015 @ 18:07
    Tyler Brown
    0

    Thanks for the help! How would I go about grabbing all variants? I see that I can get all items using value.Items (instead of value.InventoryTrackedItems) and that array is populated but I'm not sure how to invoke product variant service from inside OrderCreationAttemptChain. I get an object ref error on the line:

    var variants = _productVariantService.GetByKeys(Items.Select(x => x.ExtendedData.GetProductVariantKey())).ToArray();
    

    I'm getting a warning that _productVariantService is never assigned to and will always have its default value as null.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 30, 2015 @ 19:03
    Rusty Swayne
    0

    You can either do that in your constructor or get rid of your readonly field and do

      var productVariantService = MerchelloContext.Current.Services.ProductVariantService;
    
  • Tyler Brown 62 posts 209 karma points
    Jul 31, 2015 @ 19:05
    Tyler Brown
    0

    I've narrowed down at least what is going wrong, although I don't know why:

    var inventory is null at this line:

    var inventory = variant.CatalogInventories.FirstOrDefault(x => x.CatalogKey == item.ExtendedData.GetWarehouseCatalogKey());
    

    because item.ExtendedData.GetWarehouseCatalogKey comes back as an empty guid. If you dig into the database and look at the ExtendedData xml, the 'merchWarehouseCatalogKey' element does not exist for this item. When I created the product, I just checked the box for 'track inventory for this variant'. I also checked the box 'make this variant shippable'. Am I missing another step? Is there another reason why merchWarehouseCatalogKey would not show up for an item in and Order?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 03, 2015 @ 16:46
    Rusty Swayne
    0

    That gets looked up when the basket is packaged. How are you quoting your shipment?

  • Tyler Brown 62 posts 209 karma points
    Aug 03, 2015 @ 17:48
    Tyler Brown
    0

    I'm not doing anything with shipment.

    I began this project by using the Merchello basket tutorial that just uses the cash payment method, as a proof of concept. I've added the Stripe provider.

    Now, because the site I want to build is selling access to online exams for certification, there is no physical product. Members are paying for access to be able to take exams. I'm updating custom tables to enable access to exams for Umbraco members based on whether or not capturing payment is successful. I am capturing payment automatically when a user purchases a product. So far, that is working as expected.

    Long story short, I am trying to use the quantity property to limit how many times a product (in this case, site access) can be purchased. And that is where I am stuck. I can't seem to get this quantity property to decrement.

    So it sounds like I'm missing a step with shipment? As stated earlier, invoice.ShippingLineItems().Any() is coming back as false so to run my task, I have hardcoded it as true. But it sounds like this could be the problem. How do I reconcile shipment for a non-physical product?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 03, 2015 @ 18:59
    Rusty Swayne
    0

    The WarehouseCatalogKey is there so that the association can be made between the item, the inventory and shipping rules - so you can different shipping rules, for different countries for the same item ...

    In your case, you can just add the catalog key when you add your item to the basket. The ExtendedDataKey is

    Constants.ExtendedDataKeys.WarehouseCatalogKey 
    

    and the value will be the Guid associated with your inventory record.

    That should give you the tie you need.

  • Tyler Brown 62 posts 209 karma points
    Aug 03, 2015 @ 19:58
    Tyler Brown
    1

    ah ha! well alright- I get it now! That worked and everything is working as expected. For anyone curious to see the final result (It's not much different than the original) here is the task:

    public class RemoveOrderItemsFromInventory : OrderCreationAttemptChainTaskBase
    {
    
        public RemoveOrderItemsFromInventory(IInvoice invoice)
            : base(invoice)
        {
        }
    
        public override Attempt<IOrder> PerformTask(IOrder value)
        {
    
            var productVariantService = MerchelloContext.Current.Services.ProductVariantService;
            var trackableItems = value.InventoryTrackedItems().ToArray();
            var variants = productVariantService.GetByKeys(trackableItems.Select(x => x.ExtendedData.GetProductVariantKey())).ToArray();
    
            if (variants.Any())
            {
                foreach (var item in trackableItems)
                {
                    var variant = variants.FirstOrDefault(x => x.Key == item.ExtendedData.GetProductVariantKey());
                    if (variant == null) return Attempt<IOrder>.Fail(new NullReferenceException("A ProductVariant reference in the order could not be found"));
    
                    var inventory = variant.CatalogInventories.FirstOrDefault(x => x.CatalogKey == item.ExtendedData.GetWarehouseCatalogKey());
                    if (inventory == null) return Attempt<IOrder>.Fail(new NullReferenceException("An inventory record could not be found for an order line item"));
    
                    inventory.Count -= item.Quantity;
                }
    
                if (trackableItems.Any()) productVariantService.Save(variants);
            }
    
            return Attempt<IOrder>.Succeed(value);
        }
    }
    

    Thanks for all the help, Rusty!

  • Gordon Saxby 1444 posts 1855 karma points
    Aug 25, 2015 @ 16:46
    Gordon Saxby
    0

    I am trying to do a similar thing - well kind of similar, I want to update stock after payment has been made (SagePay plugin) rather than when the order is fulfilled.

    I have code in the PaymentGatewayMethodBase.CaptureAttempted event similar to the above. It runs OK but I don't have a return statement so the changes do not get saved ... how do I save / persist the changes?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 25, 2015 @ 16:57
    Rusty Swayne
    0

    @Gordon - The inventory is tracked with the variants, so you really don't need to save the order or return anything - just save the variants as is done above ... or are your trying to write your own taskChain?

  • Gordon Saxby 1444 posts 1855 karma points
    Aug 25, 2015 @ 17:53
    Gordon Saxby
    0

    Below is the code I have in the event handler (minus some email stuff):

        private void Payment_CaptureAttempted(PaymentGatewayMethodBase sender, PaymentAttemptEventArgs<IPaymentResult> e)
        {
            try
            {
                if (!e.Entity.Payment.Success) return;
    
                Payment p = (Payment) e.Entity.Payment.Result;
    
                // Only do emails if this is a Credit Card (Sage) sale
                if (p.PaymentMethodType != PaymentMethodType.CreditCard)
                {
                    return;
                }
    
    
                IOrder value = e.Entity.Invoice.PrepareOrder();
                var productVariantService = MerchelloContext.Current.Services.ProductVariantService;
                var trackableItems = value.InventoryTrackedItems().ToArray();
                var variants = productVariantService.GetByKeys(trackableItems.Select(x => x.ExtendedData.GetProductVariantKey())).ToArray();
    
                foreach (var item in e.Entity.Invoice.Items)
                {
                    var variant = variants.FirstOrDefault(x => x.Key == item.ExtendedData.GetProductVariantKey());
                    //if (variant == null) return Attempt<IOrder>.Fail(new NullReferenceException("A ProductVariant reference in the order could not be found"));
    
                    if (variant != null)
                    {
                        var inventory = variant.CatalogInventories.FirstOrDefault(x => x.CatalogKey == item.ExtendedData.GetWarehouseCatalogKey());
                        //if (inventory == null) return Attempt<IOrder>.Fail(new NullReferenceException("An inventory record could not be found for an order line item"));
    
                        if (inventory != null) inventory.Count -= item.Quantity;
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error<AppStartupEventHandler>("Error triggering order confirmation notification.", ex);
            }
        }
    

    I stepped through it and it did decrement the ordered item ... but the change wasn't shown when I viewed the product in the Admin.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 25, 2015 @ 17:57
    Rusty Swayne
    1

    After you foreach you need to save the variants ...

     productVariantService.save(variants);
    
  • Gordon Saxby 1444 posts 1855 karma points
    Aug 25, 2015 @ 18:04
    Gordon Saxby
    1

    Thanks Rusty, that sorted it!

  • Gordon Saxby 1444 posts 1855 karma points
    Aug 25, 2015 @ 18:16
    Gordon Saxby
    0

    Ah, spoke too soon ... even though I am setting the quantity to 1 on a product, repeated orders are making the quantity go down one each time.

    I have just done an order and the product qty was set to 1. It now shows -2 in the Admin!?

    By the way, I have commented out the following

    in merchello.config,

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 25, 2015 @ 18:24
    Rusty Swayne
    0

    what is the outofstockpurchase value on the variant?

  • Gordon Saxby 1444 posts 1855 karma points
    Aug 25, 2015 @ 18:26
    Gordon Saxby
    0

    Urm, do you mean the "Low Count" value? If so, 0 (zero).

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 25, 2015 @ 18:28
    Rusty Swayne
    0

    no, there is a bool setting OutOfStockPurchase

  • Gordon Saxby 1444 posts 1855 karma points
    Aug 25, 2015 @ 18:31
    Gordon Saxby
    0

    On each product? I can't see that, nor is there anything on the Merchello settings? Can you let me know where it is.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 25, 2015 @ 18:50
    Rusty Swayne
    0

    Sorry, It should be false - but you'll have to step through your code to see it. I've added a task to expose it in the editors in version 1.12.0

    http://issues.merchello.com/youtrack/issue/M-821

  • Gordon Saxby 1444 posts 1855 karma points
    Aug 25, 2015 @ 19:03
    Gordon Saxby
    0

    It is false ... it also seems to vary and I'm not 100% sure, but it may work differently depending on how many times I save the product in the Admin?!?!

    :-(

  • Gordon Saxby 1444 posts 1855 karma points
    Aug 25, 2015 @ 22:04
    Gordon Saxby
    0

    My situation turned out to be slightly different (that's probably the polite phrase!!) in that none of the products had any variants, so the code above didn't work well.

    With Rusty's help, we came up with this which deals with the products themselves:

                // Send Order Confirmation emails (Customer and VFA Admin)
                Notification.Trigger("OrderConfirmation", e.Entity, new[] { contactEmail });
    
                // Update the stock quantity for the purchased product(s)
                IOrder value = e.Entity.Invoice.PrepareOrder();
                var productService = MerchelloContext.Current.Services.ProductService;
                var trackableItems = value.InventoryTrackedItems().ToArray();
                var products = productService.GetByKeys(trackableItems.Select(x => x.ExtendedData.GetProductKey())).ToArray();
    
                foreach (var item in e.Entity.Invoice.Items)
                {
                    var product = products.FirstOrDefault(x => x.Key == item.ExtendedData.GetProductKey());
    
                    if (product != null)
                    {
                        var inventory = product.CatalogInventories.FirstOrDefault(x => x.CatalogKey == item.ExtendedData.GetWarehouseCatalogKey());
    
                        if (inventory != null) inventory.Count -= item.Quantity;
    
                        productService.Save(product);
                    }                    
                }
    
  • Simon 692 posts 1068 karma points
    Oct 22, 2015 @ 09:46
    Simon
    0

    Hi Guys,

    I am using the PayPal Express Payment Provider as a payment.

    Once I redirecting to Paypal, I tried to implement the CaptureAttempted Event but it did not fires up.

    Does anyone can help me achieve this since I need to do some custom logic after a customer will pay via paypal.

    Thank you.

    Kind Regards.

  • Simon 692 posts 1068 karma points
    Oct 27, 2015 @ 09:27
    Simon
    0

    Hi Guys,

    I tracking the inventory products stock but it seems there isn't a stop-point where user will be notified that he has chosen more quantity than there is in stock.

    For example trying to checkout and user has chosen 100 quantity of a product and actually there is 10 in stock.

    Does anyone knows how can I tackle this please?

    Thank you.

    Kind Regards.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Oct 27, 2015 @ 23:08
    Rusty Swayne
    0

    Hey Simon,

    Products have a total inventory count property that could be checked (this is also on the variant if your product has options).

    There are a couple of options.

    You could using handle the Basket events AddingItem / RemovingItem to check inventory counts.

    Another way would be to replace the validation task

     Merchello.Web.Validation.Tasks.ValidateProductInventoryTask
    

    with one more specific to your sites requirements. If you look in the merchello.config file, you will see a task chain:

       <taskChain alias="ItemCacheValidation">
      <!-- Added Merchello Version 1.11.0
      This chain validates basket and wish list items against values in the back office to assert that the customer has not
      added items to their basket that were subsequently changed in the back office prior to checkout.  The process is needed
      as the relation between the basket and wish list items are decoupled from the actual persisted values.
      -->
        <tasks>
            <task type="Merchello.Web.Validation.Tasks.ValidateProductsExistTask, Merchello.Web" />
            <!--
                The following task is intended to assert that pricing and/or on sale value has not changed in the back office since the
                customer has placed an item into their basket or wish list. If you have made custom pricing modifications in your
                implementation, you may either remove this task or adjust your code to add a new extended data value
                merchLineItemAllowsValidation = false
                to the line item so that it is skipped in the validation process.
            -->
            <task type="Merchello.Web.Validation.Tasks.ValidateProductPriceTask, Merchello.Web" />
            <!--
                Validates that products are still in inventory
            -->
            <task type="Merchello.Web.Validation.Tasks.ValidateProductInventoryTask, Merchello.Web" />
        </tasks>
      </taskChain>
    

    If you have time, I'd love to see your use case ..

  • Simon 692 posts 1068 karma points
    Oct 28, 2015 @ 07:53
    Simon
    0

    Hi Rusty,

    thanks for your reply.

    Basically, all I want is that users would not be allowed to continue with checkout if they choose items in the cart which are out of stock or choose to update quantity of a product which there is not enough in stock.

    Could help me sort this out please?

    Thank you.

  • Simon 692 posts 1068 karma points
    Oct 28, 2015 @ 08:11
    Simon
    0

    And when should the task be fired (PerformTask) since I am testing and added a breakpoint in the PerformTask() in the ValidateProductInventoryTask and is not entering when I add product to cart, or update product cart item, or continue with the checkout?

    Thank you.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Oct 28, 2015 @ 16:25
    Rusty Swayne
    0

    Hey Simon,

    The chain is executed when the basket is created and when the basket is converted to SalePreparation.

    Here is the method:

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/Workflow/CustomerItemCache/CustomerItemCacheBase.cs#L690

    From the basket it is called:

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/Workflow/Basket.cs#L117

    When you start the sale preparation portion of the workflow it is called

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/Workflow/BasketSalePreparation.cs#L171

    Validate() is public so you can also call it directly from the Basket, Wishlist, and SalePreparation objects.

    I think I would create my own custom validation task to check the quantity against existing inventory and instead of simply removing the item as the existing task does show a message to your customer.

    Here is the existing task:

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/Validation/Tasks/ValidateProductInventoryTask.cs

    You could create your own class

        public class MyCustomerInventoryValidationTask : CustomerItemCacheValidatationTaskBase<ValidationResult<CustomerItemCacheBase>> 
        {
    
             public override Attempt<ValidationResult<CustomerItemCacheBase>> PerformTask(ValidationResult<CustomerItemCacheBase> value)
             {
                  // custom code.
             }
        }
    

    Here is the code for the ValidationResult

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/Validation/ValidationResult%7BT%7D.cs

  • Simon 692 posts 1068 karma points
    Oct 28, 2015 @ 20:12
    Simon
    0

    Hi Rusty,

    Thanks again for your reply.

    But may I ask why it might not working, since I have tried to reduce the quantity to 0 and try to buy something from that product, and nothing happens and stops me from buying.

  • Simon 692 posts 1068 karma points
    Oct 29, 2015 @ 08:54
    Simon
    0

    Hi Rusty,

    I am stuck how I am going to check the quantity selected from the user in the cart via the totalInventoryCount.

    I want that user will be disallowed to update the cart of chekcout when the user has some of the items which has the quantity requested greater than the current stock.

    Appreciate any help.

    Thank you.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 02, 2015 @ 21:45
    Rusty Swayne
    0

    Hey Simon,

    Are you still struggling with this? I think handling the AddingItem event on the basket would be the best approach.

  • Simon 692 posts 1068 karma points
    Nov 03, 2015 @ 07:50
    Simon
    0

    Hi Rusty,

    Yes, I'm still struggling with this. The user tough, can choose to update the quantity as well in cart, so it is still a problem.

    Can you please show examples how can I achieve this, please? Appreciate.

    thank you.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 03, 2015 @ 18:43
    Rusty Swayne
    0

    Hi Simon,

    I did not think about updating the quantities ... I'll add another event to the CustomerItemCacheBase class for updating / saving and get it into 1.13.1.

    http://issues.merchello.com/youtrack/issue/M-908

  • Simon 692 posts 1068 karma points
    Nov 03, 2015 @ 20:41
    Simon
    0

    Hi Rusty,

    Aren't there any way how I can check each cart product quantities after the user click on the checkout button to go to the addresses section?

    Thank you.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 03, 2015 @ 20:58
    Rusty Swayne
    0

    That could be done as a validation task as mentioned above.

  • Simon 692 posts 1068 karma points
    Nov 03, 2015 @ 21:04
    Simon
    0

    Ahh I see.

    Can you guide me please how I can add a new task and a new class etc, from where I need to extend etc... as I have already tried and it was not being fired /working

    Basically, I want that the cart will be validate once user checkout to go to the addresses page. Here I need to check the quantities of each product and if found a product in cart which has quantity greater the the stock, will be redirected back to the cart, informing the user.

    Thank you rusty very much for your patience.

  • Simon 692 posts 1068 karma points
    Nov 12, 2015 @ 09:29
    Simon
    0

    Hi Rusty,

    Any idea or help please?

    Thank you

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 12, 2015 @ 22:25
    Rusty Swayne
    0

    Hi Simon

    Have you created your own custom task following https://our.umbraco.org/projects/collaboration/merchello/merchello/63357-Inventory-tracking#comment-232800

    If so, you should be able to call the Validate() on the basket itself and get the result of your attempt right in your controller and if it returns an failed attempt you could do your redirect with a message you return.

    The validation should also be kicking off automatically as soon as your start interacting with the SalePreparation (BasketSalePreparation) object.

  • Simon 692 posts 1068 karma points
    Nov 16, 2015 @ 19:54
    Simon
    0

    Hi Rusty,

    I cannot figure it out in order to work. I cannot understand how can I create the custom task and the following.

    If you have patience :), could you please show me and explain to me step by step exactly what I should do, because I cannot get it work.

    Thank you very much Rusty, in advance.

    Kind Regards.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 17, 2015 @ 00:07
    Rusty Swayne
    0

    Hey Simon - can you send me a DM on twitter so we can coordinate. I'm thinking it would be easiest if your repository was somewhere I could see (even privately) . If we come up with a solution, it'd be great to be able to use the code as an example "how to" in the new documentation stuff we are working on.

  • Simon 692 posts 1068 karma points
    Nov 17, 2015 @ 07:59
    Simon
    0

    Hi Rusty,

    Thank you for your reply. Even for users that might experiencing the same issue,, can you please show how to/guide me how to:

    1. Create my custom task class, namespace, etc... code to check the current quantity of the cart via the existing product quantity

    2. How to reference and initiate the custom task in the umbracoSettings.config

    3. And Eventually how and where to call the validate() method in the basket.

    Appreciate any help via examples please. I hope we can get this working once day :)

    Thank you rusty once again.

    Kind Regards

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Nov 18, 2015 @ 19:13
    Rusty Swayne
    0

    Hi Simon,

    I am trying to put together an example for you - have you written a customer Basket or Checkout controller or are you trying to do this via the controllers that are included in the Bazaar starter kit?

  • Simon 692 posts 1068 karma points
    Nov 18, 2015 @ 20:07
    Simon
    0

    Hi Rusty,

    Thank you very much.

    I am using the Bazaar starter kit.

  • Tito 313 posts 622 karma points
    Nov 22, 2016 @ 16:39
    Tito
    0

    Hi Rusty,

    I need to decrement inventory depending on payment method:

    Credit card: only when payment is ok Bank transfer: on order creation.

    The problem is i have commented this line in merchello.config:

    <task type="Merchello.Core.Chains.ShipmentCreation.RemoveShipmentOrderItemsFromInventoryAndPersistShipmentTask, Merchello.Core" />
    

    Now only have:

        <task type="Merchello.Core.Chains.ShipmentCreation.AddShippableOrderLineItemsToShipmentTask, Merchello.Core" />
     <task type="Merchello.Core.Chains.ShipmentCreation.SetOrderStatusTask, Merchello.Core" />
    

    But when i fulfill the order in backoffice i get this message: "Shipment #0 created" and no shipment is created. How could i trace what is happening? if i uncomment the line in merchello.config the shipment is created.

  • Tito 313 posts 622 karma points
    Nov 22, 2016 @ 16:45
    Tito
    2

    Sorry, i have just read the name of the task "RemoveShipmentOrderItemsFromInventoryAndPersistShipmentTask" so if i removed the shipment is not persisted. So i am going to create a new task that persists the shipment without removing inventory!

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 22, 2016 @ 16:48
    Simon Dingley
    1

    I got caught by the exact same problem - only you seem to have found it quicker than I did! :)

  • iiii 15 posts 154 karma points
    Oct 30, 2017 @ 16:05
    iiii
    1

    btw when you remove the task:

    <task type="Merchello.Core.Chains.ShipmentCreation.RemoveShipmentOrderItemsFromInventoryAndPersistShipmentTask, Merchello.Core" />
    

    you should add a "Persist Shipment Task" in order, as the name says, to persist the shipment, otherwise is not saved.

    (just a note for distracted people like me ;) )

  • Dan 1285 posts 3917 karma points c-trib
    Jul 09, 2018 @ 09:56
    Dan
    0

    Has anyone on this thread dealt with the issue of stock levels not being updated with selling non-physical products? Reading some of the issues above, it sounds like the stock level is only affected when it's associated with shipments. In our case we have no shipments as the products are event tickets (they don't even have a download element, it's just taking a payment for something that's administered offline) in which case our stock levels are never modified.

    We're using Merchello 2.5.0 and I can't see any fixes for this issue in the 2.6.0 commits.

    Can anyone suggest how to work around this issue and get stock levels updating automatically at some point in the process?

Please Sign in or register to post replies

Write your reply to:

Draft