/// <summary>
            /// Update tender line amounts with cash back for company and channel currencies.
            /// </summary>
            /// <param name="tenderLine">The tender line to update.</param>
            /// <param name="context">The request context.</param>
            private static void CalculateAmountsWithCashBack(TenderLine tenderLine, RequestContext context)
            {
                tenderLine.Amount += tenderLine.CashBackAmount;

                // In case of cashback, tendered currency is always equal to the channel currency.
                tenderLine.AmountInTenderedCurrency += tenderLine.CashBackAmount;

                string companyCurrencyCode = context.GetChannelConfiguration().CompanyCurrency;

                if (!tenderLine.Currency.Equals(companyCurrencyCode, StringComparison.OrdinalIgnoreCase))
                {
                    // Convert cashback from tendered to company currency.
                    GetCurrencyValueServiceResponse cashBackConversionInfo = ConvertCurrencyAmount(
                        tenderLine.CashBackAmount,
                        tenderLine.Currency,
                        companyCurrencyCode,
                        context);
                    tenderLine.AmountInCompanyCurrency += cashBackConversionInfo.ConvertedAmount;
                }
                else
                {
                    tenderLine.AmountInCompanyCurrency += tenderLine.CashBackAmount;
                }

                tenderLine.CashBackAmount = 0M;
            }
Пример #2
0
            /// <summary>
            /// Get the gift card.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>A response containing the gift card information.</returns>
            private static GetGiftCardServiceResponse GetGiftCard(GetGiftCardServiceRequest request)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                if (string.IsNullOrWhiteSpace(request.GiftCardId))
                {
                    throw new ArgumentException("request.Id is null or empty.", "request");
                }

                var      requestGetGiftCardBalance = new GetGiftCardRealtimeRequest(request.GiftCardId);
                GiftCard card = request.RequestContext.Execute <SingleEntityDataServiceResponse <GiftCard> >(requestGetGiftCardBalance).Entity;

                GetCurrencyValueServiceResponse convertBalanceToChannelCurrencyResponse = FromGiftCardCurrency(request.RequestContext, card.BalanceInCardCurrency, card.CardCurrencyCode);

                GiftCard giftCard = new GiftCard
                {
                    Id = request.GiftCardId,
                    BalanceCurrencyCode   = convertBalanceToChannelCurrencyResponse.ToCurrencyCode,
                    Balance               = convertBalanceToChannelCurrencyResponse.RoundedConvertedAmount,
                    CardCurrencyCode      = card.CardCurrencyCode,
                    BalanceInCardCurrency = card.BalanceInCardCurrency
                };

                return(new GetGiftCardServiceResponse(giftCard));
            }
            /// <summary>
            /// Convert amount in one currency to another.
            /// </summary>
            /// <param name="fromCurrencyCode">From currency code.</param>
            /// <param name="toCurrencyCode">To currency code.</param>
            /// <param name="amountToConvert">Amount to convert.</param>
            /// <returns>Converted amount in new currency.</returns>
            public decimal ConvertCurrency(string fromCurrencyCode, string toCurrencyCode, decimal amountToConvert)
            {
                GetCurrencyValueServiceRequest  currencyRequest  = new GetCurrencyValueServiceRequest(fromCurrencyCode, toCurrencyCode, amountToConvert);
                GetCurrencyValueServiceResponse currencyResponse = this.context.Execute <GetCurrencyValueServiceResponse>(currencyRequest);

                return(currencyResponse.ConvertedAmount);
            }
            /// <summary>
            /// Update tender line with amounts and exchange rates for company and channel currencies.
            /// </summary>
            /// <param name="tenderLine">Tender line to update.</param>
            /// <param name="context">Request context.</param>
            private static void CalculateTenderLineCurrencyAmounts(TenderLine tenderLine, RequestContext context)
            {
                string channelCurrencyCode = context.GetChannelConfiguration().Currency;

                if (!tenderLine.Currency.Equals(channelCurrencyCode, StringComparison.OrdinalIgnoreCase))
                {
                    if (tenderLine.AmountInTenderedCurrency == 0)
                    {
                        throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidRequest, "Currency on tender line is different from store currency but AmountInTenderedCurrency is not specified.");
                    }

                    IEnumerable <string> supportedCurrencies = GetCurrencyCodeSupportedByChannel(context);
                    if (supportedCurrencies.FirstOrDefault(c => string.Equals(c, tenderLine.Currency, StringComparison.OrdinalIgnoreCase)) == null)
                    {
                        throw new DataValidationException(
                                  DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_CurrencyConversionFailed,
                                  string.Format("Currency code '{0}' is not supported by current channel.", tenderLine.Currency));
                    }

                    // Convert amount and get exchange rate from tendered currency to channel currency.
                    GetCurrencyValueServiceResponse channelConversionInfo = ConvertCurrencyAmount(
                        tenderLine.AmountInTenderedCurrency,
                        tenderLine.Currency,
                        channelCurrencyCode,
                        context);

                    tenderLine.ExchangeRate = channelConversionInfo.ExchangeRate;
                    tenderLine.Amount       = channelConversionInfo.RoundedConvertedAmount;

                    // Round the amount in tendered currency.
                    tenderLine.AmountInTenderedCurrency = RoundAmountByCurrency(context, tenderLine.AmountInTenderedCurrency, tenderLine.Currency);
                }
                else
                {
                    tenderLine.ExchangeRate             = 1m;
                    tenderLine.AmountInTenderedCurrency = tenderLine.Amount;
                }

                string companyCurrencyCode = context.GetChannelConfiguration().CompanyCurrency;

                if (!tenderLine.Currency.Equals(companyCurrencyCode, StringComparison.OrdinalIgnoreCase))
                {
                    // Convert tendered amount to company amount, and get exchange rate from tendered to company currencies.
                    GetCurrencyValueServiceResponse tenderConversionInfo = ConvertCurrencyAmount(
                        tenderLine.AmountInTenderedCurrency,
                        tenderLine.Currency,
                        companyCurrencyCode,
                        context);
                    tenderLine.CompanyCurrencyExchangeRate = tenderConversionInfo.ExchangeRate;
                    tenderLine.AmountInCompanyCurrency     = tenderConversionInfo.RoundedConvertedAmount;
                }
                else
                {
                    tenderLine.CompanyCurrencyExchangeRate = 1m;
                    tenderLine.AmountInCompanyCurrency     = PaymentManagerService.RoundAmountByCurrency(context, tenderLine.AmountInTenderedCurrency, tenderLine.Currency);
                }
            }
            /// <summary>
            /// Convert amount from one currency to another.
            /// </summary>
            /// <param name="amountInCurrency">Amount to convert.</param>
            /// <param name="fromCurrencyCode">Currency to convert from.</param>
            /// <param name="toCurrencyCode">Currency to convert to.</param>
            /// <param name="context">Request context.</param>
            /// <returns>Response that contains converted amount along with exchange rate.</returns>
            private static GetCurrencyValueServiceResponse ConvertCurrencyAmount(decimal amountInCurrency, string fromCurrencyCode, string toCurrencyCode, RequestContext context)
            {
                var request = new GetCurrencyValueServiceRequest(
                    fromCurrencyCode,
                    toCurrencyCode,
                    amountInCurrency);

                GetCurrencyValueServiceResponse response = context.Execute <GetCurrencyValueServiceResponse>(request);

                return(response);
            }
Пример #6
0
            /// <summary>
            /// Converts a value from one currency to another.
            /// </summary>
            /// <param name="request">Currency request specifying retail runtime, source and destination currencies, and amount to convert.</param>
            /// <returns>
            /// The value as it is after conversion in the destination currency, rounded according to the destination currency's rounding setup.
            /// </returns>
            private static GetCurrencyValueServiceResponse CurrencyToCurrency(GetCurrencyValueServiceRequest request)
            {
                GetCurrencyValueServiceResponse response = GetCurrencyConversion(request.RequestContext, request);

                if (response.ExchangeRate <= 0)
                {
                    throw new DataValidationException(
                              DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_CurrencyConversionFailed,
                              string.Format("Exchange rate from currency '{0}' to currency '{1}' is not supported or misconfigured. Calculated exchange rate: {2}", response.FromCurrencyCode, response.ToCurrencyCode, response.ExchangeRate));
                }

                return(response);
            }
Пример #7
0
            /// <summary>
            /// Calculate the total currency amount after converting to channel currency for the given list of currencies.
            /// </summary>
            /// <param name="request">The list of currencies that requires to be summed.</param>
            /// <returns>Returns the response that contains the sum of currencies amount.</returns>
            private static CalculateTotalAmountServiceResponse CalculateTotalCurrencyAmount(CalculateTotalAmountServiceRequest request)
            {
                var     channelConfiguration = request.RequestContext.GetChannelConfiguration();
                decimal totalAmount          = 0m;

                foreach (var currency in request.CurrenciesToConvert)
                {
                    var getCurrencyValueRequest = new GetCurrencyValueServiceRequest(currency.CurrencyCode, channelConfiguration.Currency, currency.AmountToConvert);
                    GetCurrencyValueServiceResponse response = request.RequestContext.Execute <GetCurrencyValueServiceResponse>(getCurrencyValueRequest);
                    totalAmount += response.RoundedConvertedAmount;
                }

                return(new CalculateTotalAmountServiceResponse(new CurrencyAmount {
                    RoundedConvertedAmount = totalAmount, CurrencyCode = channelConfiguration.Currency
                }));
            }
Пример #8
0
            /// <summary>
            /// Gets all supported channel currency value and exchange rate for the given amount.
            /// </summary>
            /// <param name="request">Request contains the amount to be converted.</param>
            /// <returns>The converted amount with exchange rates.</returns>
            private static GetChannelCurrencyServiceResponse GetSupportedChannelCurrencies(GetChannelCurrencyServiceRequest request)
            {
                string fromCurrencyCode = request.CurrencyCode;

                GetChannelCurrenciesDataRequest     dataRequest            = new GetChannelCurrenciesDataRequest(request.QueryResultSettings ?? QueryResultSettings.AllRecords);
                PagedResult <CurrencyAmount>        pagedChannelCurrencies = request.RequestContext.Execute <EntityDataServiceResponse <CurrencyAmount> >(dataRequest).PagedEntityCollection;
                ReadOnlyCollection <CurrencyAmount> channelCurrencies      = pagedChannelCurrencies.Results;

                if (channelCurrencies == null || !channelCurrencies.Any())
                {
                    NetTracer.Warning("Cannot find channel currencies");
                    return(new GetChannelCurrencyServiceResponse());
                }

                var currencyList = channelCurrencies.ToList();

                foreach (var toCurrency in currencyList)
                {
                    var getCurrencyValueRequest = new GetCurrencyValueServiceRequest(fromCurrencyCode, toCurrency.CurrencyCode, request.Amount);
                    GetCurrencyValueServiceResponse serviceResponse = GetCurrencyConversion(request.RequestContext, getCurrencyValueRequest);
                    toCurrency.ExchangeRate           = serviceResponse.ExchangeRate;
                    toCurrency.ConvertedAmount        = serviceResponse.ConvertedAmount;
                    toCurrency.RoundedConvertedAmount = serviceResponse.RoundedConvertedAmount;
                }

                var storeCurrencyList = currencyList.Where(currency => currency.ExchangeRate > 0M).ToList();

                // If the from currency does not exists add to the list.
                if (storeCurrencyList.All(currency => string.CompareOrdinal(currency.CurrencyCode, fromCurrencyCode) != 0))
                {
                    CurrencyAmount conversionResult = GetFromCurrencyAmount(request.RequestContext, request.Amount, fromCurrencyCode);
                    storeCurrencyList.Add(conversionResult);
                }

                pagedChannelCurrencies.Results = storeCurrencyList.AsReadOnly();

                return(new GetChannelCurrencyServiceResponse(pagedChannelCurrencies));
            }
Пример #9
0
            public static SalesOrder GetSalesOrderFromInfo(CustomerOrderInfo orderInfo, ChannelConfiguration channelConfiguration, RequestContext context)
            {
                // Stores the local copy. There is high probability of having the same shipping/delivery address on all the lines.
                Dictionary <long, Address> shippingAddressDictionary = new Dictionary <long, Address>();

                decimal shippingChargeAmount;

                ColumnSet columnSet  = new ColumnSet();
                var       salesOrder = new SalesOrder
                {
                    SalesId              = orderInfo.Id,
                    TransactionType      = SalesTransactionType.CustomerOrder,
                    CustomerOrderMode    = CustomerOrderMode.OrderRecalled,
                    CartType             = CartType.CustomerOrder,
                    CustomerOrderType    = orderInfo.OrderType,
                    StoreId              = orderInfo.StoreId,
                    IsTaxIncludedInPrice = Convert.ToBoolean(orderInfo.IsTaxIncludedInPrice)
                };

                switch (orderInfo.OrderType)
                {
                case CustomerOrderType.Quote:
                    salesOrder.Status = Utilities.GetSalesStatus((SalesQuotationStatus)orderInfo.Status);
                    break;

                case CustomerOrderType.SalesOrder:
                    salesOrder.Status = Utilities.GetSalesStatus((SalesOrderStatus)orderInfo.Status, (DocumentStatus)orderInfo.DocumentStatus);
                    break;

                default:
                    salesOrder.Status = SalesStatus.Unknown;
                    break;
                }

                DateTimeOffset currentChannelDate = context.GetNowInChannelTimeZone();

                salesOrder.RequestedDeliveryDate = Utilities.ParseDateStringAsDateTimeOffset(orderInfo.RequestedDeliveryDateString, currentChannelDate.Date, currentChannelDate.Offset);
                salesOrder.QuotationExpiryDate   = Utilities.ParseDateStringAsDateTimeOffset(orderInfo.ExpiryDateString, currentChannelDate.Date, currentChannelDate.Offset);

                // CreationDate is stored in UTC. It needs to be converted to local time zone where order is accessed.
                salesOrder.BeginDateTime = Utilities.ParseDateString(orderInfo.CreationDateString, currentChannelDate.ToUniversalTime().DateTime, DateTimeStyles.AssumeUniversal);

                salesOrder.Comment = orderInfo.Comment;

                // Header delivery
                salesOrder.InventoryLocationId = orderInfo.WarehouseId;
                salesOrder.DeliveryMode        = orderInfo.DeliveryMode;

                foreach (var discountCode in orderInfo.DiscountCodes)
                {
                    salesOrder.DiscountCodes.Add(discountCode);
                }

                // Customer info
                salesOrder.CustomerId = orderInfo.CustomerAccount;
                long addressRecordIdLong = 0;

                if (long.TryParse(orderInfo.AddressRecordId, out addressRecordIdLong))
                {
                    var dataServiceRequest = new GetAddressDataRequest(addressRecordIdLong, columnSet);
                    SingleEntityDataServiceResponse <Address> dataServiceResponse = context.Execute <SingleEntityDataServiceResponse <Address> >(dataServiceRequest);

                    if (dataServiceResponse.Entity == null)
                    {
                        Utilities.DownloadCustomerData(context, salesOrder.CustomerId);
                        dataServiceResponse = context.Execute <SingleEntityDataServiceResponse <Address> >(dataServiceRequest);
                    }

                    if (dataServiceResponse.Entity != null)
                    {
                        salesOrder.ShippingAddress = dataServiceResponse.Entity;
                        shippingAddressDictionary.Add(salesOrder.ShippingAddress.RecordId, salesOrder.ShippingAddress);
                    }
                }

                if (!string.IsNullOrEmpty(orderInfo.SalespersonStaffId))
                {
                    // Sets the sales person id and name according to AX values
                    // This is done because we do not know whether the sales person information is available on this store
                    salesOrder.StaffId = orderInfo.SalespersonStaffId;
                }

                salesOrder.ChannelReferenceId = orderInfo.ChannelReferenceId;
                salesOrder.LoyaltyCardId      = orderInfo.LoyaltyCardId;

                salesOrder.ReceiptEmail = orderInfo.Email;

                string shippingChargeCode = channelConfiguration.ShippingChargeCode;

                // Items
                int lineId = 0;

                foreach (ItemInfo item in orderInfo.Items)
                {
                    lineId++;
                    var lineItem = new SalesLine
                    {
                        LineId                  = lineId.ToString(CultureInfo.InvariantCulture),
                        Found                   = true,
                        RecordId                = item.RecId,
                        ItemId                  = item.ItemId,
                        Comment                 = item.Comment,
                        Quantity                = item.Quantity,
                        ReturnQuantity          = item.Quantity,
                        SalesOrderUnitOfMeasure = item.Unit,
                        UnitOfMeasureSymbol     = item.Unit,
                        Price                   = item.Price,
                        NetAmount               = item.NetAmount,
                        QuantityOrdered         = item.Quantity,
                        QuantityInvoiced        = item.QuantityPicked,
                        DeliveryMode            = item.DeliveryMode,
                        RequestedDeliveryDate   = Utilities.ParseDateStringAsDateTimeOffset(item.RequestedDeliveryDateString, currentChannelDate.Date, currentChannelDate.Offset),
                        FulfillmentStoreId      = item.FulfillmentStoreId,
                        InventoryLocationId     = item.WarehouseId,
                        SerialNumber            = item.SerialId,
                        BatchId                 = item.BatchId,
                        Status                  = TransactionStatus.Normal,
                        SalesStatus             = Utilities.GetSalesStatus((SalesOrderStatus)item.Status)
                    };

                    // Copy charges to line and calculates total shipping charge amount
                    lineItem.ChargeLines.AddRange(SalesOrderHelper.CreateChargeLines(item.Charges, shippingChargeCode, salesOrder.BeginDateTime, out shippingChargeAmount));
                    lineItem.DeliveryModeChargeAmount = shippingChargeAmount;

                    // Line level discount amounts
                    lineItem.LineDiscount                 = item.LineDscAmount;
                    lineItem.PeriodicDiscount             = item.PeriodicDiscount;
                    lineItem.PeriodicPercentageDiscount   = item.PeriodicPercentageDiscount;
                    lineItem.LineManualDiscountAmount     = item.LineManualDiscountAmount;
                    lineItem.LineManualDiscountPercentage = item.LineManualDiscountPercentage;
                    lineItem.TotalDiscount                = item.TotalDiscount;
                    lineItem.TotalPercentageDiscount      = item.TotalPctDiscount;

                    // Copy discounts to line
                    lineItem.DiscountLines.AddRange(SalesOrderHelper.CreateDiscountLines(item.Discounts));

                    long itemAddressRecordIdLong;
                    if (long.TryParse(item.AddressRecordId, out itemAddressRecordIdLong))
                    {
                        Address lineLevelshippingAddress = new Address();

                        if (!shippingAddressDictionary.TryGetValue(itemAddressRecordIdLong, out lineLevelshippingAddress))
                        {
                            var dataServiceRequest = new GetAddressDataRequest(itemAddressRecordIdLong, columnSet);
                            SingleEntityDataServiceResponse <Address> dataServiceResponse = context.Execute <SingleEntityDataServiceResponse <Address> >(dataServiceRequest);

                            // If address not found download and get.
                            if (dataServiceResponse.Entity == null)
                            {
                                Utilities.DownloadCustomerData(context, salesOrder.CustomerId);
                                dataServiceResponse = context.Execute <SingleEntityDataServiceResponse <Address> >(dataServiceRequest);
                            }

                            if (dataServiceResponse.Entity != null)
                            {
                                lineItem.ShippingAddress = dataServiceResponse.Entity;
                                shippingAddressDictionary.Add(lineItem.ShippingAddress.RecordId, lineItem.ShippingAddress);
                            }
                        }
                        else
                        {
                            lineItem.ShippingAddress = lineLevelshippingAddress;
                        }
                    }

                    Utilities.SetUpVariantAndProduct(context, item.InventDimensionId, lineItem.ItemId, lineItem);

                    lineItem.DiscountAmount = item.Discount;

                    // Set tax info after defaults, as it may have been overridden.
                    lineItem.SalesTaxGroupId = item.SalesTaxGroup ?? string.Empty;
                    lineItem.ItemTaxGroupId  = item.ItemTaxGroup ?? string.Empty;

                    // Add it to the transaction
                    salesOrder.SalesLines.Add(lineItem);
                }

                // Charges for the header
                salesOrder.ChargeLines.AddRange(SalesOrderHelper.CreateChargeLines(orderInfo.Charges, shippingChargeCode, salesOrder.BeginDateTime, out shippingChargeAmount));
                salesOrder.DeliveryModeChargeAmount = shippingChargeAmount;

                // Payments
                // - total up amounts
                // - add history entries
                decimal nonPrepayments       = decimal.Zero;
                decimal prepaymentAmountPaid = decimal.Zero;

                int tenderLineId = 0;

                foreach (PaymentInfo payment in orderInfo.Payments)
                {
                    if (salesOrder.TenderLines == null)
                    {
                        salesOrder.TenderLines = new Collection <TenderLine>();
                    }

                    decimal amount = 0M;
                    if (string.IsNullOrWhiteSpace(payment.Currency) || payment.Currency.Equals(channelConfiguration.Currency, StringComparison.OrdinalIgnoreCase))
                    {
                        amount = payment.Amount;
                    }
                    else
                    {
                        GetCurrencyValueServiceRequest  currencyValueRequest  = new GetCurrencyValueServiceRequest(payment.Currency, channelConfiguration.Currency, payment.Amount);
                        GetCurrencyValueServiceResponse currencyValueResponse = context.Execute <GetCurrencyValueServiceResponse>(currencyValueRequest);
                        amount = currencyValueResponse.RoundedConvertedAmount;
                    }

                    if (payment.Prepayment)
                    {
                        // Sum prepayments to track total deposits paid
                        prepaymentAmountPaid += amount;
                    }
                    else
                    {
                        // Sum non-prepayments as base for calculating deposits applied to pickups
                        nonPrepayments += amount;
                    }

                    tenderLineId++;
                    var tenderLine = new TenderLine
                    {
                        TenderLineId = tenderLineId.ToString(CultureInfo.InvariantCulture),
                        Amount       = payment.Amount,
                        Currency     = payment.Currency,
                        CardTypeId   = string.Empty,
                        Status       = TenderLineStatus.Historical,
                        IsVoidable   = false,
                        TenderDate   =
                            Utilities.ParseDateString(
                                payment.DateString,
                                currentChannelDate.Date,
                                DateTimeStyles.None)         // On channel timezone
                    };

                    salesOrder.TenderLines.Add(tenderLine);
                }

                if (orderInfo.Affiliations != null && orderInfo.Affiliations.Any())
                {
                    salesOrder.AffiliationLoyaltyTierLines.Clear();
                    salesOrder.AffiliationLoyaltyTierLines.AddRange(orderInfo.Affiliations.Select(line =>
                                                                                                  new SalesAffiliationLoyaltyTier
                    {
                        AffiliationId   = line.AffiliationRecordId,
                        LoyaltyTierId   = line.LoyaltyTierRecordId,
                        AffiliationType = line.AffiliationType
                    }));
                }

                // Prepayment/Deposit override info
                if (orderInfo.PrepaymentAmountOverridden)
                {
                    salesOrder.OverriddenDepositAmount = prepaymentAmountPaid;
                }

                salesOrder.PrepaymentAmountPaid = prepaymentAmountPaid;

                // Portion of the prepayment that has been applied to invoices
                // (total amount invoiced less payments, difference is the deposit applied)
                salesOrder.PrepaymentAmountInvoiced = orderInfo.PreviouslyInvoicedAmount - nonPrepayments;

                // if the prepayment invoiced is greater than the total paid as deposit, there is no credit left
                salesOrder.AvailableDepositAmount = Math.Max(decimal.Zero, salesOrder.PrepaymentAmountPaid - salesOrder.PrepaymentAmountInvoiced);

                salesOrder.HasLoyaltyPayment = orderInfo.HasLoyaltyPayment;
                salesOrder.CurrencyCode      = orderInfo.CurrencyCode;

                return(salesOrder);
            }
Пример #10
0
            /// <summary>
            /// Add balance to existing gift card.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>A response containing the gift card information.</returns>
            private static GetGiftCardServiceResponse AddToGiftCard(AddToGiftCardServiceRequest request)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                if (string.IsNullOrWhiteSpace(request.GiftCardId))
                {
                    throw new ArgumentException("request.Id is null or empty.", "request");
                }

                if (string.IsNullOrWhiteSpace(request.CurrencyCode))
                {
                    throw new ArgumentException("request.Id is null or empty.", "request");
                }

                if (!request.CurrencyCode.Equals(request.RequestContext.GetChannelConfiguration().Currency, StringComparison.OrdinalIgnoreCase))
                {
                    throw new PaymentException(
                              PaymentErrors.Microsoft_Dynamics_Commerce_Runtime_GiftCardCurrencyMismatch,
                              string.Format("Amount cannot be added to gift in currency different from channel currency. Currency requested: {0}", request.CurrencyCode));
                }

                // Skip validation if we are reversing operation.
                if (request.Amount > 0)
                {
                    ValidateNotAlreadyAdded(request.Transaction, request.GiftCardId);
                }

                string   giftCardCurrencyCode;
                GiftCard currentGiftCard;

                if (request.IsReversal)
                {
                    // In case of reversal (voiding gift card sales line) gift card we already know gift card currency
                    // so call to GetGiftCardBalance() can be skipped.
                    giftCardCurrencyCode = request.CardCurrencyCode;
                }
                else
                {
                    var getGiftCardBalanceRequest = new GetGiftCardRealtimeRequest(request.GiftCardId);
                    currentGiftCard      = request.RequestContext.Execute <SingleEntityDataServiceResponse <GiftCard> >(getGiftCardBalanceRequest).Entity;
                    giftCardCurrencyCode = currentGiftCard.CardCurrencyCode;
                }

                GetCurrencyValueServiceResponse convertAmountToGiftCardCurrencyResponse = ToGiftCardCurrency(request.RequestContext, request.Amount, giftCardCurrencyCode);

                var addToGiftCardRequest = new AddToGiftCardRealtimeRequest(
                    request.GiftCardId,
                    convertAmountToGiftCardCurrencyResponse.RoundedConvertedAmount,
                    convertAmountToGiftCardCurrencyResponse.ToCurrencyCode,
                    request.RequestContext.GetPrincipal().ChannelId,
                    request.RequestContext.GetTerminal() == null ? string.Empty : request.RequestContext.GetTerminal().TerminalId,
                    request.RequestContext.GetPrincipal().UserId,
                    request.Transaction.Id,
                    string.Empty); // ReceiptId is passed as an empty string in EPOS too at this point.
                GiftCard card = request.RequestContext.Execute <SingleEntityDataServiceResponse <GiftCard> >(addToGiftCardRequest).Entity;

                GetCurrencyValueServiceResponse convertBalanceToChannelCurrencyResponse = FromGiftCardCurrency(request.RequestContext, card.BalanceInCardCurrency, card.BalanceCurrencyCode);

                GiftCard giftCard = new GiftCard
                {
                    Id = request.GiftCardId,
                    BalanceCurrencyCode   = convertBalanceToChannelCurrencyResponse.ToCurrencyCode,
                    Balance               = convertBalanceToChannelCurrencyResponse.RoundedConvertedAmount,
                    CardCurrencyCode      = giftCardCurrencyCode,
                    BalanceInCardCurrency = card.BalanceInCardCurrency
                };

                return(new GetGiftCardServiceResponse(giftCard));
            }