/// <summary> /// Given a transaction and some discount processing info, activate the given offers /// to the transaction if applicable /// </summary> /// <param name="retailTransaction">Transaction to process</param> /// <param name="concurrencyMode">For this concurrency mode, try to activate the given offers</param> /// <param name="offers">List of offers to attempt to activate</param> /// <param name="appliedOffers">Set of offers which have already been activated</param> /// <param name="rejectedOffers">Set of offers which have already been rejected</param> private static void ApplyPeriodicDiscounts(RetailTransaction retailTransaction, ConcurrencyMode concurrencyMode, List <OfferGroup> offers, HashSet <OfferGroup> appliedOffers, HashSet <OfferGroup> rejectedOffers) { foreach (var salesItem in retailTransaction.SaleItems) { if (salesItem.PeriodicDiscountPossibilities.Count == 0) { continue; } bool wasDiscountApplied = false; // if there are any discounts of the given concurrency mode if (salesItem.PeriodicDiscountPossibilities.Any(d => d.ConcurrencyMode == concurrencyMode)) { // apply the discount and return it if applied PeriodicDiscountItem registeredDiscount = ApplyValidDiscountFromOffers(salesItem, offers, appliedOffers, rejectedOffers); wasDiscountApplied = (registeredDiscount != null); // for exclusive and best price computations, if discount was put on item, mark the rest of possibilities as rejected if (concurrencyMode != ConcurrencyMode.Compounded && registeredDiscount != null) { foreach (var discount in salesItem.PeriodicDiscountPossibilities) { if (discount != registeredDiscount) { rejectedOffers.Add(GetOfferGroup(discount)); } } } } // If processing compound discounts, add all non-rejected, non-compoundable, non-applied ones if (concurrencyMode == ConcurrencyMode.Compounded) { foreach (PeriodicDiscountItem discount in salesItem.PeriodicDiscountPossibilities.Where(d => d.IsCompoundable && d.ConcurrencyMode == ConcurrencyMode.Compounded)) { OfferGroup offerGroup = GetOfferGroup(discount); if (!rejectedOffers.Contains(offerGroup) && !appliedOffers.Contains(offerGroup) && !salesItem.DiscountLines.Contains(discount)) { salesItem.DiscountLines.AddLast(discount); wasDiscountApplied = true; NetTracer.Information("DiscountConcurrencyRules::ApplyPeriodicDiscounts: Applying periodic discount {0} for item {1} line {2}", discount.OfferId, salesItem.ItemId, salesItem.LineId); } } } // Set whole quantity of the row as discounted because final discount of this item has now been set if (wasDiscountApplied) { salesItem.QuantityDiscounted = salesItem.Quantity; } } }
/// <summary> /// Given sales item, and possible offers sorted by priority (biggest discount), /// activate best, valid discount on the item /// </summary> /// <param name="saleItem">Item to process</param> /// <param name="offers">Sorted, possible discount offers</param> /// <param name="appliedOffers">Set of offers already applied</param> /// <param name="rejectedOffers">Set of offer already rejected</param> /// <returns>Discount which was activated or null if none were</returns> private static PeriodicDiscountItem ApplyValidDiscountFromOffers(SaleLineItem saleItem, List <OfferGroup> offers, HashSet <OfferGroup> appliedOffers, HashSet <OfferGroup> rejectedOffers) { PeriodicDiscountItem selectedPeriodicDiscount = null; var offersToPickFrom = offers; // If some of this item's discounts have already been applied, they get priority bool someOfferAlreadyApplied = saleItem.PeriodicDiscountPossibilities.Any(d => appliedOffers.Contains(GetOfferGroup(d))); offersToPickFrom = someOfferAlreadyApplied ? offersToPickFrom.Where(o => appliedOffers.Contains(o)).ToList() : offersToPickFrom; // If some of this item's discounts have already been rejected, they shouldn't be an option to choose from bool someOfferAlreadyRejected = saleItem.PeriodicDiscountPossibilities.Any(d => rejectedOffers.Contains(GetOfferGroup(d))); offersToPickFrom = someOfferAlreadyRejected ? offersToPickFrom.Where(o => !rejectedOffers.Contains(o)).ToList() : offersToPickFrom; if (offersToPickFrom.Count > 0) { // Since offersToPickFrom is sorted by discount amount and filtered for availability, // we need to find the first offer from the available offers which is also a possible item discount. selectedPeriodicDiscount = GetFirstPeriodicDiscountFromOffers(saleItem.PeriodicDiscountPossibilities, offersToPickFrom); if (selectedPeriodicDiscount != null && !saleItem.DiscountLines.Contains(selectedPeriodicDiscount)) { // Apply this discount to the item saleItem.DiscountLines.AddLast(selectedPeriodicDiscount); // Mark that we've applied this offer appliedOffers.Add(GetOfferGroup(selectedPeriodicDiscount)); NetTracer.Information("DiscountConcurrencyRules::ApplyValidDiscountFromOffers: Applying periodic discount {0} for item {1} line {2}", selectedPeriodicDiscount.OfferId, saleItem.ItemId, saleItem.LineId); } // if not applied, make sure we return null else { selectedPeriodicDiscount = null; } } return(selectedPeriodicDiscount); }
/// <summary> /// Gets the OfferGroup which is associated to this discount item /// </summary> /// <param name="discountItem"></param> /// <returns></returns> private static OfferGroup GetOfferGroup(PeriodicDiscountItem discountItem) { return(new OfferGroup { OfferId = discountItem.OfferId, DiscountGroup = discountItem.PeriodicDiscGroupId }); }