/// <summary>
            /// Updates the sales transaction with the quantity promotion if applicable.
            /// </summary>
            /// <param name="existingTransaction">Existing transaction.</param>
            /// <param name="tempSalesTransaction">Copy of existing transaction.</param>
            /// <param name="context">The request context.</param>
            /// <param name="salesLineIndex">The sales line under consideration.</param>
            /// <param name="discountLine">The quantity discount under consideration.</param>
            /// <param name="multiBuyDiscountLines">The multi buy discount lines.</param>
            private static void GetQuantityPromotions(SalesTransaction existingTransaction, SalesTransaction tempSalesTransaction, RequestContext context, int salesLineIndex, DiscountLine discountLine, IEnumerable <QuantityDiscountLevel> multiBuyDiscountLines)
            {
                // Get the multi buy discount lines for the current multi buy discount.
                IEnumerable <QuantityDiscountLevel> multiBuyLinesForCurrentOffer = multiBuyDiscountLines.Where(j => j.OfferId.Equals(discountLine.OfferId)).OrderBy(l => l.MinimumQuantity);

                List <SalesLine> salesLinesWithSameProduct = tempSalesTransaction.SalesLines.Where(j => j.ItemId == tempSalesTransaction.SalesLines[salesLineIndex].ItemId && j.InventoryDimensionId == tempSalesTransaction.SalesLines[salesLineIndex].InventoryDimensionId).ToList();
                decimal          totalQuantity             = salesLinesWithSameProduct.Select(j => j.Quantity).Sum();
                decimal          currentQuantity           = tempSalesTransaction.SalesLines[salesLineIndex].Quantity;

                salesLinesWithSameProduct.Remove(tempSalesTransaction.SalesLines[salesLineIndex]);
                bool neverApplied = true;

                foreach (QuantityDiscountLevel multiBuyLine in multiBuyLinesForCurrentOffer)
                {
                    // removing the quantity discounts that were not applied (because of concurrency rules).
                    if (multiBuyLine.MinimumQuantity <= totalQuantity)
                    {
                        continue;
                    }

                    // Temporarily update the current transaction with the new quantity to see if the quantity discount will be applied.
                    existingTransaction.SalesLines[salesLineIndex].Quantity = multiBuyLine.MinimumQuantity - totalQuantity + currentQuantity;

                    CartWorkflowHelper.Calculate(context, existingTransaction, CalculationModes.All);
                    DiscountLine isApplied = existingTransaction.SalesLines[salesLineIndex].DiscountLines.Where(j => j.OfferId == discountLine.OfferId).SingleOrDefault();

                    // If the quantity discount will be applied then remove the discount line from the lines with same product and get the min quantity to buy for discount.
                    if (isApplied != null && (isApplied.Amount != 0 || isApplied.Percentage != 0))
                    {
                        int toBuy = (int)(multiBuyLine.MinimumQuantity - totalQuantity);
                        if (isApplied.Amount != 0)
                        {
                            discountLine.OfferName = string.Format(CultureInfo.CurrentUICulture, Resources.MultiBuyDiscountPricePromotion, toBuy, Math.Round(isApplied.Amount, 2));
                        }
                        else
                        {
                            discountLine.OfferName = string.Format(CultureInfo.CurrentUICulture, Resources.MultiBuyDiscountPercentagePromotion, toBuy, Math.Round(isApplied.Percentage, 2));
                        }

                        neverApplied = false;
                        break;
                    }
                }

                if (neverApplied)
                {
                    tempSalesTransaction.SalesLines[salesLineIndex].DiscountLines.Remove(discountLine);
                }

                existingTransaction.SalesLines[salesLineIndex].Quantity = currentQuantity;
                CartWorkflowHelper.Calculate(context, existingTransaction, CalculationModes.All);

                foreach (SalesLine sameproductCartLine in salesLinesWithSameProduct)
                {
                    sameproductCartLine.DiscountLines.Remove(sameproductCartLine.DiscountLines.Where(k => k.OfferId == discountLine.OfferId).SingleOrDefault());
                }
            }
            /// <summary>
            /// Recalculates the sales transaction.
            /// </summary>
            /// <param name="context">The context.</param>
            /// <param name="request">The request.</param>
            /// <param name="transaction">Current transaction.</param>
            private static void RecalculateSalesTransaction(RequestContext context, SaveCartLinesRequest request, SalesTransaction transaction)
            {
                // Sets the wharehouse id and invent location id for each line
                ItemAvailabilityHelper.SetSalesLineInventory(context, transaction);

                // Calculate totals and saves the sales transaction
                CartWorkflowHelper.Calculate(context, transaction, request.CalculationModes);

                // Calculate the required reason codes after the price calculation
                ReasonCodesWorkflowHelper.CalculateRequiredReasonCodes(context, transaction, ReasonCodeSourceType.None);
            }
            /// <summary>
            /// Returns Sales Order by sales id and terminal id. Used to get remote orders from AX which does not have transaction id.
            /// </summary>
            /// <param name="salesId">The sales id parameter.</param>
            /// <returns>The  SalesOrder.</returns>
            private SalesOrder GetTransactionBySalesId(string salesId)
            {
                // Recall the customer order
                var realtimeRequest = new RecallCustomerOrderRealtimeRequest(salesId, isQuote: false);

                var        serviceResponse = this.Context.Execute <RecallCustomerOrderRealtimeResponse>(realtimeRequest);
                SalesOrder salesOrder      = serviceResponse.SalesOrder;

                // Channel and terminal don't come from ax
                salesOrder.ChannelId  = this.Context.GetPrincipal().ChannelId;
                salesOrder.TerminalId = this.Context.GetTerminal().TerminalId;

                // Perform order calculations (deposit, amount due, etc)
                CartWorkflowHelper.Calculate(this.Context, salesOrder, requestedMode: null);

                return(salesOrder);
            }
Beispiel #4
0
            /// <summary>
            /// Executes the workflow to resume suspended cart.
            /// </summary>
            /// <param name="request">Instance of <see cref="ResumeCartRequest"/>.</param>
            /// <returns>Instance of <see cref="ResumeCartResponse"/>.</returns>
            protected override ResumeCartResponse Process(ResumeCartRequest request)
            {
                ThrowIf.Null(request, "request");

                var getSalesTransactionServiceRequest = new GetSalesTransactionsServiceRequest(
                    new CartSearchCriteria {
                    CartId = request.CartId
                },
                    QueryResultSettings.SingleRecord,
                    mustRemoveUnavailableProductLines: true);
                var getSalesTransactionServiceResponse = this.Context.Execute <GetSalesTransactionsServiceResponse>(getSalesTransactionServiceRequest);
                SalesTransaction transaction           = null;

                if (getSalesTransactionServiceResponse.SalesTransactions != null)
                {
                    transaction = getSalesTransactionServiceResponse.SalesTransactions.FirstOrDefault();
                }

                if (!transaction.IsSuspended)
                {
                    throw new CartValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidStatus, request.CartId, "Cart is not suspended.");
                }

                // Resume the suspended transaction to normal state.
                transaction.EntryStatus   = TransactionStatus.Normal;
                transaction.IsSuspended   = false;
                transaction.TerminalId    = this.Context.GetTerminal().TerminalId;
                transaction.BeginDateTime = this.Context.GetNowInChannelTimeZone();
                CartWorkflowHelper.Calculate(this.Context, transaction, null);
                CartWorkflowHelper.SaveSalesTransaction(this.Context, transaction);

                if (getSalesTransactionServiceResponse.LinesWithUnavailableProducts.Any())
                {
                    // Send notification to decide if caller should be notified about discontinued products.
                    var notification = new ProductDiscontinuedFromChannelNotification(getSalesTransactionServiceResponse.LinesWithUnavailableProducts);
                    this.Context.Notify(notification);
                }

                Cart cart = CartWorkflowHelper.ConvertToCart(this.Context, transaction);

                CartWorkflowHelper.RemoveHistoricalTenderLines(cart);

                return(new ResumeCartResponse(cart));
            }
Beispiel #5
0
            /// <summary>
            /// Executes the workflow to recalculate a sales transaction and return a cart representing the transaction.
            /// </summary>
            /// <param name="request">Instance of <see cref="RecalculateOrderRequest"/>.</param>
            /// <returns>Instance of <see cref="RecalculateOrderResponse"/>.</returns>
            protected override RecalculateOrderResponse Process(RecalculateOrderRequest request)
            {
                ThrowIf.Null(request, "request");

                // Recovers transaction from database
                SalesTransaction salesTransaction = CartWorkflowHelper.LoadSalesTransaction(this.Context, request.CartId);

                if (salesTransaction == null)
                {
                    throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_CartNotFound, "Cart does not exist.");
                }

                // Check permissions.
                RetailOperation operation = salesTransaction.CartType == CartType.CustomerOrder ? RetailOperation.RecalculateCustomerOrder : RetailOperation.CalculateFullDiscounts;

                request.RequestContext.Execute <NullResponse>(new CheckAccessServiceRequest(operation));

                // When recalcalculating order, unlock prices so new prices and discounts are applied to the entire order.
                foreach (SalesLine salesLine in salesTransaction.SalesLines)
                {
                    salesLine.IsPriceLocked = false;
                }

                // Recalculate transaction
                CartWorkflowHelper.Calculate(this.Context, salesTransaction, requestedMode: null, discountCalculationMode: DiscountCalculationMode.CalculateAll);

                // Update order on database
                CartWorkflowHelper.SaveSalesTransaction(this.Context, salesTransaction);

                // Convert the SalesOrder into a cart object for the client
                Cart cart = CartWorkflowHelper.ConvertToCart(this.Context, salesTransaction);

                CartWorkflowHelper.RemoveHistoricalTenderLines(cart);

                // Return cart
                return(new RecalculateOrderResponse(cart));
            }
            /// <summary>
            /// Execute method to be overridden by each derived class.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>
            /// The response.
            /// </returns>
            protected override UpdateDeliverySpecificationsResponse Process(UpdateDeliverySpecificationsRequest request)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.NullOrWhiteSpace(request.CartId, "request.CartId");

                SalesTransaction transaction = CartWorkflowHelper.LoadSalesTransaction(this.Context, request.CartId);

                if (transaction == null)
                {
                    string message = string.Format("Cart with identifer {0} was not found.", request.CartId);
                    throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound, message);
                }

                if (request.UpdateOrderLevelDeliveryOptions)
                {
                    transaction = this.UpdateOrderLevelDeliverySpecification(transaction, request.DeliverySpecification);
                }
                else
                {
                    transaction = this.UpdateLineLevelDeliveryOptions(transaction, request.LineDeliverySpecifications);
                }

                // Validate and resolve addresses.
                ShippingHelper.ValidateAndResolveAddresses(this.Context, transaction);

                // Updating the shipping information should only affect charges, taxes, amount due and totals.
                CartWorkflowHelper.Calculate(this.Context, transaction, CalculationModes.Charges | CalculationModes.Taxes | CalculationModes.AmountDue | CalculationModes.Totals);

                CartWorkflowHelper.SaveSalesTransaction(this.Context, transaction);

                Cart updatedCart = CartWorkflowHelper.ConvertToCart(this.Context, transaction);

                CartWorkflowHelper.RemoveHistoricalTenderLines(updatedCart);

                return(new UpdateDeliverySpecificationsResponse(updatedCart));
            }
            /// <summary>
            /// Gets the shopping cart specified by cart identifier and optionally calculates the totals on the cart.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns><see cref="GetCartResponse"/> object containing the shopping cart or a new one if the flag to create is set and no cart was found.</returns>
            protected override GetCartResponse Process(GetCartRequest request)
            {
                ThrowIf.Null(request, "request");

                var validateCustomerAccountRequest  = new GetValidatedCustomerAccountNumberServiceRequest(request.SearchCriteria.CustomerAccountNumber, throwOnValidationFailure: false);
                var validateCustomerAccountResponse = this.Context.Execute <GetValidatedCustomerAccountNumberServiceResponse>(validateCustomerAccountRequest);

                if (validateCustomerAccountResponse.IsCustomerAccountNumberInContextDifferent)
                {
                    request.SearchCriteria.CustomerAccountNumber = validateCustomerAccountResponse.ValidatedAccountNumber;
                    request.SearchCriteria.IncludeAnonymous      = true;
                }

                if (!request.SearchCriteria.SuspendedOnly && string.IsNullOrEmpty(request.SearchCriteria.CartId) && string.IsNullOrEmpty(request.SearchCriteria.CustomerAccountNumber))
                {
                    throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidRequest, "SearchCriteria requires one of the following fields to be set: SuspendedOnly, CartId, CustomerAccountNumber.");
                }

                if (!string.IsNullOrEmpty(request.SearchCriteria.CustomerAccountNumber) && string.IsNullOrWhiteSpace(request.SearchCriteria.CartId) && !request.SearchCriteria.SuspendedOnly)
                {
                    // If the only search criteria set is that of customer account number, then anonymous carts should not be fetched.
                    request.SearchCriteria.IncludeAnonymous = false;
                }

                // User query result settings provided by caller. If not available and cart identifier is set retrieve one row, otherwise do not apply paging.
                QueryResultSettings queryResultSettings = request.QueryResultSettings ?? (string.IsNullOrEmpty(request.SearchCriteria.CartId) ? QueryResultSettings.AllRecords : QueryResultSettings.SingleRecord);
                bool removeUnassortedProducts           = !request.SearchCriteria.SuspendedOnly;

                var getCartsServiceRequest  = new GetSalesTransactionsServiceRequest(request.SearchCriteria, queryResultSettings, removeUnassortedProducts);
                var getCartsServiceResponse = this.Context.Execute <GetSalesTransactionsServiceResponse>(getCartsServiceRequest);

                PagedResult <SalesTransaction>           salesTransactions            = getCartsServiceResponse.SalesTransactions;
                IDictionary <string, IList <SalesLine> > linesWithUnavailableProducts = getCartsServiceResponse.LinesWithUnavailableProducts;

                IEnumerable <SalesTransaction> transactionWithUnassortedProducts = salesTransactions.Results.Where(t => linesWithUnavailableProducts.Keys.Contains(t.Id));

                if (removeUnassortedProducts && linesWithUnavailableProducts.Any())
                {
                    foreach (SalesTransaction transaction in transactionWithUnassortedProducts)
                    {
                        // Recalculate totals (w/o unassorted products and save cart).
                        CartWorkflowHelper.Calculate(this.Context, transaction, requestedMode: null);
                        CartWorkflowHelper.SaveSalesTransaction(this.Context, transaction);
                    }

                    if (!request.IgnoreProductDiscontinuedNotification)
                    {
                        // Send notification to decide if caller should be notified about discontinued products.
                        var notification = new ProductDiscontinuedFromChannelNotification(linesWithUnavailableProducts);
                        this.Context.Notify(notification);
                    }

                    // Reload to cart to avoid version mismatch.
                    var reloadCartsServiceRequest = new GetSalesTransactionsServiceRequest(request.SearchCriteria, queryResultSettings, mustRemoveUnavailableProductLines: false);
                    salesTransactions = this.Context.Execute <GetSalesTransactionsServiceResponse>(reloadCartsServiceRequest).SalesTransactions;
                }

                PagedResult <Cart> carts = salesTransactions.ConvertTo(transaction => ConvertTransactionToCart(this.Context, transaction, request.IncludeHistoricalTenderLines));

                return(new GetCartResponse(carts, salesTransactions));
            }
            /// <summary>
            /// Saves (updating if it exists and creating a new one if it does not) the shopping cart on the request.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns><see cref="SaveCartResponse"/> object containing the cart with updated item quantities.</returns>
            protected override SaveCartResponse Process(SaveCartRequest request)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.Null(request.Cart, "request.Cart");

                var validateCustomerAccountRequest  = new GetValidatedCustomerAccountNumberServiceRequest(request.Cart.CustomerId, throwOnValidationFailure: false);
                var validateCustomerAccountResponse = this.Context.Execute <GetValidatedCustomerAccountNumberServiceResponse>(validateCustomerAccountRequest);

                if (validateCustomerAccountResponse.IsCustomerAccountNumberInContextDifferent)
                {
                    request.Cart.CustomerId = validateCustomerAccountResponse.ValidatedAccountNumber;
                }

                bool isItemSale = request.Cart.CartLines.Any(l => string.IsNullOrWhiteSpace(l.LineId) && !l.IsGiftCardLine && !l.IsVoided && l.Quantity >= 0m);

                if (string.IsNullOrWhiteSpace(request.Cart.Id))
                {
                    request.Cart.Id = CartWorkflowHelper.GenerateRandomTransactionId(this.Context);
                }

                // Copy the logic from CartService.CreateCart().
                foreach (CartLine line in request.Cart.CartLines)
                {
                    // Sets the IsReturn flag to true, when ReturnTransactionId is specified.
                    // The reason of doing so is that the IsReturn is not currently exposed on CartLine entity.
                    if (!string.IsNullOrEmpty(line.ReturnTransactionId))
                    {
                        line.LineData.IsReturnByReceipt = true;
                    }
                }

                // Get the Sales Transaction
                SalesTransaction salesTransaction = CartWorkflowHelper.LoadSalesTransaction(this.Context, request.Cart.Id);

                if (salesTransaction == null)
                {
                    // New transaction - set the default cart type to shopping if none
                    if (request.Cart.CartType == CartType.None)
                    {
                        request.Cart.CartType = CartType.Shopping;
                    }
                }

                CartWorkflowHelper.ValidateCartPermissions(salesTransaction, request.Cart, this.Context);

                if (salesTransaction == null)
                {
                    // New transaction - set the default cart type to shopping if none
                    if (request.Cart.CartType == CartType.None)
                    {
                        request.Cart.CartType = CartType.Shopping;
                    }

                    // Do not allow new transaction for blocked customer.
                    CartWorkflowHelper.ValidateCustomerAccount(this.Context, request.Cart, null);

                    // Set loyalty card from the customer number
                    CartWorkflowHelper.SetLoyaltyCardFromCustomer(this.Context, request.Cart);

                    // Set affiliations from the customer number
                    CartWorkflowHelper.AddOrUpdateAffiliationLinesFromCustomer(this.Context, null, request.Cart);

                    // If cannot find the transaction, create a new transaction.
                    salesTransaction = CartWorkflowHelper.CreateSalesTransaction(this.Context, request.Cart.Id, request.Cart.CustomerId);

                    // Set initial values on cart to be same as on transaction.
                    request.Cart.CopyPropertiesFrom(salesTransaction);

                    // Update transaction level reason code lines.
                    ReasonCodesWorkflowHelper.AddOrUpdateReasonCodeLinesOnTransaction(salesTransaction, request.Cart);

                    // Calculate required reason code lines for start of transaction.
                    ReasonCodesWorkflowHelper.CalculateRequiredReasonCodesOnTransaction(this.Context, salesTransaction, ReasonCodeSourceType.StartOfTransaction);
                }

                // If cart or the sales transaction is suspended then update is not permitted
                if (salesTransaction.IsSuspended)
                {
                    throw new CartValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_CartNotActive, request.Cart.Id);
                }

                // If the terminal id of the cart is not same as the context then it means that the cart is active on another terminal.
                GetCurrentTerminalIdDataRequest dataRequest = new GetCurrentTerminalIdDataRequest();

                if (!(salesTransaction.TerminalId ?? string.Empty).Equals(this.Context.Execute <SingleEntityDataServiceResponse <string> >(dataRequest).Entity ?? string.Empty, StringComparison.OrdinalIgnoreCase))
                {
                    throw new CartValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_LoadingActiveCartFromAnotherTerminalNotAllowed, request.Cart.Id);
                }

                // At this point, the sales transaction is either newly created with no sales lines or just loaded from DB.
                // We are yet to add new sales lines or update existing sales lines.
                // Get the returned sales transaction if the cart contains a return line
                SalesTransaction returnTransaction = CartWorkflowHelper.LoadSalesTransactionForReturn(this.Context, request.Cart, salesTransaction, request.OperationType);

                // If customer account number is not specified on the request it should not be overriden.
                if (request.Cart.CustomerId == null)
                {
                    request.Cart.CustomerId = salesTransaction.CustomerId;
                }

                // Get the products in the cart lines
                IDictionary <long, SimpleProduct> productsByRecordId = CartWorkflowHelper.GetProductsInCartLines(this.Context, request.Cart.CartLines);

                // Validate update cart request
                CartWorkflowHelper.ValidateUpdateCartRequest(this.Context, salesTransaction, returnTransaction, request.Cart, request.IsGiftCardOperation, productsByRecordId);

                request.Cart.IsReturnByReceipt = returnTransaction != null;
                request.Cart.ReturnTransactionHasLoyaltyPayment = returnTransaction != null && returnTransaction.HasLoyaltyPayment;

                if (returnTransaction != null &&
                    !string.IsNullOrWhiteSpace(returnTransaction.LoyaltyCardId) &&
                    string.IsNullOrWhiteSpace(salesTransaction.LoyaltyCardId))
                {
                    // Set the loyalty card of the returned transaction to the current transaction
                    request.Cart.LoyaltyCardId = returnTransaction.LoyaltyCardId;
                }

                HashSet <string> newSalesLineIdSet = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                // Perform update cart operations
                CartWorkflowHelper.PerformSaveCartOperations(this.Context, request, salesTransaction, returnTransaction, newSalesLineIdSet, productsByRecordId);

                // Sets the wharehouse id and invent location id for each line
                ItemAvailabilityHelper.SetSalesLineInventory(this.Context, salesTransaction);

                // Calculate totals and saves the sales transaction
                CartWorkflowHelper.Calculate(this.Context, salesTransaction, request.CalculationModes, isItemSale, newSalesLineIdSet);

                // Validate price on sales line after calculations
                CartWorkflowHelper.ValidateSalesLinePrice(this.Context, salesTransaction, productsByRecordId);

                // Validate the customer account deposit transaction.
                AccountDepositHelper.ValidateCustomerAccountDepositTransaction(this.Context, salesTransaction);

                // Validate return item and return transaction permissions
                CartWorkflowHelper.ValidateReturnPermission(this.Context, salesTransaction, request.Cart.CartType);

                // Calculate the required reason codes after the price calculation
                ReasonCodesWorkflowHelper.CalculateRequiredReasonCodes(this.Context, salesTransaction, ReasonCodeSourceType.None);

                CartWorkflowHelper.SaveSalesTransaction(this.Context, salesTransaction);

                Cart cart = CartWorkflowHelper.ConvertToCart(this.Context, salesTransaction);

                CartWorkflowHelper.RemoveHistoricalTenderLines(cart);

                return(new SaveCartResponse(cart));
            }
            /// <summary>
            /// Executes the workflow to fetch the promotions.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>The response.</returns>
            protected override GetPromotionsResponse Process(GetPromotionsRequest request)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.Null(request.CartId, "request.CartId");

                // Get the current instance of the transaction from the database.
                SalesTransaction transaction = CartWorkflowHelper.LoadSalesTransaction(this.Context, request.CartId);

                if (transaction == null)
                {
                    return(new GetPromotionsResponse(null));
                }

                ThrowIf.Null(transaction, "transaction");

                // Calculate totals on the current instance of transaction.
                CartWorkflowHelper.Calculate(this.Context, transaction, CalculationModes.All);

                // The discount lines on this transaction are the discount lines that have been applied.
                SalesTransaction currentSalesTransaction = transaction.Clone <SalesTransaction>();

                Cart cart = CartWorkflowHelper.ConvertToCart(this.Context, currentSalesTransaction);

                CartWorkflowHelper.RemoveHistoricalTenderLines(cart);

                // The discount lines on the transaction are all available discount lines for the items.
                CartWorkflowHelper.LoadAllPeriodicDiscounts(this.Context, currentSalesTransaction);
                SalesTransaction tempSalesTransaction = transaction.Clone <SalesTransaction>();

                transaction = currentSalesTransaction.Clone <SalesTransaction>();

                Collection <string>            cartPromotionLines = new Collection <string>();
                Collection <CartLinePromotion> cartLinePromotions = new Collection <CartLinePromotion>();

                for (int i = 0; i < currentSalesTransaction.SalesLines.Count; i++)
                {
                    // Removing the applied discount lines, except multiple buy because a different discount level of the already applied multi buy discount can be promoted.
                    foreach (DiscountLine discountLine in currentSalesTransaction.SalesLines[i].DiscountLines)
                    {
                        tempSalesTransaction.SalesLines[i].DiscountLines.Remove(tempSalesTransaction.SalesLines[i].DiscountLines.Where(j => j.OfferId == discountLine.OfferId).SingleOrDefault());
                    }

                    // Removing the discounts that require coupon code.
                    // Removing the discount offers those were not applied (because of concurrency rules).
                    // Removing mix and match discounts (mix and match discounts are not shown as promotions).
                    List <DiscountLine> offerDiscountLines = tempSalesTransaction.SalesLines[i].DiscountLines.Where(j => (j.PeriodicDiscountType == PeriodicDiscountOfferType.Offer) || j.IsDiscountCodeRequired || (j.PeriodicDiscountType == PeriodicDiscountOfferType.MixAndMatch)).ToList();
                    foreach (DiscountLine discountLine in offerDiscountLines)
                    {
                        tempSalesTransaction.SalesLines[i].DiscountLines.Remove(discountLine);
                    }

                    PricingDataManager pricingDataManager = new PricingDataManager(this.Context);

                    // Quantity discounts.
                    // Finding all the quantity discounts that will be applied to the cart.
                    List <DiscountLine> quantityDiscountLines = tempSalesTransaction.SalesLines[i].DiscountLines.Where(j => j.PeriodicDiscountType == PeriodicDiscountOfferType.MultipleBuy).ToList();

                    // Get the multibuy discount lines for this multi buy discounts.
                    IEnumerable <QuantityDiscountLevel> multiBuyDiscountLines = pricingDataManager.GetMultipleBuyDiscountLinesByOfferIds(quantityDiscountLines.Select(j => j.OfferId));

                    foreach (DiscountLine discountLine in quantityDiscountLines)
                    {
                        GetQuantityPromotions(transaction, tempSalesTransaction, this.Context, i, discountLine, multiBuyDiscountLines);
                    }

                    // Threshhold Discounts.
                    // Finding all the threshold discounts that will be applied to the cart.
                    List <DiscountLine> thresholdDiscountLines = tempSalesTransaction.SalesLines[i].DiscountLines.Where(j => j.PeriodicDiscountType == PeriodicDiscountOfferType.Threshold).ToList();

                    // Get the tiers for this threshold discounts
                    IEnumerable <ThresholdDiscountTier> tiers = pricingDataManager.GetThresholdTiersByOfferIds(thresholdDiscountLines.Select(j => j.OfferId));

                    foreach (DiscountLine thresholdDiscount in thresholdDiscountLines)
                    {
                        GetThresholdDiscounts(transaction, tempSalesTransaction, this.Context, i, cartPromotionLines, thresholdDiscount, tiers);
                    }

                    IEnumerable <string> promotionsForCurrentLine = tempSalesTransaction.SalesLines[i].DiscountLines.Select(j => j.OfferName);
                    cartLinePromotions.Add(new CartLinePromotion(cart.CartLines[i].LineId, promotionsForCurrentLine));
                }

                CartPromotions cartPromotions = new CartPromotions(cartPromotionLines, cartLinePromotions);

                return(new GetPromotionsResponse(cartPromotions));
            }
            /// <summary>
            /// Updates the sales transaction with the threshold promotion if applicable.
            /// </summary>
            /// <param name="existingTransaction">Existing transaction.</param>
            /// <param name="tempSalesTransaction">Copy of existing transaction.</param>
            /// <param name="context">The request context.</param>
            /// <param name="salesLineIndex">The sales line under consideration.</param>
            /// <param name="cartPromotionLines">The object with the cart promotion lines.</param>
            /// <param name="thresholdDiscount">The threshold discount line under consideration.</param>
            /// <param name="tiers">The tiers for the threshold discount.</param>
            private static void GetThresholdDiscounts(
                SalesTransaction existingTransaction,
                SalesTransaction tempSalesTransaction,
                RequestContext context,
                int salesLineIndex,
                Collection <string> cartPromotionLines,
                DiscountLine thresholdDiscount,
                IEnumerable <ThresholdDiscountTier> tiers)
            {
                // Find all the sales lines with the same offer.
                List <SalesLine> salesLinesWithOffer = tempSalesTransaction.SalesLines.Where(j => j.DiscountLines.Any(k => k.OfferId.Equals(thresholdDiscount.OfferId))).ToList();
                decimal          totalAmount         = salesLinesWithOffer.Select(j => j.GrossAmount).Sum();
                decimal          currentQuantity     = tempSalesTransaction.SalesLines[salesLineIndex].Quantity;

                // Find the minimum threshold amount required to hit a discount among all the tiers for this offer.
                IEnumerable <ThresholdDiscountTier> tiersForCurrentAmtOffer = tiers.Where(j => j.OfferId.Equals(thresholdDiscount.OfferId) && j.AmountThreshold > totalAmount).OrderBy(l => l.AmountThreshold);
                ThresholdDiscountTier tier = tiersForCurrentAmtOffer.Any() ? tiersForCurrentAmtOffer.First() : null;

                if (tier != null)
                {
                    // Add that amount difference to the first item that has this offer in the cart by increasing its quantity and see if this discount applies after applying concurrency rules.
                    existingTransaction.SalesLines[salesLineIndex].Quantity =
                        Math.Ceiling(tempSalesTransaction.SalesLines[salesLineIndex].Quantity *
                                     (tier.AmountThreshold - totalAmount + tempSalesTransaction.SalesLines[salesLineIndex].GrossAmount) / (tempSalesTransaction.SalesLines[salesLineIndex].GrossAmount / tempSalesTransaction.SalesLines[salesLineIndex].Quantity));

                    CartWorkflowHelper.Calculate(context, existingTransaction, CalculationModes.All);
                    DiscountLine isApplied = existingTransaction.SalesLines[salesLineIndex].DiscountLines.Where(j => j.OfferId.Equals(thresholdDiscount.OfferId)).SingleOrDefault();

                    if (isApplied != null)
                    {
                        var getItemsRequest = new GetItemsDataRequest(salesLinesWithOffer.Select(j => j.ItemId))
                        {
                            QueryResultSettings = new QueryResultSettings(new ColumnSet("NAME"), PagingInfo.AllRecords)
                        };
                        var getItemsResponse = context.Runtime.Execute <GetItemsDataResponse>(getItemsRequest, context);

                        ReadOnlyCollection <Item> items  = getItemsResponse.Items;
                        StringBuilder             buffer = new StringBuilder();

                        foreach (Item item in items.ToList())
                        {
                            buffer.Append(item.Name).Append(", ");
                        }

                        buffer.Remove(buffer.Length - 2, 1);

                        if (tier.DiscountMethod == ThresholdDiscountMethod.AmountOff)
                        {
                            thresholdDiscount.OfferName = string.Format(CultureInfo.CurrentUICulture, Resources.ThresholdDiscountPricePromotion, buffer, Math.Round(tier.AmountThreshold, 2), Math.Round(tier.DiscountValue, 2));
                        }
                        else
                        {
                            thresholdDiscount.OfferName = string.Format(CultureInfo.CurrentUICulture, Resources.ThresholdDiscountPercentagePromotion, buffer, Math.Round(tier.AmountThreshold, 2), Math.Round(tier.DiscountValue, 2));
                        }

                        cartPromotionLines.Add(thresholdDiscount.OfferName);
                    }
                }

                existingTransaction.SalesLines[salesLineIndex].Quantity = currentQuantity;
                CartWorkflowHelper.Calculate(context, existingTransaction, CalculationModes.All);

                foreach (SalesLine salesLineWithOffer in salesLinesWithOffer)
                {
                    salesLineWithOffer.DiscountLines.Remove(salesLineWithOffer.DiscountLines.Where(k => k.OfferId == thresholdDiscount.OfferId).SingleOrDefault());
                }
            }
Beispiel #11
0
            /// <summary>
            /// Executes the workflow to add or delete discount codes in cart.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>The response.</returns>
            protected override SaveCartResponse Process(AddOrRemoveDiscountCodesRequest request)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.Null(request.CartId, "request.CartId");
                ThrowIf.Null(request.DiscountCodes, "request.DiscountCodes");

                // Load sales transaction.
                SalesTransaction transaction = CartWorkflowHelper.LoadSalesTransaction(this.Context, request.CartId);

                if (transaction == null)
                {
                    throw new CartValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_CartNotFound, request.CartId);
                }

                IEnumerable <SalesTransaction> salesTransactions = new[] { transaction };

                transaction = salesTransactions.SingleOrDefault();

                if (transaction == null)
                {
                    return(new SaveCartResponse(new Cart()));
                }

                bool update = false;

                switch (request.DiscountCodesOperation)
                {
                case DiscountCodesOperation.Add:
                    foreach (string discountCode in request.DiscountCodes)
                    {
                        if (!transaction.DiscountCodes.Contains(discountCode))
                        {
                            transaction.DiscountCodes.Add(discountCode);
                            update = true;
                        }
                    }

                    break;

                case DiscountCodesOperation.Remove:
                    foreach (string discountCode in request.DiscountCodes)
                    {
                        transaction.DiscountCodes.Remove(discountCode);
                        update = true;
                    }

                    break;

                default:
                    throw new DataValidationException(
                              DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidRequest,
                              string.Format("Invalid discount code operation value: {0}", request.DiscountCodesOperation));
                }

                if (update)
                {
                    // Calculate totals
                    CartWorkflowHelper.Calculate(this.Context, transaction, null);

                    // Save the sales transaction
                    CartWorkflowHelper.SaveSalesTransaction(this.Context, transaction);
                }

                Cart cart = CartWorkflowHelper.ConvertToCart(this.Context, transaction);

                CartWorkflowHelper.RemoveHistoricalTenderLines(cart);

                return(new SaveCartResponse(cart));
            }