/// <summary>
            /// Voids the gift card operation.
            /// </summary>
            /// <param name="context">Request context.</param>
            /// <param name="salesTransaction">The sales transaction.</param>
            /// <param name="giftCardId">The identifier of the gift card to be voided.</param>
            /// <param name="giftCardCurrencyCode">Gift card's currency code.</param>
            /// <param name="giftCardOperation">The type of gift card operation(Issue or AddTo).</param>
            /// <param name="amount">The amount on the gift card.</param>
            internal static void VoidGiftCardOperation(RequestContext context, SalesTransaction salesTransaction, string giftCardId, string giftCardCurrencyCode, GiftCardOperationType giftCardOperation, decimal amount)
            {
                ThrowIf.Null(context, "context");
                ThrowIf.Null(salesTransaction, "salesTransaction");

                switch (giftCardOperation)
                {
                case GiftCardOperationType.AddTo:
                {
                    GiftCardWorkflowHelper.VoidAddToGiftCardOperation(context, salesTransaction, giftCardId, giftCardCurrencyCode, amount);

                    // Unlock the gift card.
                    GiftCardWorkflowHelper.UnlockGiftCard(context, giftCardId);
                    break;
                }

                case GiftCardOperationType.Issue:
                {
                    GiftCardWorkflowHelper.VoidIssuedGiftCardOperation(context, giftCardId);

                    // No need to unlock gift card in this case because voiding will result gift card entry being deleted.
                    break;
                }

                default:
                    throw new InvalidOperationException(string.Format("Gift card operation {0} is not supported.", giftCardOperation));
                }
            }
            /// <summary>
            /// Unlocks the gift cards that are on active sales lines.
            /// </summary>
            /// <param name="context">The context.</param>
            /// <param name="salesTransaction">The sales transaction.</param>
            internal static void UnlockGiftCardsOnActiveSalesLines(RequestContext context, SalesTransaction salesTransaction)
            {
                ThrowIf.Null(context, "context");
                ThrowIf.Null(salesTransaction, "salesTransaction");

                IEnumerable <SalesLine> activeGiftCardLines = salesTransaction.SalesLines.Where(l => l.IsGiftCardLine && !l.IsVoided);

                foreach (SalesLine line in activeGiftCardLines)
                {
                    GiftCardWorkflowHelper.UnlockGiftCard(context, line.GiftCardId);
                }
            }
            /// <summary>
            /// Voids active gift card lines on the transaction.
            /// </summary>
            /// <param name="context">The context.</param>
            /// <param name="salesTransaction">The sales transaction.</param>
            internal static void VoidGiftCardSalesLines(RequestContext context, SalesTransaction salesTransaction)
            {
                ThrowIf.Null(context, "context");
                ThrowIf.Null(salesTransaction, "salesTransaction");

                IEnumerable <SalesLine> activeGiftCardLines = salesTransaction.SalesLines.Where(l => l.IsGiftCardLine && !l.IsVoided);

                foreach (SalesLine line in activeGiftCardLines)
                {
                    GiftCardWorkflowHelper.VoidGiftCardOperation(context, salesTransaction, line.GiftCardId, line.GiftCardCurrencyCode, line.GiftCardOperation, line.TotalAmount);
                }
            }
            /// <summary>
            /// Creates a sales transaction given the cart.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>The response.</returns>
            protected override SubmitSalesTransactionResponse Process(SubmitSalesTransactionRequest request)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.Null(request.CartId, "request.CartId");

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

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

                if (salesTransaction.IsSuspended)
                {
                    throw new CartValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_CartNotActive, request.CartId);
                }

                if (!(salesTransaction.TerminalId ?? string.Empty).Equals(this.Context.GetTerminal().TerminalId ?? string.Empty, StringComparison.OrdinalIgnoreCase))
                {
                    // If the terminal id of the cart is not same as the context then it means that the cart is active on another terminal.
                    throw new CartValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_LoadingActiveCartFromAnotherTerminalNotAllowed, request.CartId);
                }

                salesTransaction.ReceiptEmail = request.ReceiptEmail;

                // Set the transaction type to represent Cash & Carry sales by default, if it's not an IncomeExpense nor AccountDeposit transaction.
                if (salesTransaction.TransactionType != SalesTransactionType.IncomeExpense && salesTransaction.TransactionType != SalesTransactionType.CustomerAccountDeposit)
                {
                    salesTransaction.TransactionType = SalesTransactionType.Sales;
                }

                // Fill in Store and Terminal information.
                OrderWorkflowHelper.FillTransactionWithContextData(this.Context, salesTransaction);

                // Calculate required reason code for end of transaction.
                ReasonCodesWorkflowHelper.CalculateRequiredReasonCodesOnTransaction(this.Context, salesTransaction, ReasonCodeSourceType.EndOfTransaction);

                // Validate required reason code lines has been filled.
                ReasonCodesWorkflowHelper.ValidateRequiredReasonCodeLinesFilled(this.Context, salesTransaction);

                // Validate return permission.
                CartWorkflowHelper.ValidateReturnPermission(this.Context, salesTransaction, salesTransaction.CartType);

                // Fill in variant information.
                OrderWorkflowHelper.FillVariantInformation(this.Context, salesTransaction);

                // Fill in Receipt Id.
                OrderWorkflowHelper.FillInReceiptId(this.Context, salesTransaction, request.ReceiptNumberSequence);

                // Validate sales order for creation.
                OrderWorkflowHelper.ValidateContextForCreateOrder(this.Context, salesTransaction);

                // Validate payments.
                OrderWorkflowHelper.CalculateAndValidateAmountPaidForCheckout(this.Context, salesTransaction);

                salesTransaction = OrderWorkflowHelper.ProcessCheckoutPayments(this.Context, salesTransaction);

                // release/unlock gift cards on sales lines
                GiftCardWorkflowHelper.UnlockGiftCardsOnActiveSalesLines(this.Context, salesTransaction);

                // Pay Sales Invoices...
                OrderWorkflowHelper.SettleInvoiceSalesLines(this.Context, salesTransaction);

                // Create order
                var salesOrder = OrderWorkflowHelper.CreateSalesOrder(this.Context, salesTransaction);

                // We also need to delete the shopping cart.
                CartWorkflowHelper.TryDeleteCart(
                    this.Context,
                    new SalesTransaction
                {
                    Id         = salesTransaction.Id,
                    TerminalId = salesTransaction.TerminalId,
                    CustomerId = salesTransaction.CustomerId,
                });

                return(new SubmitSalesTransactionResponse(salesOrder));
            }
            /// <summary>
            /// Creates a sales transaction given the cart.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>The response.</returns>
            protected override SaveVoidTransactionResponse Process(SaveVoidTransactionRequest request)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.Null(request.CartId, "request.CartId");

                // Get the Sales Transaction
                SalesTransaction salesTransaction = CartWorkflowHelper.LoadSalesTransaction(this.Context, request.CartId, ignoreProductDiscontinuedNotification: true);

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

                if (salesTransaction.IsSuspended)
                {
                    throw new CartValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_CartNotActive, request.CartId);
                }

                // If there are unvoided non-historical Tender lines throw exception that Transaction cannot be voided.
                if (salesTransaction.TenderLines != null && salesTransaction.TenderLines.Any(l => l.TransactionStatus != TransactionStatus.Voided && l.Status != TenderLineStatus.Historical))
                {
                    throw new CartValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_VoidTransactionContainsTenderedLines, request.CartId);
                }

                // Add or update any incoming reason codes on the transaction.
                Cart cartToBeVoided = new Cart
                {
                    Id = request.CartId
                };

                if (request.ReasonCodeLines != null)
                {
                    cartToBeVoided.ReasonCodeLines = request.ReasonCodeLines.ToList();
                }

                ReasonCodesWorkflowHelper.AddOrUpdateReasonCodeLinesOnTransaction(salesTransaction, cartToBeVoided);

                // Calculate the required reason codes on the tender line for voiding transaction.
                ReasonCodesWorkflowHelper.CalculateRequiredReasonCodesOnTransaction(this.Context, salesTransaction, ReasonCodeSourceType.VoidTransaction);

                GiftCardWorkflowHelper.VoidGiftCardSalesLines(this.Context, salesTransaction);

                // Set the Sales Transaction Status to Voided.
                salesTransaction.EntryStatus = TransactionStatus.Voided;

                // Set the transaction type to represent Cash & Carry sales.
                if (salesTransaction.TransactionType != SalesTransactionType.IncomeExpense)
                {
                    salesTransaction.TransactionType = SalesTransactionType.Sales;
                }

                // Fill in Store and Terminal information.
                OrderWorkflowHelper.FillTransactionWithContextData(this.Context, salesTransaction);

                // Create order
                var salesOrder = OrderWorkflowHelper.CreateSalesOrder(this.Context, salesTransaction);

                // We also need to delete the shopping cart.
                CartWorkflowHelper.TryDeleteCart(
                    this.Context,
                    new SalesTransaction
                {
                    Id         = request.CartId,
                    TerminalId = salesTransaction.TerminalId,
                    CustomerId = salesTransaction.CustomerId,
                });

                CartWorkflowHelper.LogAuditEntry(
                    this.Context,
                    "SaveVoidTransactionRequestHandler.Process",
                    string.Format("Transaction '{0}' voided.", request.CartId));

                return(new SaveVoidTransactionResponse(salesOrder));
            }
            /// <summary>
            /// Processes the void cart lines request.
            /// </summary>
            /// <param name="context">The context.</param>
            /// <param name="request">The request.</param>
            /// <param name="salesTransaction">Sales transaction.</param>
            private static void ProcessVoidCartLinesRequest(RequestContext context, SaveCartLinesRequest request, SalesTransaction salesTransaction)
            {
                Dictionary <string, SalesLine> salesLinesById = salesTransaction.SalesLines.ToDictionary(sl => sl.LineId, sl => sl);

                // Keeps track of the enabled (unvoided) sales lines.
                var enabledSalesLines = new List <SalesLine>();

                foreach (CartLine cartLine in request.CartLines)
                {
                    var salesLine = salesLinesById[cartLine.LineId];

                    if (salesTransaction.CartType == CartType.CustomerOrder &&
                        salesTransaction.CustomerOrderMode != CustomerOrderMode.CustomerOrderCreateOrEdit &&
                        salesTransaction.CustomerOrderMode != CustomerOrderMode.QuoteCreateOrEdit &&
                        cartLine.IsVoided)
                    {
                        string errorMessage = "Cart line can be voided only at the time of CustomerOrderCreateOrEdit or QuoteCreateOrEdit.";
                        throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidCustomerOrderModeForVoidProducts, errorMessage);
                    }

                    if ((cartLine.IsCustomerAccountDeposit || salesTransaction.CartType == CartType.AccountDeposit) && cartLine.IsVoided)
                    {
                        throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_CustomerAccountDepositCannotBeVoided, "Cart line cannot be voided for customer account deposit transaction.");
                    }

                    if (!cartLine.IsVoided && salesLine.IsVoided)
                    {
                        // Unvoid
                        if (cartLine.IsGiftCardLine)
                        {
                            throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_GiftCardLineVoidReversalNotSupported, "Gift card line cannot be unvoided.");
                        }

                        // Unvoid the sales line.
                        salesLine.IsVoided = false;

                        // Unvoid the linked products' sales lines if any.
                        if (salesLine.LineIdsLinkedProductMap.Any())
                        {
                            foreach (string lineId in salesLine.LineIdsLinkedProductMap.Keys)
                            {
                                if (salesLinesById[lineId] != null)
                                {
                                    salesLinesById[lineId].IsVoided = false;
                                    enabledSalesLines.Add(salesLinesById[lineId]);
                                }
                                else
                                {
                                    throw new DataValidationException(
                                              DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound,
                                              string.Format("Sales line of the linked product with id : {0} was not found.", lineId));
                                }
                            }
                        }

                        // Add the new line to the collection for attribute updates.
                        enabledSalesLines.Add(salesLine);

                        // Perform additional side-effect logic here (i.e. issue gift cart etc.)
                    }
                    else
                    {
                        // Process reason code lines on the cart line.
                        ReasonCodesWorkflowHelper.AddOrUpdateReasonCodeLinesOnSalesLine(salesLine, cartLine, salesTransaction.Id);

                        // Calculate the required reason codes for voiding sales lines.
                        ReasonCodesWorkflowHelper.CalculateRequiredReasonCodesOnSalesLine(context, salesTransaction, salesLine, ReasonCodeSourceType.VoidItem);

                        // Void the sales line.
                        salesLine.IsVoided = true;

                        // Void the linked products' sales lines if any.
                        if (salesLine.LineIdsLinkedProductMap.Any())
                        {
                            foreach (string lineId in salesLine.LineIdsLinkedProductMap.Keys)
                            {
                                if (salesLinesById[lineId] != null)
                                {
                                    salesLinesById[lineId].IsVoided = true;
                                }
                                else
                                {
                                    throw new DataValidationException(
                                              DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound,
                                              string.Format("Sales line of the linked product with id : {0} was not found.", lineId));
                                }
                            }
                        }

                        // Void gift card lines.
                        if (salesLine.IsGiftCardLine)
                        {
                            GiftCardWorkflowHelper.VoidGiftCardOperation(context, salesTransaction, salesLine.GiftCardId, salesLine.GiftCardCurrencyCode, salesLine.GiftCardOperation, salesLine.TotalAmount);
                        }

                        CartWorkflowHelper.LogAuditEntry(
                            context,
                            "SaveCartLinesRequestHandler.ProcessVoidCartLinesRequest",
                            string.Format("Line item voided: {0}, #: {1}", salesLine.Description, salesLine.LineNumber));
                    }
                }

                // Set default attributes from order header if there are any enabled sales lines.
                if (enabledSalesLines.Any())
                {
                    CartWorkflowHelper.SetDefaultDataOnSalesLines(context, salesTransaction, enabledSalesLines);
                }
            }