Exemple #1
0
            /// <summary>
            /// Static entry point to calculate amount paid and due.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="salesTransaction">The sales transaction.</param>
            public static void CalculateAmountPaidAndDue(RequestContext context, SalesTransaction salesTransaction)
            {
                ThrowIf.Null(context, "context");
                ThrowIf.Null(salesTransaction, "salesTransaction");

                decimal paymentRequiredAmount;

                salesTransaction.AmountPaid = SalesTransactionTotaler.GetPaymentsSum(salesTransaction.TenderLines);

                // decides what is expected to be paid for this transaction
                switch (salesTransaction.CartType)
                {
                case CartType.CustomerOrder:
                    paymentRequiredAmount = SalesTransactionTotaler.CalculateRequiredPaymentAmount(context, salesTransaction);
                    break;

                case CartType.Shopping:
                case CartType.Checkout:
                case CartType.AccountDeposit:
                    paymentRequiredAmount = salesTransaction.TotalAmount;
                    break;

                case CartType.IncomeExpense:
                    paymentRequiredAmount = salesTransaction.IncomeExpenseTotalAmount;
                    break;

                default:
                    throw new DataValidationException(
                              DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidRequest,
                              string.Format("SalesTransactionTotaler::CalculateAmountPaidAndDue: CartType '{0}' not supported.", salesTransaction.CartType));
                }

                salesTransaction.SalesPaymentDifference = paymentRequiredAmount - salesTransaction.AmountPaid;
                salesTransaction.AmountDue = paymentRequiredAmount - salesTransaction.AmountPaid;

                TenderLine lastTenderLine = null;

                if (!salesTransaction.TenderLines.IsNullOrEmpty())
                {
                    lastTenderLine = salesTransaction.ActiveTenderLines.LastOrDefault();
                }

                if (lastTenderLine != null)
                {
                    // Calculate the expected (rounded) amount due for last payment.
                    decimal amountDueBeforeLastPayment = paymentRequiredAmount - salesTransaction.ActiveTenderLines.Take(salesTransaction.ActiveTenderLines.Count - 1).Sum(t => t.Amount);
                    GetPaymentRoundedValueServiceRequest roundAmountDueBeforeLastPaymentRequest  = new GetPaymentRoundedValueServiceRequest(amountDueBeforeLastPayment, lastTenderLine.TenderTypeId, isChange: false);
                    GetRoundedValueServiceResponse       roundAmountDueBeforeLastPaymentResponse = context.Execute <GetRoundedValueServiceResponse>(roundAmountDueBeforeLastPaymentRequest);

                    // Set amont due to zero if payment amount equals to expected rounded payment amount. Otherwise another payment should be required (that could use different rounding settings).
                    if (roundAmountDueBeforeLastPaymentResponse.RoundedValue == lastTenderLine.Amount)
                    {
                        salesTransaction.AmountDue = decimal.Zero;
                    }
                }

                // When required amount is positive, amount due must be zero or negative (overtender), otherwise (e.g. for refunds or exchanges) exact amount has to refunded (zero balance).
                salesTransaction.IsRequiredAmountPaid = (paymentRequiredAmount > 0 && salesTransaction.AmountDue <= 0) ||
                                                        (paymentRequiredAmount <= 0 && salesTransaction.AmountDue == 0);
            }
            /// <summary>
            /// Gets the change.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>
            /// A response containing the change tender line.
            /// </returns>
            private static GetChangePaymentServiceResponse GetChange(GetChangePaymentServiceRequest request)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                if (request.ChangeTenderTypeId == null)
                {
                    throw new ArgumentException("request.TenderType is null", "request");
                }

                string creditMemoId = IssueCreditMemo(request.RequestContext, request.ChangeAmount, request.CurrencyCode, request.Transaction.Id, request.Transaction.ReceiptId);

                var changeTenderLine = new TenderLine
                {
                    CreditMemoId = creditMemoId,
                    Amount       = decimal.Negate(request.ChangeAmount), // change tender line must have negative amount
                    Currency     = request.CurrencyCode,
                    TenderLineId = Guid.NewGuid().ToString("N"),
                    TenderTypeId = request.ChangeTenderTypeId,
                    Status       = TenderLineStatus.Committed,
                    IsVoidable   = false,
                    IsChangeLine = true
                };

                return(new GetChangePaymentServiceResponse(changeTenderLine));
            }
            /// <summary>
            /// Gets the change.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>
            /// A response containing the change tender line.
            /// </returns>
            private static GetChangePaymentServiceResponse GetChange(GetChangePaymentServiceRequest request)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                if (string.IsNullOrWhiteSpace(request.ChangeTenderTypeId))
                {
                    throw new PaymentException(PaymentErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidPaymentRequest, "request.TenderTypeId is null or empty.");
                }

                var changeTenderLine = new TenderLine
                {
                    Amount       = decimal.Negate(request.ChangeAmount), // change tender line must have negative amount
                    Currency     = request.CurrencyCode,
                    TenderLineId = Guid.NewGuid().ToString("N"),
                    TenderTypeId = request.ChangeTenderTypeId,
                    Status       = TenderLineStatus.Committed,
                    IsVoidable   = true,
                    IsChangeLine = true
                };

                return(new GetChangePaymentServiceResponse(changeTenderLine));
            }
            /// <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;
            }
            /// <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>
            /// Adds the or update reason code lines.
            /// </summary>
            /// <param name="tenderLine">The tender line.</param>
            /// <param name="cartTenderLine">The cart tender line.</param>
            /// <param name="transactionId">The transaction id.</param>
            public static void AddOrUpdateReasonCodeLinesOnTenderLine(TenderLine tenderLine, TenderLineBase cartTenderLine, string transactionId)
            {
                ThrowIf.Null(tenderLine, "tenderLine");
                ThrowIf.Null(cartTenderLine, "cartTenderLine");

                if (cartTenderLine.ReasonCodeLines != null &&
                    cartTenderLine.ReasonCodeLines.Any())
                {
                    AddOrUpdateReasonCodeLinesHelper(
                        reasonCodeLinesToUpdate: tenderLine.ReasonCodeLines,
                        reasonCodeLines: cartTenderLine.ReasonCodeLines,
                        transactionId: transactionId,
                        parentLineId: tenderLine.TenderLineId,
                        reasonCodeLineType: ReasonCodeLineType.Payment);
                }
            }
Exemple #7
0
            /// <summary>
            /// Calculate specific reason codes on a tender line and add them to the incoming collection.
            /// </summary>
            /// <param name="request">The request object.</param>
            /// <param name="requiredReasonCodes">The collection to which required reason codes are added.</param>
            /// <param name="reasonCodeRequirements">The required specific reason codes map.</param>
            /// <param name="tenderLine">The tenderLine on which to calculate required reason codes.</param>
            private static void CalculateRequiredSpecificReasonCodesOnTenderLine(
                CalculateRequiredReasonCodesServiceRequest request,
                IDictionary <string, ReasonCode> requiredReasonCodes,
                HashSet <ReasonCodeRequirement> reasonCodeRequirements,
                TenderLine tenderLine)
            {
                if (tenderLine.Status == TenderLineStatus.Historical)
                {
                    return;
                }

                // if tenderline is a card, refrelation3 becomes tenderline.cardtypeid and tablerefid is creditcard
                var getChannelTenderTypesDataRequest = new GetChannelTenderTypesDataRequest(request.RequestContext.GetPrincipal().ChannelId, QueryResultSettings.AllRecords);
                var tenderTypes = request.RequestContext.Runtime.Execute <EntityDataServiceResponse <TenderType> >(getChannelTenderTypesDataRequest, request.RequestContext).PagedEntityCollection.Results;

                ReasonCodeTableRefType tableRef = ReasonCodeTableRefType.Tender;
                string refRelation3             = string.Empty;

                TenderType tenderType = tenderTypes.Where(type => type.TenderTypeId == tenderLine.TenderTypeId).SingleOrDefault();

                if (tenderType.OperationId == (int)RetailOperation.PayCard)
                {
                    refRelation3 = tenderLine.CardTypeId;
                    tableRef     = ReasonCodeTableRefType.CreditCard;
                }

                CalculateReasonCodesSpecificToEntity(
                    request,
                    tenderLine.TenderTypeId,
                    tableRef,
                    request.SalesTransaction.StoreId,
                    tenderLine.TenderTypeId,
                    refRelation3,
                    requiredReasonCodes,
                    reasonCodeRequirements,
                    tenderLine.ReasonCodeLines,
                    null);
            }
 /// <summary>
 /// Gets a value indicating whether a tender is card refund.
 /// </summary>
 /// <param name="tenderType">The tender type.</param>
 /// <param name="tenderLine">The tender line.</param>
 /// <returns>The result value.</returns>
 private static bool IsCardRefund(TenderType tenderType, TenderLine tenderLine)
 {
     return(tenderType.OperationType == RetailOperation.PayCard && tenderLine.Amount < 0);
 }
            /// <summary>
            /// Validate that all payments made with same tender type do not exceed limits defined for tender type.
            /// </summary>
            /// <param name="salesTransaction">Sales transaction.</param>
            /// <param name="tenderLine">Tender line to validate.</param>
            /// <param name="tenderType">Tender type information.</param>
            private static void ValidateTransactionLimits(SalesTransaction salesTransaction, TenderLine tenderLine, TenderType tenderType)
            {
                if (salesTransaction == null)
                {
                    throw new ArgumentNullException("salesTransaction");
                }

                if (tenderLine == null)
                {
                    throw new ArgumentNullException("tenderLine");
                }

                if (tenderType == null)
                {
                    throw new ArgumentNullException("tenderType");
                }

                decimal amountAlreadyPaidWithSameTenderType = salesTransaction.TenderLines.Where(t => t.Status != TenderLineStatus.Voided && t.TenderTypeId == tenderLine.TenderTypeId).Sum(t => t.Amount);
                decimal totalAmountPaidWithTenderType       = tenderLine.Amount + amountAlreadyPaidWithSameTenderType;

                var validationFailures = new Collection <DataValidationFailure>();

                // Card refund does not honor MaximumAmountPerTransaction.
                // Only check the limit when the tender is not card refund.
                if (!IsCardRefund(tenderType, tenderLine))
                {
                    if (tenderType.MaximumAmountPerTransaction > 0 && Math.Abs(totalAmountPaidWithTenderType) > tenderType.MaximumAmountPerTransaction)
                    {
                        validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_PaymentExceedsMaximumAmountPerTransaction, "tenderLine.Amount"));
                    }
                }

                if (validationFailures.Any())
                {
                    throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_AggregateValidationError, validationFailures, "Payment amount exceeds limits defined per transaction.");
                }
            }
            /// <summary>
            /// Validates that tender line fulfills limits configured for card type.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="tenderLine">Payment information.</param>
            /// <returns>Collection of <see cref="DataValidationFailure"/>.</returns>
            private static IEnumerable <DataValidationFailure> ValidateCardTypeLimits(RequestContext context, TenderLine tenderLine)
            {
                List <DataValidationFailure> failures = new List <DataValidationFailure>();

                CardTypeInfo cardTypeInfo = CardTypeHelper.GetCardTypeConfiguration(tenderLine.CardTypeId, context);

                if (cardTypeInfo == null)
                {
                    failures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound, "Card type with id '{0}' not found", tenderLine.CardTypeId));
                }

                if (tenderLine.CashBackAmount < 0)
                {
                    failures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidCashBackAmount, "Cash back amount cannot be negative."));
                }

                if (tenderLine.CashBackAmount != 0 && tenderLine.Amount < 0)
                {
                    failures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_AmountDueMustBePaidBeforeCheckout, "Cash back not allowed for refunds."));
                }

                if (cardTypeInfo != null && tenderLine.CashBackAmount > cardTypeInfo.CashBackLimit)
                {
                    string message = string.Format("Cash back amount for card type '{0}' exceed maximum allowed value {1}.", cardTypeInfo.TypeId, cardTypeInfo.CashBackLimit);
                    failures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidCashBackAmount, message));
                }

                return(failures);
            }
            /// <summary>
            /// Validate that amount on tender line do not exceed limits defined for tender type.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="salesTransaction">Sales transaction.</param>
            /// <param name="tenderLine">Tender line to validate.</param>
            /// <param name="tenderType">Tender type information.</param>
            private static void ValidateTenderLineLimits(RequestContext context, SalesTransaction salesTransaction, TenderLine tenderLine, TenderType tenderType)
            {
                if (salesTransaction == null)
                {
                    throw new ArgumentNullException("salesTransaction");
                }

                if (tenderLine == null)
                {
                    throw new ArgumentNullException("tenderLine");
                }

                if (tenderType == null)
                {
                    throw new ArgumentNullException("tenderType");
                }

                decimal amountDue = RoundAmountByTenderType(context, tenderLine.TenderTypeId, salesTransaction.AmountDue, isChange: false);

                var validationFailures = new Collection <DataValidationFailure>();

                if (tenderLine.Amount != 0 && (Math.Sign(amountDue) != Math.Sign(tenderLine.Amount)))
                {
                    validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_IncorrectPaymentAmountSign, "tenderLine.Amount"));
                }

                if (tenderType.MaximumAmountPerLine > 0 && Math.Abs(tenderLine.Amount) > tenderType.MaximumAmountPerLine)
                {
                    // 1396 = The maximum amount allowed is:
                    validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_PaymentExceedsMaximumAmountPerLine, "tenderLine.Amount"));
                }

                if (tenderType.MinimumAmountPerLine > 0 && Math.Abs(tenderLine.Amount) < tenderType.MinimumAmountPerLine)
                {
                    // 1397 = The minimum amount allowed is:
                    validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_PaymentExceedsMinimumAmountPerLine, "tenderLine.Amount"));
                }

                // Card refund does not honor overtender/undertender limits.
                // Only check those limits when the tender is not card refund.
                if (!IsCardRefund(tenderType, tenderLine))
                {
                    if (Math.Abs(amountDue) < Math.Abs(tenderLine.Amount))
                    {
                        if (!tenderType.IsOvertenderAllowed)
                        {
                            // 1391 = No change allowed:
                            validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ChangebackIsNotAllowed, "tenderLine.Amount"));
                        }

                        if (tenderType.MaximumOvertenderAmount > 0 &&
                            tenderType.MaximumOvertenderAmount < (Math.Abs(tenderLine.Amount) - Math.Abs(amountDue)))
                        {
                            validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_OvertenderAmountExceedsMaximumAllowedValue, "tenderLine.Amount"));
                        }
                    }
                    else if (Math.Abs(amountDue) > Math.Abs(tenderLine.Amount))
                    {
                        if (!tenderType.IsUndertenderAllowed)
                        {
                            // 1394 = This payment must be used to finalize the transaction:
                            validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_PaymentMustBeUsedToFinalizeTransaction, "tenderLine.Amount"));
                        }

                        if (tenderType.MaximumUndertenderAmount > 0 && (Math.Abs(amountDue) - Math.Abs(tenderLine.Amount)) > tenderType.MaximumUndertenderAmount)
                        {
                            validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_BalanceAmountExceedsMaximumAllowedValue, "tenderLine.Amount"));
                        }

                        if (tenderLine.CashBackAmount > 0)
                        {
                            validationFailures.Add(new DataValidationFailure(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_PaymentMustBeUsedToFinalizeTransaction, "tenderLine.Amount"));
                        }
                    }
                }

                if (tenderType.OperationType == RetailOperation.PayCard)
                {
                    validationFailures.AddRange(ValidateCardTypeLimits(context, tenderLine));
                }

                if (validationFailures.Any())
                {
                    throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_AggregateValidationError, validationFailures, "Payment amount exceeds limits defined per transaction.");
                }
            }
Exemple #12
0
            /// <summary>
            /// Validates sufficient funds on account.
            /// </summary>
            /// <param name="context">The service request context.</param>
            /// <param name="customer">The customer.</param>
            /// <param name="useInvoiceAccount">Whether to use invoice account.</param>
            /// <param name="tenderLine">The tender line.</param>
            private static void CheckIfPaymentExceedsBalance(RequestContext context, Customer customer, bool useInvoiceAccount, TenderLine tenderLine)
            {
                // Search location set to all in order to retrieve pending transactions anchor from AX
                var localBalanceServiceRequest = new GetCustomerBalanceServiceRequest(
                    customer.AccountNumber,
                    customer.InvoiceAccount,
                    SearchLocation.All);

                CustomerBalances pendingAccountBalances = context.Execute <GetCustomerBalanceServiceResponse>(localBalanceServiceRequest).Balance;

                // Total amount to verify is the sum of amount on the tender line and pending customer account balance
                // where pending balances = (tendered amounts - any deposits made) which is not yet uploaded to AX.
                decimal amountToVerify = tenderLine.Amount + (useInvoiceAccount ? pendingAccountBalances.InvoiceAccountPendingBalance : pendingAccountBalances.PendingBalance);

                var validateCustomerAccountPaymentRealtimeRequest = new ValidateCustomerAccountPaymentRealtimeRequest(tenderLine.CustomerId, amountToVerify, tenderLine.Currency);

                context.Execute <NullResponse>(validateCustomerAccountPaymentRealtimeRequest);
            }
Exemple #13
0
            /// <summary>
            /// Authorizes the payment.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>A response containing the authorized tender line.</returns>
            private static AuthorizePaymentServiceResponse AuthorizePayment(AuthorizePaymentServiceRequest request)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                TenderLine tenderLine = request.TenderLine;

                if (tenderLine == null)
                {
                    throw new PaymentException(PaymentErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidPaymentRequest, "Customer account payment requires tender line.");
                }

                if (string.IsNullOrWhiteSpace(tenderLine.CustomerId))
                {
                    throw new PaymentException(PaymentErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidPaymentRequest, "Customer account payment requires CustomerId to be set on tender line.");
                }

                SalesTransaction transaction = request.Transaction;

                if (transaction.CartType == CartType.CustomerOrder &&
                    (transaction.CustomerOrderMode == CustomerOrderMode.CustomerOrderCreateOrEdit || transaction.CustomerOrderMode == CustomerOrderMode.Cancellation))
                {
                    throw new PaymentException(
                              PaymentErrors.Microsoft_Dynamics_Commerce_Runtime_CustomerAccountPaymentIsNotAllowedForCustomerOrderDepositAndCancellation,
                              string.Format("Customer account payment cannot be used to pay customer order deposit or cancellation (current mode {0}).", transaction.CustomerOrderMode));
                }

                if (string.IsNullOrWhiteSpace(transaction.CustomerId))
                {
                    throw new PaymentException(PaymentErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidPaymentRequest, "Customer account payment requires CustomerId to be set on cart.");
                }

                Customer customerOnTransaction = GetCustomer(request.RequestContext, transaction.CustomerId);

                if (customerOnTransaction == null)
                {
                    throw new PaymentException(
                              PaymentErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidPaymentRequest,
                              string.Format(CultureInfo.InvariantCulture, "Customer with id {0} was not found.", transaction.CustomerId));
                }

                ValidateCustomerForPayment(customerOnTransaction);

                bool   useInvoiceAccount       = !string.IsNullOrWhiteSpace(customerOnTransaction.InvoiceAccount);
                string customerAccountToCharge = useInvoiceAccount ? customerOnTransaction.InvoiceAccount : customerOnTransaction.AccountNumber;

                if (!tenderLine.CustomerId.Equals(customerAccountToCharge, StringComparison.OrdinalIgnoreCase))
                {
                    // Someone is trying to pay with unathorized account
                    throw new PaymentException(PaymentErrors.Microsoft_Dynamics_Commerce_Runtime_PaymentUsingUnauthorizedAccount, "Customer account payment requires its own account or matching invoice account on a tender line.");
                }

                CheckIfPaymentExceedsBalance(request.RequestContext, customerOnTransaction, useInvoiceAccount, tenderLine);

                // Looks like request has successfully validated
                tenderLine.Status     = TenderLineStatus.Committed;
                tenderLine.IsVoidable = true;

                return(new AuthorizePaymentServiceResponse(tenderLine));
            }
Exemple #14
0
 public Task <Cart> AddPreprocessedTenderLine(string id, TenderLine preprocessedTenderLine)
 {
     return(Task.Run(() => OrderManager.Create(CommerceRuntimeManager.Runtime).AddTenderLine(id, preprocessedTenderLine)));
 }
Exemple #15
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);
            }
            /// <summary>
            /// Calculates the required reason codes on tender line.
            /// </summary>
            /// <param name="requestContext">The request context.</param>
            /// <param name="transaction">Current transaction.</param>
            /// <param name="tenderLine">The tender line.</param>
            /// <param name="sourceType">Type of the source.</param>
            public static void CalculateRequiredReasonCodesOnTenderLine(RequestContext requestContext, SalesTransaction transaction, TenderLine tenderLine, ReasonCodeSourceType sourceType)
            {
                ThrowIf.Null(requestContext, "requestContext");
                ThrowIf.Null(tenderLine, "tenderLine");

                var serviceRequest = new CalculateRequiredReasonCodesServiceRequest(transaction, sourceType, new[] { tenderLine });

                CalculateRequiredReasonCodesHelper(requestContext, serviceRequest);
            }
Exemple #17
0
 public Task ValidateTenderLineForAdd(string id, TenderLine tenderLine)
 {
     return(Task.Run(() => OrderManager.Create(CommerceRuntimeManager.Runtime).ValidateTenderLineForAdd(id, tenderLine)));
 }