Page 1 of 1

Adding Custom Charge

Posted: Wed Jul 15, 2015 3:17 pm
by Brewhaus
We are trying to use variants or kitting to add a preset charge that is static (ie. it is a charge that will not change with order qty). The goal is specifically for our private label sauces, allowing us to add an artwork fee to new custom labels. This will not be based on the qty of a sauce that the customer orders, and will only be assigned to one variant. It is straight forward adding a per unit add-on, but how can we add a flat fee when a specific selection is made? This way, if a customer wants a generic label, or already has custom artwork on file, they will not have the charge added.

We have tried using a separate 'product' that must be added if custom work is involved, but, of course, nobody adds that by choice- hoping that we will just let it slide through. That creates the hassle of calling the customer, adding to their order, and getting authorization for the charge. It is much easier, and more professional, to have it done seamlessly based on their selection.

Re: Adding Custom Charge

Posted: Fri Jul 17, 2015 3:09 am
by mazhar
Not sure but what about placing some code to add basket item of type OrderItemType.Charge when some option is selected. The complicated part of this will be to ensure to add/remove charge item if basket changes.

Re: Adding Custom Charge

Posted: Fri Jul 17, 2015 4:24 am
by Brewhaus
Variants are the perfect option, unfortunately the extra charge will be added for each bottle, not as a one-time charge. The advantage is that the charge would also be removed if the product is removed from the cart. Kitting would do the same thing, but has the same problem- it is added once for each bottle purchased. I expect that it would require some in-depth coding to create a variant or kit item that only adds a flat rate once.

One thought- is it possible to add a 'handling fee' to a product? If so, then maybe the handling fee could be added only if a specific variant or kit item was selected.

Re: Adding Custom Charge

Posted: Fri Jul 17, 2015 6:58 am
by jguengerich
I have a similar requirement - I need to add quantity 1 of product B for every 1-10 quantity of product A (so quantity 1-10 A = 1 B, quantity 11-20 A = 2 B, etc.). I implemented it using kits and an entry in the ac_CustomFields table related to the ac_KitProducts table.

I had to modify the following site pages:
Admin/Products/Kits/AddKitProduct
Admin/Products/Kits/EditKitProduct

and the following source code files:
CommerceBuilder/Products/KitProduct.cs
CommerceBuilder/Products/KitProductRepository.cs
CommerceBuilder/Services/Checkout/CheckoutService.cs
CommerceBuilder/Services/Checkout/BasketService.cs

If you want, I can try to dig up what the actual changes were. Note that is on R5, not using single page checkout.

EDIT: Your changes would probably be simpler than mine, because you don't care about the actual quantity of product A, you just want only one of Product B. So you'd only need a "true/false" flag for your kit that means "only add one/standard behavior". You still might need to change the same files, but the changes might be less code.

EDIT 2: Corrected site and source code file names.

Re: Adding Custom Charge

Posted: Fri Jul 17, 2015 7:26 am
by Brewhaus
If you are able to dig up the information / method that you used without too much trouble, and don't mind sharing it, that would be very much appreciated. :-)

Re: Adding Custom Charge

Posted: Fri Jul 17, 2015 8:10 am
by jguengerich
I can share what I did, but you'll still have to modify it for your scenario.

In both Admin/Products/Kits/AddKitProducts.aspx and Admin/Products/Kits/EditKitProduct.aspx, added the following right under the KitQuantity row:

Code: Select all

                                <tr>
                                    <th>
                                        <cb:ToolTipLabel ID="ParentQuantityLabel" runat="server" Text="Parent quantity grouping:" AssociatedControlID="ParentQuantity" ToolTip="If parent quantities > 1 should only get a fixed quantity of this product, enter that parent quantity here.  For example, a 10 here and a quantity 1 above would mean that any quantity from 1-10 of the parent product will add 1 of this product.  Leave at 1 to add the above quantity of this product for every parent product"></cb:ToolTipLabel>
                                    </th>
                                    <td>
                                        <asp:TextBox ID="ParentQuantity" runat="server" Columns="2" MaxLength="3" Text="1"></asp:TextBox>
                                    </td>
                                </tr>
In Admin/Products/Kits/AddKitProducts.aspx.cs, added the following in FinishButton_Click, at the end of the "if (quantity > 0)..." section:

Code: Select all

                if (quantity > 0)
                {
                    // existing code here....

                    TextBox ParentQuantity = (TextBox)ri.FindControl("ParentQuantity");
                    int parentQuantity = AlwaysConvert.ToInt(ParentQuantity.Text);
                    if (parentQuantity == 0) parentQuantity = 1;
                    AbleCommerce.Code.CustomFieldHelper.SetInt(kitProduct, "ac_KitProducts", "ParentQuantity", parentQuantity);
                    ParentQuantity.Text = parentQuantity.ToString();
                }
In Admin/Products/Kits/EditKitProduct.aspx.cs, added the following at the end of Page_Init:

Code: Select all

        int? parentQuantity = AbleCommerce.Code.CustomFieldHelper.GetInt(_KitProduct, "ac_KitProducts", "ParentQuantity");
        if (!parentQuantity.HasValue) parentQuantity = 1;
        ParentQuantity.Text = parentQuantity.ToString();
In Admin/Products/Kits/EditKitProduct.aspx.cs, added the following in SaveButton_Click, at the end of the "if (quantity > 0)..." section:

Code: Select all

        if (quantity > 0)
        {
            // existing code here....

            int parentQuantity = AlwaysConvert.ToInt(ParentQuantity.Text);
            if (parentQuantity == 0) parentQuantity = 1;
            AbleCommerce.Code.CustomFieldHelper.SetInt(_KitProduct, "ac_KitProducts", "ParentQuantity", parentQuantity);
            ParentQuantity.Text = parentQuantity.ToString();
        }
In CommerceBuilder/Products/KitProduct.cs, added the following to the DisplayName field, right before the "return displayName;" statement:

Code: Select all

                NHibernate.ICriteria quantityGroupingCriteria = NHibernateHelper.CreateCriteria<CommerceBuilder.Stores.CustomField>()
                    .Add(NHibernate.Criterion.Restrictions.Eq("Store.Id", CommerceBuilder.Common.AbleContext.Current.StoreId) &&
                            NHibernate.Criterion.Restrictions.Eq("TableName", "ac_KitProducts") &&
                            NHibernate.Criterion.Restrictions.Eq("ForeignKeyId", this.Id) &&
                            NHibernate.Criterion.Restrictions.Eq("FieldName", "ParentQuantity"));
                System.Collections.Generic.IList<CommerceBuilder.Stores.CustomField> kitProductFields = CommerceBuilder.Stores.CustomFieldDataSource.LoadForCriteria(quantityGroupingCriteria);
                if (kitProductFields.Count > 0)
                {
                    int parentQuantity = AlwaysConvert.ToInt(kitProductFields[0].FieldValue);
                    if (parentQuantity > 1)
                    {
                        displayName = displayName + " (for up to " + parentQuantity.ToString() + " parts)";
                    }
                }
In CommerceBuilder/Products/KitProductRepository.cs, added the following to override BeforeDelete:

Code: Select all

        /// <inheritdoc />
        public override void BeforeDelete(object entity)
        {
            NHibernate.ICriteria quantityGroupingCriteria = NHibernateHelper.CreateCriteria<CommerceBuilder.Stores.CustomField>()
                .Add(NHibernate.Criterion.Restrictions.Eq("Store.Id", CommerceBuilder.Common.AbleContext.Current.StoreId) &&
                        NHibernate.Criterion.Restrictions.Eq("TableName", "ac_KitProducts") &&
                        NHibernate.Criterion.Restrictions.Eq("ForeignKeyId", ((KitProduct)entity).Id) &&
                        NHibernate.Criterion.Restrictions.Eq("FieldName", "ParentQuantity"));
            System.Collections.Generic.IList<CommerceBuilder.Stores.CustomField> kitProductFields = CommerceBuilder.Stores.CustomFieldDataSource.LoadForCriteria(quantityGroupingCriteria);
            foreach (CommerceBuilder.Stores.CustomField oneField in kitProductFields)
            {
                oneField.Delete();
            }
        }
In CommerceBuilder/Services/Checkout/BasketService.cs, in the RecalculateItems method, find:

Code: Select all

                                    childItem.Quantity = (short)(kitProduct.Quantity * basketItem.Quantity);
and replace it with:

Code: Select all

                                    decimal parentQuantity = GetCustomDecimal(kitProduct, "ac_KitProducts", "ParentQuantity");
                                    if (parentQuantity < 1.0M) parentQuantity = 1.0M;
                                    parentQuantity = Math.Ceiling((decimal)basketItem.Quantity / parentQuantity);
                                    childItem.Quantity = (short)(kitProduct.Quantity * (short)parentQuantity);
In CommerceBuilder/Services/Checkout/CheckoutService.cs, in the GenerateKitOrderItems method, find:

Code: Select all

                                item.Quantity = (short)(kp.Quantity * basketItem.Quantity);
and replace it with:

Code: Select all

                                string parentQuantityString = GetCustomString(kp, "ac_KitProducts", "ParentQuantity");
                                decimal parentQuantity = string.IsNullOrEmpty(parentQuantityString) ? 1.0M : AlwaysConvert.ToDecimal(parentQuantityString);
                                if (parentQuantity < 1.0M) parentQuantity = 1.0M;
                                parentQuantity = Math.Ceiling((decimal)basketItem.Quantity / parentQuantity);
                                item.Quantity = (short)(kp.Quantity * (short)parentQuantity);
I'm not sure where I was in the process of learning C#, ASP.NET, and NHibernate when I wrote that code, so I can't vouch for how efficient or "correct" the code is, other than that it seems to be working fine :). Also, you may have to add some "using" statements to some of those files to include the required namespaces.

Re: Adding Custom Charge

Posted: Mon Jul 20, 2015 6:14 am
by Brewhaus
Thank you very much. I had hoped to look at this over the weekend, but time got away from me. Hopefully I can get through it this week and test it on our DEV site.