/// <summary>
        /// Applies a discount to an order by reducing product prices.
        /// </summary>
        /// <param name="products">The order on which to apply a discount.</param>
        /// <param name="discount">The amount to deduct from the products.</param>
        /// <returns>Returns the amount of unused discount.</returns>
        public static decimal ApplyDiscount(this IList <ProcessOrderProduct> products, decimal discount)
        {
            products.CheckNotNull(nameof(products));
            discount.CheckRange(nameof(discount), min: 0);

            var edited = false;

            while (discount > 0 && GetProductWithPrice(out var product))
            {
                if (product.Quantity > 1)
                {
                    // Split product so we edit the price of only 1 item.
                    product.Quantity--;
                    product = new ProcessOrderProduct(product.Name, product.Price);
                    products.Add(product);
                }

                var reduce = Math.Min(discount, product.Price);
                product.Price -= reduce;
                discount      -= reduce;
                edited         = true;
            }
            if (edited)
            {
                GroupDuplicates(products);
            }
            return(discount);

            bool GetProductWithPrice(out ProcessOrderProduct product)
            {
                product = products.FirstOrDefault(x => x.Price > 0);
                return(product != null);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Returns the list of products to settle due payments.
        /// </summary>
        /// <param name="contact">The contact to create the bill for.</param>
        /// <param name="planList">The list of due payment plans.</param>
        /// <param name="currentDate">The current date, ensuring all calculations are done using the exact same date.</param>
        /// <param name="deduct">An amount to deduct from the order, such as account credits.</param>
        /// <returns>The list of products to pay.</returns>
        public IList <ProcessOrderProduct> GetBillProducts(ApiCustomContact contact, IEnumerable <ApiPaymentPlan> planList, DateTimeOffset currentDate)
        {
            contact.CheckNotNull(nameof(contact));
            planList.CheckNotNull(nameof(planList));

            var products = new List <ProcessOrderProduct>();

            // Don't bill trials until something has been paid.
            foreach (var plan in planList.Where(x => contact.HasPaidHealingTechnologies == true || x.PeriodUnit != PaymentPeriodUnit.MonthPostpaidTrial))
            {
                //var isTrialExpired = ((currentDate - (plan.StartDate ?? plan.DateAdded!.Value)).TotalDays > (plan.PeriodQty ?? _config.Value.DefaultTrialDays))
                var qty = CalcPeriodQty(plan, currentDate);

                if (qty > 0)
                {
                    // Apply TransactionsLeft.
                    //var maxQty = plan.TransactionsLeft ?? (plan.PeriodUnit == PaymentPeriodUnit.MonthPostpaid || plan.PeriodUnit == PaymentPeriodUnit.MonthPostpaidTrial ? (int?)null : 1);
                    //if (maxQty != null && qty > maxQty)
                    //{
                    //    qty = maxQty.Value;
                    //}

                    // Add product to cart.
                    var product = new ProcessOrderProduct(plan.ProductName ?? string.Empty);
                    if ((qty % 1) == 0)
                    {
                        product.Quantity = (int)qty;
                        product.Price    = plan.PricePerPeriod ?? 0;
                    }
                    else
                    {
                        product.Quantity = 1;
                        product.Price    = Math.Round((plan.PricePerPeriod ?? 0) * qty, 2);
                    }
                    products.Add(product);
                }
            }

            return(products.GroupDuplicates());
        }