Copied to clipboard

Flag this post as spam?

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


  • Simon Dingley 1470 posts 3427 karma points c-trib
    Feb 08, 2016 @ 13:10
    Simon Dingley
    0

    Adding Default Product Options/values

    I am working on a store that sells shoes. Shoes come in standard sizes but in order to avoid the monotonous entry of the same shoe sizes for EVERY product I want to setup default options when a new product is created. For some reason this does not appear to be working:

    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        ProductService.Created += ProductServiceCreated;
    }
    
    private void ProductServiceCreated(IProductService sender, Core.Events.NewEventArgs<IProduct> e)
    {
        var productOptionCollection = new ProductOptionCollection();
    
        // Add default product option for shoe size
        var shoeSizeOption = new ProductOption("Size") { Required = true };
    
        // Add default product size options
        shoeSizeOption.Choices.Add(new ProductAttribute("UK2.5 | EU35.5 | US5", "UK25EU355US5"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK2 | EU35 | US4.5", "UK2EU35US45"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK3 | EU36 | US5.5", "UK3EU36US55"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK3.5 | EU36.5 | US6", "UK35EU365US6"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK4 | EU37 | US6.5", "UK4EU37US65"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK4.5 | EU37.5 | US7", "UK45EU375US7"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK5 | EU38 | US7.5", "UK5EU38US75"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK5.5 | EU38.5 | US8", "UK55EU385US8"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK6 | EU39 | US8.5", "UK6EU39US85"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK6.5 | EU39.5 | US9", "UK65EU395US9"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK7 | EU40 | US9.5", "UK7EU40US95"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK8 | EU41 | US10.5", "UK8EU41US105"));
        shoeSizeOption.Choices.Add(new ProductAttribute("UK9 | EU42 | US11.5", "UK9EU42US115"));
    
        // Add the shoe size option to the new product options collection
        productOptionCollection.Add(shoeSizeOption);
    
        // Set product options to the new product options collection 
        e.Entity.ProductOptions = productOptionCollection;
    
        // Save the product to persist the changes
        sender.Save(e.Entity);
    } 
    

    The UI loads and the This variant has options (like size or color) option remains unchecked and no product options are visible.

    Is there another step required?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Feb 08, 2016 @ 17:30
    Rusty Swayne
    0

    Hmm - your code looks fine. Maybe it's the order the events are firing.

    Can you add a product and then rebuild the product Examine index and see if they appear? I'd have to think about that one if it is the case.

    You can also create a template product and "Copy" it which copies all the options and extended content.

    Copy is available through the back office under the "Save" button.

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Feb 08, 2016 @ 17:42
    Simon Dingley
    0

    Hi Rusty,

    No they don't appear after rebuilding the Examine product index. I also checked the [merchProductOption] table and the product option is not in there so there is possibly something not firing in that chain events.

    Copy option is handy to know - thanks. I might be able to find some time tonight to step through the source to see if I can spot why the save event is not completing as expected.

    In case its of relevance the site is using v1.13.3.

    Cheers, Simon

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Feb 08, 2016 @ 17:44
    Rusty Swayne
    0

    Great Simon!

    The copy product is done through a chain of tasks. You can find the actual types in the merchello.config if you want to see how that is done.

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Feb 09, 2016 @ 10:33
    Simon Dingley
    0

    I upgraded last night to v1.14.0 before attempting to do anything further but the problem still persists.

    I also took a look at the CopyProduct task chain and from what I can see its not really much different in terms of the core steps but I updated my event handler a little to more closely match, still no luck.

    Stepping through the process I see that after uow.Commit(); is called the production option appears in the [merchProductOption] table. When we get into the EnsureVariants method and product.GetPossibleProductAttributeCombinations() is called there are no lists returned however the Save event is executed twice and in the next pass we get back the 13 expected combinations but the product no longer has any ProductOption in the collection.

    The call stack shows the second execution of the save event coming from the ProductApiController. Inside the AddProduct method is a call to the extension method ToProduct(this ProductDisplay productDisplay, IProduct destination) and this it seems is where we lose the product options I think.

    I will try and see if I can get any further with this today.

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Feb 09, 2016 @ 20:29
    Simon Dingley
    0

    Customer just contacted me to advise they can't actually copy a product either due to an exception:

    Received an error from the server
    Failed to delete detached content
    
    Object reference not set to an instance of an object.
    
    EXCEPTION DETAILS:
    
    System.NullReferenceException: Object reference not set to an instance of an object.
    STACKTRACE:
    
    at Merchello.Web.WebApi.JsonCamelCaseFormatter.OnActionExecuted(HttpActionExecutedContext httpActionExecutedContext)
       at System.Web.Http.Filters.ActionFilterAttribute.OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
    

    I don't think this is related but posting just in case.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Feb 09, 2016 @ 20:38
    Rusty Swayne
    0

    Are your event handlers in kicking in when he copies?

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Feb 09, 2016 @ 21:23
    Simon Dingley
    0

    Yes, I just discovered that and disabled them. The products he created prior to this have errors so will need to be recreated, I think that is a result of now having 2 product options with the same name. If I save the products now a null reference exception is thrown even after deleting the duplicated product option.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Feb 09, 2016 @ 21:36
    Rusty Swayne
    0

    Try rebuilding the ProductIndex?

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Feb 10, 2016 @ 10:10
    Simon Dingley
    0

    Unfortunately not. It will be quicker for me to recreate them at the moment in order to keep them happy.

  • Geoff R G Williams 3 posts 74 karma points
    Jun 14, 2016 @ 08:13
    Geoff R G Williams
    0

    Hey Simon,

    Thought I'd add my NZD0.02 in here as I struggled with the same thing under Merchello 2.1.0.

    Ultimately, any ProductOptions which I attempted to add in the Creating or Created event handlers for the Merchello.Core.Services.ProductService were not persisted to the database on save, even with an explicit call to sender.Save() such as you were doing above.

    However, they can be persisted from the Saving event handler off the same service.

    For your example above, try:

    public class UmbracoEventHandler : ApplicationEventHandler {
    private static UmbracoApplicationBase _umbracoApplication;
    private static ApplicationContext _applicationContext;
    private static object _startLock = new object();
    private static bool _started = false;
    
    public UmbracoEventHandler() {
    }
    
    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) {
        if (!_started) {
            lock (_startLock) {
                if (!_started) {
    
                    ProductService.Saving += ProductService_Saving;
    
                    _started = true;
                }
            }
        }
    }
    
    private void ProductService_Saving(IProductService sender, Umbraco.Core.Events.SaveEventArgs<IProduct> e) {
        foreach (var entity in e.SavedEntities) {
            if (entity.CreateDate == entity.UpdateDate) {   //new product
                entity.ProductOptions = new ProductOptionCollection() {
                    new ProductOption("Size") {
                        Choices = {
                            new ProductAttribute("UK2", "UK2"),
                            new ProductAttribute("UK2.5", "UK25")
                        },
                        Required = true
                    }
                };
            };
        }
    }
    

    Note that in absence of an "IsNew" or "IsInitialSave" field, I'm using entity.CreateDate == entity.UpdateDate to check whether the Product is a new product. You might consider a different approach here depending on your situation, and I can't guarantee that mine doesn't break under certain circumstances.

    You also don't need to explicitly call sender.Save() - in fact, doing so will kick you back into the event handler again.

    Hope that helps you or someone else in the future with this same issue. Let me know if you have any difficulties.

    Cheers and good luck!

    -Geoff R G Williams for Primal Blaze.

Please Sign in or register to post replies

Write your reply to:

Draft