/// <summary> /// Adds the discount to line item. /// </summary> /// <param name="order">The order.</param> /// <param name="itemRecord">The item record.</param> /// <param name="entryCode">The entry code.</param> /// <param name="itemDiscount">The item discount.</param> /// <param name="orderLevelDiscount">The order level discount.</param> private void AddDiscountToLineItem(OrderGroup order, PromotionItemRecord itemRecord, PromotionEntry promotionEntry, decimal itemDiscount, decimal orderLevelDiscount) { LineItem item = FindLineItemByPromotionEntry(order, promotionEntry); if (item != null) { // Add line item properties item.LineItemDiscountAmount += itemDiscount; item.OrderLevelDiscountAmount += orderLevelDiscount; item.ExtendedPrice = item.ListPrice * item.Quantity - item.LineItemDiscountAmount - item.OrderLevelDiscountAmount; if (itemRecord.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Entry).Key, StringComparison.OrdinalIgnoreCase)) { LineItemDiscount discount = FindLineItemDiscountById(order, itemRecord.PromotionItem.DataRow.PromotionId, item.LineItemId); if (discount == null) { discount = new LineItemDiscount(); } discount.DiscountAmount = itemRecord.PromotionItem.DataRow.OfferAmount; discount.DiscountCode = itemRecord.PromotionItem.DataRow.CouponCode; discount.DiscountName = itemRecord.PromotionItem.DataRow.Name; discount.DiscountValue = itemRecord.PromotionItem.DataRow.OfferAmount; discount.DisplayMessage = GetDisplayName(itemRecord.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); discount.LineItemId = item.LineItemId; discount.DiscountId = itemRecord.PromotionItem.DataRow.PromotionId; item.Discounts.Add(discount); } } }
/// <summary> /// Pre processes item record adding additional LineItems if needed. /// </summary> /// <param name="order">The order.</param> /// <param name="record">The record.</param> private void PreProcessItemRecord(OrderGroup order, PromotionItemRecord record) { // We do special logic for the gift promotion reward if (record.PromotionReward is GiftPromotionReward) { // Check if item already in the cart, if not add if (((GiftPromotionReward)record.PromotionReward).AddStrategy == GiftPromotionReward.Strategy.AddWhenNeeded) { // We assume that all affected entries are the gifts that need to be added to the cart foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { LineItem item = FindLineItemByCatalogEntryId(order, entry.CatalogEntryCode); // Didn't find, add it if (item == null) { // we should some kind of delegate or common implementation here so we can use the same function in both discount and front end Entry catEntry = CatalogContext.Current.GetCatalogEntry(entry.CatalogEntryCode); LineItem lineItem = CreateLineItem(catEntry, entry.Quantity); // Need to determine which order form the entry belongs to (which should be the same as the target entry) // hack: add to the first order.OrderForms[0].LineItems.Add(lineItem); } } } } }
/// <summary> /// Adds the promotion item record. /// </summary> /// <param name="reward">The reward.</param> /// <param name="entriesCollection">The entries collection.</param> /// <returns></returns> public void AddPromotionItemRecord(PromotionReward reward, params PromotionEntriesSet[] entrySetsCollection) { foreach (PromotionEntriesSet entrySet in entrySetsCollection) { PromotionItemRecord record = new PromotionItemRecord(this.PromotionContext.TargetEntriesSet, entrySet, reward); record.PromotionItem = PromotionContext.CurrentPromotion; PromotionContext.PromotionResult.PromotionRecords.Add(record); } }
/// <summary> /// Pre processes item record adding additional LineItems if needed. /// </summary> /// <param name="order">The order.</param> /// <param name="record">The record.</param> private void PreProcessItemRecord(OrderGroup order, PromotionItemRecord record) { // We do special logic for the gift promotion reward if (record.PromotionReward is GiftPromotionReward) { // Check if item already in the cart, if not add if (((GiftPromotionReward)record.PromotionReward).AddStrategy == GiftPromotionReward.Strategy.AddWhenNeeded) { // We assume that all affected entries are the gifts that need to be added to the cart foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { LineItem giftLineItem = FindGiftLineItemInOrder(order, entry.CatalogEntryCode, record); if (!IsOrderHaveSpecifiedGiftPromotion(order, record)) { // Didn't find, add it if (giftLineItem == null) { // we should some kind of delegate or common implementation here so we can use the same function in both discount and front end CatalogEntryResponseGroup responseGroup = new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.Variations); Entry catEntry = CatalogContext.Current.GetCatalogEntry(entry.CatalogEntryCode, responseGroup); giftLineItem = AddNewGiftLineItemToOrder(order, catEntry, entry.Quantity); AddGiftItemToAShipment(giftLineItem, giftLineItem.Parent.LineItems.Count - 1); CatalogEntryDto entryDto = CatalogContext.Current.GetCatalogEntryDto(giftLineItem.Code, responseGroup); CatalogEntryDto.CatalogEntryRow entryRow = entryDto.CatalogEntry[0]; Money?price = GetItemPrice(entryRow, giftLineItem, CustomerContext.Current.CurrentContact); giftLineItem.ListPrice = price.HasValue ? price.Value.Amount : 0m; giftLineItem.PlacedPrice = giftLineItem.ListPrice; // populate inventory information for giftLineItem var aggregateInventory = ServiceLocator.Current.GetInstance <IInventoryService>().QueryByEntry(new [] { entryRow.Code }); foreach (var inventoryRecord in aggregateInventory) { PopulateInventoryInfo(inventoryRecord, giftLineItem); } } else { giftLineItem.Quantity = Math.Max(entry.Quantity, giftLineItem.Quantity); var index = giftLineItem.Parent.LineItems.IndexOf(giftLineItem); if (!giftLineItem.Parent.Shipments.SelectMany(x => x.LineItemIndexes).Contains(index.ToString())) { AddGiftItemToAShipment(giftLineItem, index); } } } else { entry.Quantity = giftLineItem != null?Math.Min(entry.Quantity, giftLineItem.Quantity) : entry.Quantity; } entry.Owner = giftLineItem; entry.CostPerEntry = giftLineItem != null ? giftLineItem.ListPrice : 0m; } } } }
private bool IsOrderHaveSpecifiedGiftPromotion(OrderGroup order, PromotionItemRecord promoRecord) { bool retVal = false; foreach (OrderFormDiscount discount in order.OrderForms[0].Discounts) { if (GetGiftPromotionName(promoRecord) == discount.DiscountName) { retVal = true; break; } } return(retVal); }
/// <summary> /// Gets the discount amount for one entry only. /// </summary> /// <param name="record">The record.</param> /// <param name="reward">The reward.</param> /// <returns></returns> private static decimal GetDiscountAmount(PromotionItemRecord record, PromotionReward reward) { decimal discountAmount = 0; if (reward.RewardType == PromotionRewardType.EachAffectedEntry || reward.RewardType == PromotionRewardType.AllAffectedEntries) { if (reward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = record.AffectedEntriesSet.TotalCost * reward.AmountOff / 100; } else // need to split discount between all items { discountAmount += reward.AmountOff; // since we assume only one entry in affected items } } return(Math.Round(discountAmount, 2)); }
private decimal GetDiscountAmount(PromotionItemRecord record, PromotionReward reward) { decimal discountAmount = 0; if (reward.RewardType != PromotionRewardType.EachAffectedEntry && reward.RewardType != PromotionRewardType.AllAffectedEntries) { return(Math.Round(discountAmount, 2)); } if (reward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = record.AffectedEntriesSet.TotalCost * reward.AmountOff / 100; } else { discountAmount += reward.AmountOff; } return(Math.Round(discountAmount, 2)); }
private bool IsOrderHaveSpecifiedGiftPromotion(OrderGroup order, PromotionItemRecord promoRecord) { bool retVal = false; foreach (OrderFormDiscount discount in order.OrderForms[0].Discounts) { if (GetGiftPromotionName(promoRecord) == discount.DiscountName) { retVal = true; break; } } return retVal; }
private LineItem FindGiftLineItemInOrder(OrderGroup order, string catalogEntryId, PromotionItemRecord promoRecord) { var lineItems = order.OrderForms[0].LineItems.ToArray().Where(x => x.Code == catalogEntryId); foreach (var lineitem in lineItems) { foreach (LineItemDiscount discount in lineitem.Discounts) { if (discount.DiscountName == GetGiftPromotionName(promoRecord)) { return lineitem; } } } return null; }
private void AddDiscountToLineItem(OrderGroup order, PromotionItemRecord itemRecord, PromotionEntry promotionEntry, decimal itemDiscount, decimal orderLevelDiscount) { orderLevelDiscount = Math.Floor(orderLevelDiscount * 100) * 0.01m; LineItem item = FindLineItemByPromotionEntry(order, promotionEntry); if (item != null) { //reset gift line item discount if (IsGiftLineItem(item)) { item.PlacedPrice = promotionEntry.CostPerEntry; item.LineItemDiscountAmount = itemDiscount; item.OrderLevelDiscountAmount = 0; item.ExtendedPrice = item.PlacedPrice; } else { // Add line item properties item.LineItemDiscountAmount += itemDiscount; item.OrderLevelDiscountAmount += orderLevelDiscount; item.ExtendedPrice = item.PlacedPrice * item.Quantity - item.LineItemDiscountAmount - item.OrderLevelDiscountAmount; } if (itemRecord.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Entry).Key, StringComparison.OrdinalIgnoreCase) || itemRecord.PromotionReward is GiftPromotionReward || (itemRecord.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Order).Key, StringComparison.OrdinalIgnoreCase) && itemRecord.PromotionReward.RewardType == PromotionRewardType.EachAffectedEntry)) { LineItemDiscount discount = FindLineItemDiscountById(order, itemRecord.PromotionItem.DataRow.PromotionId, item.LineItemId); if (discount == null) { discount = new LineItemDiscount(); item.Discounts.Add(discount); } var discountName = itemRecord.PromotionItem.DataRow.Name; if (itemRecord.PromotionReward is GiftPromotionReward) { discount.DiscountName = GetGiftPromotionName(itemRecord); } else { discount.DiscountName = String.Format("{0}{1}", itemRecord.PromotionItem.DataRow.Name, itemRecord.PromotionItem.DataRow.OfferType == 1 ? ":PercentageBased" : ":ValueBased"); } discount.DiscountAmount = itemRecord.PromotionReward.AmountOff; discount.DiscountCode = itemRecord.PromotionItem.DataRow.CouponCode; discount.DiscountValue = itemDiscount; // use the promotion name if the localized display message is null or empty discount.DisplayMessage = GetDisplayName(itemRecord.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); if (string.IsNullOrEmpty(discount.DisplayMessage)) discount.DisplayMessage = itemRecord.PromotionItem.DataRow.Name; discount.LineItemId = item.LineItemId; discount.DiscountId = itemRecord.PromotionItem.DataRow.PromotionId; } } }
/// <summary> /// Applies the item discount. /// </summary> /// <param name="order">The order.</param> /// <param name="record">The record.</param> /// <param name="totalAmount">The total amount.</param> /// <returns></returns> private decimal ApplyItemDiscount(OrderGroup order, PromotionItemRecord record, decimal totalAmount) { decimal discountAmount = 0; if (record.PromotionReward.RewardType == PromotionRewardType.AllAffectedEntries) { if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = record.AffectedEntriesSet.TotalCost * record.PromotionReward.AmountOff / 100; decimal averageDiscountAmount = discountAmount / record.AffectedEntriesSet.TotalQuantity; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { // Sasha: changed back, CostPerEntry does not change dynamically, while total cost does // AddDiscountToLineItem(order, record, entry, entry.CostPerEntry * entry.Quantity * record.PromotionReward.AmountOff / 100m, 0); // AddDiscountToLineItem(order, record, entry.CatalogEntryCode, averageDiscountAmount * entry.Quantity, 0); AddDiscountToLineItem(order, record, entry, averageDiscountAmount * entry.Quantity, 0); } } else // need to split discount between all items { discountAmount = record.PromotionReward.AmountOff; decimal averageDiscountAmount = record.PromotionReward.AmountOff / record.AffectedEntriesSet.TotalQuantity; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, averageDiscountAmount * entry.Quantity, 0); } } } else if (record.PromotionReward.RewardType == PromotionRewardType.EachAffectedEntry) { if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = record.AffectedEntriesSet.TotalCost * record.PromotionReward.AmountOff / 100; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, entry.CostPerEntry * entry.Quantity * record.PromotionReward.AmountOff / 100, 0); } } else { discountAmount = record.AffectedEntriesSet.TotalQuantity * record.PromotionReward.AmountOff; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, record.PromotionReward.AmountOff * entry.Quantity, 0); } } } else if (record.PromotionReward.RewardType == PromotionRewardType.WholeOrder) { decimal percentageOffTotal = 0; if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { decimal extendedCost = 0; foreach (OrderForm orderForm in order.OrderForms) { extendedCost += orderForm.LineItems.ToArray().Sum(x => x.ExtendedPrice); } // calculate percentage adjusted by the running amount, so it will be a little less if running amount is less than total percentageOffTotal = (record.PromotionReward.AmountOff / 100) * (totalAmount / extendedCost); //percentageOffTotal = PromotionReward.AmountOff / 100; discountAmount = totalAmount * record.PromotionReward.AmountOff / 100; } else { // Calculate percentage off discount price if (totalAmount > 0) { percentageOffTotal = record.PromotionReward.AmountOff / totalAmount; // but since CostPerEntry is not an adjusted price, we need to take into account additional discounts already applied percentageOffTotal = percentageOffTotal * (totalAmount / record.AffectedEntriesSet.TotalCost); } else { percentageOffTotal = 100m; } discountAmount = record.PromotionReward.AmountOff; } // Now distribute discount amount evenly over all entries taking into account running total // Special case for shipments, we consider WholeOrder to be a shipment if (!record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Shipping).Key, StringComparison.OrdinalIgnoreCase)) { foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { LineItem item = FindLineItemByPromotionEntry(order, entry); AddDiscountToLineItem(order, record, entry, 0, item.ExtendedPrice * percentageOffTotal); } } } // Save discounts if (record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Order).Key, StringComparison.OrdinalIgnoreCase) || record.PromotionReward is GiftPromotionReward) { if (record.PromotionReward.RewardType == PromotionRewardType.WholeOrder) { OrderFormDiscount discount = FindOrderFormDiscountById(order, record.PromotionItem.DataRow.PromotionId, Int32.Parse(record.AffectedEntriesSet.OrderFormId)); bool hasOrderFormDiscount = true; if (discount == null) { discount = new OrderFormDiscount(); hasOrderFormDiscount = false; } var discountName = record.PromotionItem.DataRow.Name; if (record.PromotionReward is GiftPromotionReward) { discountName = GetGiftPromotionName(record); } discount.DiscountName = discountName; discount.DiscountAmount = record.PromotionReward.AmountOff; discount.DiscountCode = record.PromotionItem.DataRow.CouponCode; discount.DiscountValue = hasOrderFormDiscount ? discountAmount + discount.DiscountValue : discountAmount; discount.DisplayMessage = GetDisplayName(record.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); discount.OrderFormId = Int32.Parse(record.AffectedEntriesSet.OrderFormId); discount.DiscountId = record.PromotionItem.DataRow.PromotionId; foreach (OrderForm form in order.OrderForms) { if (form.OrderFormId == discount.OrderFormId && !hasOrderFormDiscount) form.Discounts.Add(discount); } } } else if (record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Shipping).Key, StringComparison.OrdinalIgnoreCase)) { ShipmentDiscount discount = FindShipmentDiscountById(order, record.PromotionItem.DataRow.PromotionId, Int32.Parse(record.AffectedEntriesSet.ShipmentId)); if (discount == null) discount = new ShipmentDiscount(); discount.DiscountAmount = record.PromotionReward.AmountOff; discount.DiscountCode = record.PromotionItem.DataRow.CouponCode; discount.DiscountName = record.PromotionItem.DataRow.Name; discount.DisplayMessage = GetDisplayName(record.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); discount.ShipmentId = Int32.Parse(record.AffectedEntriesSet.ShipmentId); discount.DiscountId = record.PromotionItem.DataRow.PromotionId; foreach (OrderForm form in order.OrderForms) { foreach (Shipment shipment in form.Shipments) { if (shipment.ShipmentId == discount.ShipmentId) { shipment.Discounts.Add(discount); if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = shipment.ShippingSubTotal * record.PromotionReward.AmountOff / 100; } else { discountAmount = Math.Min(record.PromotionReward.AmountOff, shipment.ShippingSubTotal); } shipment.ShippingDiscountAmount = Math.Min(shipment.ShippingDiscountAmount + discountAmount, shipment.ShippingSubTotal); // ShippingDiscountAmount will not be subtracted from the ShipmentTotal per discussions on 2/22/2012. break; } } } discount.DiscountValue = discountAmount; } return discountAmount; }
/// <summary> /// Pre processes item record adding additional LineItems if needed. /// </summary> /// <param name="order">The order.</param> /// <param name="record">The record.</param> private void PreProcessItemRecord(OrderGroup order, PromotionItemRecord record) { // We do special logic for the gift promotion reward if (record.PromotionReward is GiftPromotionReward) { // Check if item already in the cart, if not add if (((GiftPromotionReward)record.PromotionReward).AddStrategy == GiftPromotionReward.Strategy.AddWhenNeeded) { // We assume that all affected entries are the gifts that need to be added to the cart foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { LineItem giftLineItem = FindGiftLineItemInOrder(order, entry.CatalogEntryCode, record); if (!IsOrderHaveSpecifiedGiftPromotion(order, record)) { // Didn't find, add it if (giftLineItem == null) { // we should some kind of delegate or common implementation here so we can use the same function in both discount and front end CatalogEntryResponseGroup responseGroup = new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.Variations); Entry catEntry = CatalogContext.Current.GetCatalogEntry(entry.CatalogEntryCode, responseGroup); giftLineItem = AddNewGiftLineItemToOrder(order, catEntry, entry.Quantity); AddGiftItemToAShipment(giftLineItem); CatalogEntryDto entryDto = CatalogContext.Current.GetCatalogEntryDto(giftLineItem.Code, responseGroup); CatalogEntryDto.CatalogEntryRow entryRow = entryDto.CatalogEntry[0]; Money? price = GetItemPrice(entryRow, giftLineItem, CustomerContext.Current.CurrentContact); giftLineItem.ListPrice = price.HasValue ? price.Value.Amount : 0m; giftLineItem.PlacedPrice = giftLineItem.ListPrice; // populate inventory information for giftLineItem IWarehouseInventory aggregateInventory = ServiceLocator.Current.GetInstance<IWarehouseInventoryService>().GetTotal(new CatalogKey(entryRow)); PopulateInventoryInfo(aggregateInventory, giftLineItem); } else { giftLineItem.Quantity = Math.Max(entry.Quantity, giftLineItem.Quantity); } } else { entry.Quantity = giftLineItem != null ? Math.Min(entry.Quantity, giftLineItem.Quantity) : entry.Quantity; } entry.Owner = giftLineItem; entry.CostPerEntry = giftLineItem != null ? giftLineItem.ListPrice : 0m; } } } }
/// <summary> /// Adds the promotion item record. /// </summary> /// <param name="record">The record.</param> /// <returns></returns> public PromotionItemRecord AddPromotionItemRecord(PromotionItemRecord record) { PromotionContext.PromotionResult.PromotionRecords.Add(record); return(record); }
private LineItem FindGiftLineItemInOrder(OrderGroup order, string catalogEntryId, PromotionItemRecord promoRecord) { var lineItems = order.OrderForms[0].LineItems.ToArray().Where(x => x.Code == catalogEntryId); foreach (var lineitem in lineItems) { foreach (LineItemDiscount discount in lineitem.Discounts) { if (discount.DiscountName == GetGiftPromotionName(promoRecord)) { return(lineitem); } } } return(null); }
private void AddDiscountToLineItem(OrderGroup order, PromotionItemRecord itemRecord, PromotionEntry promotionEntry, decimal itemDiscount, decimal orderLevelDiscount) { itemDiscount = _currency.Round(itemDiscount); orderLevelDiscount = _currency.Round(orderLevelDiscount); LineItem item = FindLineItemByPromotionEntry(order, promotionEntry); if (item != null) { //reset gift line item discount if (IsGiftLineItem(item)) { item.PlacedPrice = promotionEntry.CostPerEntry; item.LineItemDiscountAmount = itemDiscount; item.OrderLevelDiscountAmount = 0; item.ExtendedPrice = item.PlacedPrice; } else { // Add line item properties item.LineItemDiscountAmount += itemDiscount; item.OrderLevelDiscountAmount += orderLevelDiscount; item.ExtendedPrice = item.PlacedPrice * item.Quantity - item.LineItemDiscountAmount - item.OrderLevelDiscountAmount; } if (itemRecord.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Entry).Key, StringComparison.OrdinalIgnoreCase) || itemRecord.PromotionReward is GiftPromotionReward || (itemRecord.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Order).Key, StringComparison.OrdinalIgnoreCase) && itemRecord.PromotionReward.RewardType == PromotionRewardType.EachAffectedEntry)) { LineItemDiscount discount = FindLineItemDiscountById(order, itemRecord.PromotionItem.DataRow.PromotionId, item.LineItemId); if (discount == null) { discount = new LineItemDiscount(); item.Discounts.Add(discount); } var discountName = itemRecord.PromotionItem.DataRow.Name; if (itemRecord.PromotionReward is GiftPromotionReward) { discount.DiscountName = GetGiftPromotionName(itemRecord); } else { discount.DiscountName = String.Format("{0}{1}", itemRecord.PromotionItem.DataRow.Name, itemRecord.PromotionItem.DataRow.OfferType == 1 ? ":PercentageBased" : ":ValueBased"); } discount.DiscountAmount = itemRecord.PromotionReward.AmountOff; discount.DiscountCode = itemRecord.PromotionItem.DataRow.CouponCode; discount.DiscountValue = itemDiscount; // use the promotion name if the localized display message is null or empty discount.DisplayMessage = GetDisplayName(itemRecord.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); if (string.IsNullOrEmpty(discount.DisplayMessage)) { discount.DisplayMessage = itemRecord.PromotionItem.DataRow.Name; } discount.LineItemId = item.LineItemId; discount.DiscountId = itemRecord.PromotionItem.DataRow.PromotionId; } } }
/// <summary> /// Applies the item discount. /// </summary> /// <param name="order">The order.</param> /// <param name="record">The record.</param> /// <param name="totalAmount">The total amount.</param> /// <returns></returns> private decimal ApplyItemDiscount(OrderGroup order, PromotionItemRecord record, decimal totalAmount) { decimal discountAmount = 0; if (record.PromotionReward.RewardType == PromotionRewardType.AllAffectedEntries) { decimal averageDiscountAmount; if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = _currency.Percentage(record.AffectedEntriesSet.TotalCost, record.PromotionReward.AmountOff); averageDiscountAmount = discountAmount / record.AffectedEntriesSet.TotalQuantity; } else // need to split discount between all items { discountAmount = record.PromotionReward.AmountOff; averageDiscountAmount = record.PromotionReward.AmountOff / record.AffectedEntriesSet.TotalQuantity; } foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, averageDiscountAmount * entry.Quantity, 0); } } else if (record.PromotionReward.RewardType == PromotionRewardType.EachAffectedEntry) { if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = _currency.Percentage(record.AffectedEntriesSet.TotalCost, record.PromotionReward.AmountOff); foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, entry.CostPerEntry * entry.Quantity * record.PromotionReward.AmountOff / 100, 0); } } else { discountAmount = record.AffectedEntriesSet.TotalQuantity * record.PromotionReward.AmountOff; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, record.PromotionReward.AmountOff * entry.Quantity, 0); } } } else if (record.PromotionReward.RewardType == PromotionRewardType.WholeOrder) { decimal percentageOffTotal = 0; if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { decimal extendedCost = 0; foreach (OrderForm orderForm in order.OrderForms) { extendedCost += orderForm.LineItems.ToArray().Sum(x => x.ExtendedPrice); } // calculate percentage adjusted by the running amount, so it will be a little less if running amount is less than total percentageOffTotal = (record.PromotionReward.AmountOff / 100) * (totalAmount / extendedCost); discountAmount = _currency.Percentage(totalAmount, record.PromotionReward.AmountOff); } else { // Calculate percentage off discount price if (totalAmount > 0) { percentageOffTotal = record.PromotionReward.AmountOff / totalAmount; // but since CostPerEntry is not an adjusted price, we need to take into account additional discounts already applied percentageOffTotal = percentageOffTotal * (totalAmount / record.AffectedEntriesSet.TotalCost); discountAmount = record.PromotionReward.AmountOff; } else { percentageOffTotal = 100m; discountAmount = 0; } } // Now distribute discount amount evenly over all entries taking into account running total // Special case for shipments, we consider WholeOrder to be a shipment if (!record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Shipping).Key, StringComparison.OrdinalIgnoreCase)) { foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { LineItem item = FindLineItemByPromotionEntry(order, entry); var orderLevelDiscount = _currency.Round(item.ExtendedPrice * percentageOffTotal); AddDiscountToLineItem(order, record, entry, 0, orderLevelDiscount); } } } // Save discounts if (record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Order).Key, StringComparison.OrdinalIgnoreCase) || record.PromotionReward is GiftPromotionReward) { if (record.PromotionReward.RewardType == PromotionRewardType.WholeOrder && discountAmount > 0) { OrderFormDiscount discount = FindOrderFormDiscountById(order, record.PromotionItem.DataRow.PromotionId, Int32.Parse(record.AffectedEntriesSet.OrderFormId)); bool hasOrderFormDiscount = true; if (discount == null) { discount = new OrderFormDiscount(); hasOrderFormDiscount = false; } var discountName = record.PromotionItem.DataRow.Name; if (record.PromotionReward is GiftPromotionReward) { discountName = GetGiftPromotionName(record); } discount.DiscountName = discountName; discount.DiscountAmount = record.PromotionReward.AmountOff; discount.DiscountCode = record.PromotionItem.DataRow.CouponCode; discount.DiscountValue = hasOrderFormDiscount ? discountAmount + discount.DiscountValue : discountAmount; discount.DisplayMessage = GetDisplayName(record.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); discount.OrderFormId = Int32.Parse(record.AffectedEntriesSet.OrderFormId); discount.DiscountId = record.PromotionItem.DataRow.PromotionId; foreach (OrderForm form in order.OrderForms) { if (form.OrderFormId == discount.OrderFormId && !hasOrderFormDiscount) { form.Discounts.Add(discount); } } } } else if (record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Shipping).Key, StringComparison.OrdinalIgnoreCase)) { ShipmentDiscount discount = FindShipmentDiscountById(order, record.PromotionItem.DataRow.PromotionId, Int32.Parse(record.AffectedEntriesSet.ShipmentId)); if (discount == null) { discount = new ShipmentDiscount(); } discount.DiscountAmount = record.PromotionReward.AmountOff; discount.DiscountCode = record.PromotionItem.DataRow.CouponCode; discount.DiscountName = record.PromotionItem.DataRow.Name; discount.DisplayMessage = GetDisplayName(record.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); discount.ShipmentId = Int32.Parse(record.AffectedEntriesSet.ShipmentId); discount.DiscountId = record.PromotionItem.DataRow.PromotionId; var shipment = order.OrderForms.SelectMany(o => o.Shipments).FirstOrDefault(s => s.ShipmentId == discount.ShipmentId); if (shipment != null) { shipment.Discounts.Add(discount); if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = _currency.Percentage(shipment.ShippingSubTotal, record.PromotionReward.AmountOff); } else { // PromotionReward.AmountOff was calculated in Promotion context, it's not be rounded. discountAmount = _currency.Round(Math.Min(record.PromotionReward.AmountOff, shipment.ShippingSubTotal)); } shipment.ShippingDiscountAmount = Math.Min(shipment.ShippingDiscountAmount + discountAmount, shipment.ShippingSubTotal); // ShippingDiscountAmount will not be subtracted from the ShipmentTotal per discussions on 2/22/2012. } discount.DiscountValue = discountAmount; } return(discountAmount); }
/// <summary> /// Gets the name of the gift promotion. /// </summary> /// <param name="promoRecord">The promo record.</param> /// <returns></returns> public string GetGiftPromotionName(PromotionItemRecord promoRecord) { return("@" + promoRecord.PromotionItem.DataRow.Name + ":Gift"); }
/// <summary> /// Adds the promotion item record. /// </summary> /// <param name="record">The record.</param> public void AddPromotionItemRecord(PromotionItemRecord record) { PromotionResult.PromotionRecords.Add(record); }
/// <summary> /// Evaluates the promotions. /// </summary> /// <param name="context">The context.</param> /// <param name="promotions">The promotions.</param> /// <param name="filter">The filter.</param> /// <returns>Collection of promotions that were applied. Look inside PromotionContext for actual rewards and for items /// that have been affected by these promotions.</returns> public PromotionItemCollection EvaluatePromotions(PromotionContext context, PromotionItemCollection promotions, PromotionFilter filter) { // Start checking discounts List <int> rowIndexes = new List <int>(); // Retrieve all the coupons customer entered List <string> coupons = context.Coupons; // Retrieve customer segments, it should be initialized beforehand List <int> segments = context.Segments; // Retrieve customer id, it should be initialized beforehand Guid customerId = context.CustomerId; foreach (PromotionItem item in promotions) { // Set currently executed promotion context.CurrentPromotion = item; // If discount is global and other discount has been applied already, skip it if (item.DataRow.ExclusivityType.Equals(ExclusionType.GlobalLevel) && rowIndexes.Count != 0) { continue; } // Check if it belongs to a group specified if (!String.IsNullOrEmpty(context.TargetGroup) && !context.TargetGroup.Equals(item.DataRow.PromotionGroup, StringComparison.OrdinalIgnoreCase)) { continue; } // Check group exclusivity if (context.ExclusiveGroups.Contains(item.DataRow.PromotionGroup)) { continue; } // Check limits here int currentOrderPromotionCount = context.PromotionResult.GetCount(item.DataRow.PromotionId); // Start with per order limit if (item.DataRow.PerOrderLimit > 0 && currentOrderPromotionCount >= item.DataRow.PerOrderLimit) { continue; } // Check application limit limit, only check when limit is set if (item.DataRow.ApplicationLimit > 0 && item.TotalUsedCount - context.ReservedCount + currentOrderPromotionCount >= item.DataRow.ApplicationLimit) { continue; } // Check customer limit, only check when limit is set and customer id is set if ((item.DataRow.CustomerLimit > 0 && customerId == Guid.Empty) || item.DataRow.CustomerLimit > 0 && item.GetCustomerUsageCount(customerId) - context.ReservedCount + currentOrderPromotionCount >= item.DataRow.CustomerLimit) { continue; } // First do simply checks that will take little time and save us from wasting processor time // ----------------------------------------------------------------------------------------- if (!filter.IncludeInactive) { // Skip if start date is past now if (!item.Campaign.IsActive) { continue; } if (item.Campaign.StartDate > FrameworkContext.Current.CurrentDateTime) { continue; } // Skip if end date is in the past if (item.Campaign.EndDate < FrameworkContext.Current.CurrentDateTime) { continue; } // Skip if start date is past now if (item.DataRow.StartDate > FrameworkContext.Current.CurrentDateTime) { continue; } // Skip if end date is in the past if (item.DataRow.EndDate < FrameworkContext.Current.CurrentDateTime) { continue; } // Check promotion status if (!item.DataRow.Status.Equals(PromotionStatus.Active)) { continue; } } if (!filter.IncludeCoupons) { // Check coupons string couponCode = item.DataRow.CouponCode; if (!String.IsNullOrEmpty(couponCode)) { if (coupons == null) { continue; } bool foundCoupon = false; foreach (string coupon in coupons) { if (couponCode.Equals(coupon, StringComparison.OrdinalIgnoreCase)) { foundCoupon = true; } } if (!foundCoupon) { continue; } } } // Check catalog / node / entry filtering if (context.TargetEntriesSet.Entries.Count > 0) { bool isValid = true; foreach (PromotionEntry entry in context.TargetEntriesSet.Entries) { string catalogEntryId = entry.CatalogEntryCode; string catalogNodeId = entry.CatalogNodeCode; string catalogName = entry.CatalogName; if (!String.IsNullOrEmpty(catalogEntryId) || !String.IsNullOrEmpty(catalogNodeId) || !String.IsNullOrEmpty(catalogName)) { PromotionDto.PromotionConditionRow[] conditions = item.DataRow.GetPromotionConditionRows(); if (conditions != null && conditions.Length > 0) { foreach (PromotionDto.PromotionConditionRow condition in conditions) { if (!String.IsNullOrEmpty(catalogEntryId) && !condition.IsCatalogEntryIdNull() && !condition.CatalogEntryId.Equals(catalogEntryId)) { isValid = false; break; } else if (!String.IsNullOrEmpty(catalogNodeId) && !condition.IsCatalogNodeIdNull() && !condition.CatalogNodeId.Equals(catalogNodeId)) { isValid = false; break; } else if (!String.IsNullOrEmpty(catalogName) && !condition.IsCatalogNameNull() && !condition.CatalogName.Equals(catalogName)) { isValid = false; break; } } if (!isValid) { continue; } } } } if (!isValid) { continue; } } // Start doing more expensive checks here // ----------------------------------------------------------------------------------------- if (!filter.IgnoreSegments) { // Check customer segments, customer should belong to a segment for promotion to apply CampaignDto.CampaignSegmentRow[] segmentRows = item.Campaign.GetCampaignSegmentRows(); // if there are no segments defined assume it applies to everyone if (segmentRows != null && segmentRows.Length > 0) { // customer is not within any segment, so promotion does not apply if (segments == null || segments.Count == 0) { continue; } // start checking segments bool apply = false; foreach (CampaignDto.CampaignSegmentRow row in segmentRows) { if (segments.Contains(row.SegmentId)) { // mark promotion as apply and leave loop apply = true; break; } } // if does not apply continue with a next promotion if (!apply) { continue; } } } if (!filter.IgnoreConditions) { // Validate expressions if (item.Expressions.Count > 0) { bool isValid = true; foreach (ExpressionDto.ExpressionRow expression in item.Expressions) { if (!String.IsNullOrEmpty(expression.ExpressionXml)) { ValidationResult result = ValidateExpression(expression.ApplicationId.ToString() + "-" + expression.Category + "-" + expression.ExpressionId.ToString(), expression.ExpressionXml, context); if (!result.IsValid) { isValid = false; break; } } } if (!isValid) { continue; } } } else { // Create Award manually based on default settings PromotionReward reward = new PromotionReward(PromotionRewardType.EachAffectedEntry, item.DataRow.OfferAmount, item.DataRow.OfferType == 0 ? PromotionRewardAmountType.Percentage : PromotionRewardAmountType.Value); PromotionItemRecord record = new PromotionItemRecord(context.TargetEntriesSet, context.TargetEntriesSet, reward); record.PromotionItem = context.CurrentPromotion; context.PromotionResult.PromotionRecords.Add(record); } if (!filter.IgnorePolicy) { // Validate store policies if (item.PolicyExpressions.Count > 0) { bool isValid = true; foreach (ExpressionDto.ExpressionRow expression in item.PolicyExpressions) { if (!String.IsNullOrEmpty(expression.ExpressionXml)) { ValidationResult result = ValidateExpression(expression.ApplicationId.ToString() + "-" + expression.Category + "-" + expression.ExpressionId.ToString(), expression.ExpressionXml, context); if (!result.IsValid) { isValid = false; break; } } } if (!isValid) { // Invalidates all records added during this evaluation sequence context.RejectRecords(); continue; } } } // Commits all records added during this evaluation sequence context.CommitRecords(); // Apply item rowIndexes.Add(item.RowIndex); // Add item to a group if it is applied if (item.DataRow.ExclusivityType.Equals(ExclusionType.GroupLevel)) { context.ExclusiveGroups.Add(item.DataRow.PromotionGroup); } // Finish processing if global level item has been added if (item.DataRow.ExclusivityType.Equals(ExclusionType.GlobalLevel)) { break; } } // Assign curren promotion to null context.CurrentPromotion = null; return(new PromotionItemCollection(promotions, rowIndexes.ToArray())); }
/// <summary> /// Gets the name of the gift promotion. /// </summary> /// <param name="promoRecord">The promo record.</param> /// <returns></returns> public string GetGiftPromotionName(PromotionItemRecord promoRecord) { return "@" + promoRecord.PromotionItem.DataRow.Name + ":Gift"; }
private decimal GetDiscountAmount(PromotionItemRecord record, PromotionReward reward) { decimal discountAmount = 0; if (reward.RewardType != PromotionRewardType.EachAffectedEntry && reward.RewardType != PromotionRewardType.AllAffectedEntries) { return Math.Round(discountAmount, 2); } if (reward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = record.AffectedEntriesSet.TotalCost * reward.AmountOff / 100; } else { discountAmount += reward.AmountOff; } return Math.Round(discountAmount, 2); }
/// <summary> /// Applies the item discount. /// </summary> /// <param name="order">The order.</param> /// <param name="record">The record.</param> /// <param name="totalAmount">The total amount.</param> /// <returns></returns> private decimal ApplyItemDiscount(OrderGroup order, PromotionItemRecord record, decimal totalAmount) { decimal discountAmount = 0; if (record.PromotionReward.RewardType == PromotionRewardType.AllAffectedEntries) { if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = record.AffectedEntriesSet.TotalCost * record.PromotionReward.AmountOff / 100; decimal averageDiscountAmount = discountAmount / record.AffectedEntriesSet.TotalQuantity; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { // Sasha: changed back, CostPerEntry does not change dynamically, while total cost does // AddDiscountToLineItem(order, record, entry, entry.CostPerEntry * entry.Quantity * record.PromotionReward.AmountOff / 100m, 0); // AddDiscountToLineItem(order, record, entry.CatalogEntryCode, averageDiscountAmount * entry.Quantity, 0); AddDiscountToLineItem(order, record, entry, averageDiscountAmount * entry.Quantity, 0); } } else // need to split discount between all items { discountAmount = record.PromotionReward.AmountOff; decimal averageDiscountAmount = record.PromotionReward.AmountOff / record.AffectedEntriesSet.TotalQuantity; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, averageDiscountAmount * entry.Quantity, 0); } } } else if (record.PromotionReward.RewardType == PromotionRewardType.EachAffectedEntry) { if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = record.AffectedEntriesSet.TotalCost * record.PromotionReward.AmountOff / 100; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, entry.CostPerEntry * entry.Quantity * record.PromotionReward.AmountOff / 100, 0); } } else { discountAmount = record.AffectedEntriesSet.TotalQuantity * record.PromotionReward.AmountOff; foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, record.PromotionReward.AmountOff * entry.Quantity, 0); } } } else if (record.PromotionReward.RewardType == PromotionRewardType.WholeOrder) { decimal percentageOffTotal = 0; if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { // calculate percentage adjusted by the running amount, so it will be a little less if running amount is less than total percentageOffTotal = (record.PromotionReward.AmountOff / 100) * (totalAmount / record.AffectedEntriesSet.TotalCost); //percentageOffTotal = PromotionReward.AmountOff / 100; discountAmount = totalAmount * record.PromotionReward.AmountOff / 100; } else { // Calculate percentage off discount price percentageOffTotal = record.PromotionReward.AmountOff / totalAmount; // but since CostPerEntry is not an adjusted price, we need to take into account additional discounts already applied percentageOffTotal = percentageOffTotal * (totalAmount / record.AffectedEntriesSet.TotalCost); discountAmount = record.PromotionReward.AmountOff; } // Now distribute discount amount evenly over all entries taking into account running total // Special case for shipments, we consider WholeOrder to be a shipment if (!record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Shipping).Key, StringComparison.OrdinalIgnoreCase)) { foreach (PromotionEntry entry in record.AffectedEntriesSet.Entries) { AddDiscountToLineItem(order, record, entry, 0, (((entry.CostPerEntry * entry.Quantity) /* - entry.Discount*/)) * percentageOffTotal); } } } // Save discounts if (record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Order).Key, StringComparison.OrdinalIgnoreCase) || record.PromotionReward is GiftPromotionReward) { if (record.PromotionReward.RewardType == PromotionRewardType.WholeOrder) { OrderFormDiscount discount = FindOrderFormDiscountById(order, record.PromotionItem.DataRow.PromotionId, Int32.Parse(record.AffectedEntriesSet.OrderFormId)); bool hasOrderFormDiscount = true; if (discount == null) { discount = new OrderFormDiscount(); hasOrderFormDiscount = false; } var discountName = record.PromotionItem.DataRow.Name; if (record.PromotionReward is GiftPromotionReward) { discountName = GetGiftPromotionName(record); } discount.DiscountName = discountName; discount.DiscountAmount = record.PromotionReward.AmountOff; discount.DiscountCode = record.PromotionItem.DataRow.CouponCode; discount.DiscountValue = hasOrderFormDiscount ? discountAmount + discount.DiscountValue : discountAmount; discount.DisplayMessage = GetDisplayName(record.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); discount.OrderFormId = Int32.Parse(record.AffectedEntriesSet.OrderFormId); discount.DiscountId = record.PromotionItem.DataRow.PromotionId; foreach (OrderForm form in order.OrderForms) { if (form.OrderFormId == discount.OrderFormId && !hasOrderFormDiscount) { form.Discounts.Add(discount); } } } } else if (record.PromotionItem.DataRow.PromotionGroup.Equals(PromotionGroup.GetPromotionGroup(PromotionGroup.PromotionGroupKey.Shipping).Key, StringComparison.OrdinalIgnoreCase)) { ShipmentDiscount discount = FindShipmentDiscountById(order, record.PromotionItem.DataRow.PromotionId, Int32.Parse(record.AffectedEntriesSet.ShipmentId)); if (discount == null) { discount = new ShipmentDiscount(); } discount.DiscountAmount = record.PromotionReward.AmountOff; discount.DiscountCode = record.PromotionItem.DataRow.CouponCode; discount.DiscountName = record.PromotionItem.DataRow.Name; discount.DisplayMessage = GetDisplayName(record.PromotionItem.DataRow, Thread.CurrentThread.CurrentCulture.Name); discount.ShipmentId = Int32.Parse(record.AffectedEntriesSet.ShipmentId); discount.DiscountId = record.PromotionItem.DataRow.PromotionId; foreach (OrderForm form in order.OrderForms) { foreach (Shipment shipment in form.Shipments) { if (shipment.ShipmentId == discount.ShipmentId) { shipment.Discounts.Add(discount); if (record.PromotionReward.AmountType == PromotionRewardAmountType.Percentage) { discountAmount = shipment.ShipmentTotal * record.PromotionReward.AmountOff / 100; } else { discountAmount = Math.Min(record.PromotionReward.AmountOff, shipment.ShipmentTotal); } shipment.ShippingDiscountAmount += discountAmount; // ShippingDiscountAmount will not be subtracted from the ShipmentTotal per discussions on 2/22/2012. break; } } } discount.DiscountValue = discountAmount; } return(discountAmount); }