コード例 #1
0
        /// <summary>
        /// Normalizes an order to purchase net new subscriptions.
        /// </summary>
        /// <returns>Normalized order.</returns>
        public async Task <OrderViewModel> NormalizePurchaseSubscriptionOrderAsync()
        {
            OrderViewModel order = this.Order;

            order.CustomerId.AssertNotEmpty(nameof(order.CustomerId));
            if (order.OperationType != CommerceOperationType.NewPurchase)
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, Resources.InvalidOperationForOrderMessage).AddDetail("Field", "OperationType");
            }

            // create result order object prefilling it with operation type & customer id.
            OrderViewModel orderResult = new OrderViewModel()
            {
                CustomerId    = order.CustomerId,
                OrderId       = order.OrderId,
                OperationType = order.OperationType
            };

            order.Subscriptions.AssertNotNull(nameof(order.Subscriptions));
            List <OrderSubscriptionItemViewModel> orderSubscriptions = order.Subscriptions.ToList();

            if (orderSubscriptions.Count < 1)
            {
                throw new ArgumentException(Resources.NotEnoughItemsInOrderErrorMessage, nameof(order.Subscriptions));
            }

            // retrieve all the partner offers to match against them
            IEnumerable <PartnerOffer> allPartnerOffers = await ApplicationDomain.Instance.OffersRepository.RetrieveAsync();

            List <OrderSubscriptionItemViewModel> resultOrderSubscriptions = new List <OrderSubscriptionItemViewModel>();

            foreach (var lineItem in orderSubscriptions)
            {
                PartnerOffer offerToPurchase = allPartnerOffers.Where(offer => offer.Id == lineItem.SubscriptionId).FirstOrDefault();

                if (offerToPurchase == null)
                {
                    // oops, this offer Id is unknown to us
                    throw new PartnerDomainException(ErrorCode.PartnerOfferNotFound).AddDetail("Id", lineItem.SubscriptionId);
                }
                else if (offerToPurchase.IsInactive)
                {
                    // purchasing deleted offers is prohibited
                    throw new PartnerDomainException(ErrorCode.PurchaseDeletedOfferNotAllowed).AddDetail("Id", offerToPurchase.Id);
                }

                // populate details for each order subscription item to purchase.
                resultOrderSubscriptions.Add(new OrderSubscriptionItemViewModel()
                {
                    OfferId          = offerToPurchase.Id,
                    SubscriptionId   = offerToPurchase.Id,
                    Quantity         = lineItem.Quantity,
                    SeatPrice        = offerToPurchase.Price,
                    SubscriptionName = offerToPurchase.Title
                });
            }

            orderResult.Subscriptions = resultOrderSubscriptions;
            return(await Task.FromResult(orderResult));
        }
        /// <summary>
        /// Updates an existing partner offer.
        /// </summary>
        /// <param name="partnerOfferUpdate">The partner offer to update.</param>
        /// <returns>The updated partner offer.</returns>
        public async Task <PartnerOffer> UpdateAsync(PartnerOffer partnerOfferUpdate)
        {
            if (partnerOfferUpdate == null)
            {
                throw new ArgumentNullException(nameof(partnerOfferUpdate));
            }

            IList <PartnerOffer> allPartnerOffers = new List <PartnerOffer>(await this.RetrieveAsync());

            new PartnerOfferNormalizer().Normalize(partnerOfferUpdate);

            var existingPartnerOffer = allPartnerOffers.Where(offer => offer.Id == partnerOfferUpdate.Id).FirstOrDefault();

            if (existingPartnerOffer == null)
            {
                throw new PartnerDomainException(ErrorCode.PartnerOfferNotFound, "Offer not found");
            }

            if (existingPartnerOffer.MicrosoftOfferId != partnerOfferUpdate.MicrosoftOfferId)
            {
                // we do not allow changing the Microsoft offer association since there may be existing purchases that purchased the original Microsoft offer
                throw new PartnerDomainException(ErrorCode.MicrosoftOfferImmutable, "Microsoft offer is not allowed to be updated. Create a new offer instead.");
            }

            allPartnerOffers[allPartnerOffers.IndexOf(existingPartnerOffer)] = partnerOfferUpdate;

            await this.UpdateAsync(allPartnerOffers);

            return(partnerOfferUpdate);
        }
        /// <summary>
        /// Applies business rules to a partner offer.
        /// </summary>
        /// <param name="partnerOffer">The partner offer to normalize.</param>
        public void Normalize(PartnerOffer partnerOffer)
        {
            partnerOffer.AssertNotNull(nameof(partnerOffer));

            // ensure the Microsoft offer ID and other required properties are set
            Guid offerId;

            if (!Guid.TryParse(partnerOffer.Id, out offerId))
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, Resources.IdMustBeAValidGUID).AddDetail("Field", "Id");
            }

            if (string.IsNullOrWhiteSpace(partnerOffer.MicrosoftOfferId))
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, Resources.MicrosoftOfferIdMustBeSet).AddDetail("Field", "MicrosoftOfferId");
            }

            partnerOffer.Title.AssertNotEmpty("Offer title");

            if (partnerOffer.Price <= 0)
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, Resources.OfferPriceShouldBeMoreThanZero).AddDetail("Field", "Price");
            }

            // flatten the offer price based on locale decimal settings.
            partnerOffer.Price = Math.Round(partnerOffer.Price, Resources.Culture.NumberFormat.CurrencyDecimalDigits, MidpointRounding.AwayFromZero);

            partnerOffer.Features = PartnerOfferNormalizer.CleanupEmptyEntries(partnerOffer.Features);
            partnerOffer.Summary  = PartnerOfferNormalizer.CleanupEmptyEntries(partnerOffer.Summary);
        }
コード例 #4
0
        /// <summary>
        /// Binds each purchase line item with the partner offer it is requesting.
        /// </summary>
        /// <param name="purchaseLineItems">A collection of purchase line items.</param>
        /// <returns>The requested association.</returns>
        private async Task <IEnumerable <PurchaseLineItemWithOffer> > AssociateWithPartnerOffersAsync(IEnumerable <PurchaseLineItem> purchaseLineItems)
        {
            // retrieve all the partner offers to match against them
            IEnumerable <PartnerOffer> allPartnerOffers = await this.ApplicationDomain.OffersRepository.RetrieveAsync();

            ICollection <PurchaseLineItemWithOffer> lineItemToOfferAssociations = new List <PurchaseLineItemWithOffer>();

            foreach (var lineItem in purchaseLineItems)
            {
                if (lineItem == null)
                {
                    throw new ArgumentException("a line item is null");
                }

                PartnerOffer offerToPurchase = allPartnerOffers.Where(offer => offer.Id == lineItem.PartnerOfferId).FirstOrDefault();

                if (offerToPurchase == null)
                {
                    // oops, this offer Id is unknown to us
                    throw new PartnerDomainException(ErrorCode.PartnerOfferNotFound).AddDetail("Id", lineItem.PartnerOfferId);
                }
                else if (offerToPurchase.IsInactive)
                {
                    // purchasing deleted offers is prohibited
                    throw new PartnerDomainException(ErrorCode.PurchaseDeletedOfferNotAllowed).AddDetail("Id", offerToPurchase.Id);
                }

                // associate the line item with the partner offer
                lineItemToOfferAssociations.Add(new PurchaseLineItemWithOffer(lineItem, offerToPurchase));
            }

            return(lineItemToOfferAssociations);
        }
コード例 #5
0
        /// <summary>
        /// Updates an existing partner offer.
        /// </summary>
        /// <param name="partnerOfferUpdate">The partner offer to update.</param>
        /// <returns>The updated partner offer.</returns>
        public async Task <PartnerOffer> UpdateAsync(PartnerOffer partnerOfferUpdate)
        {
            partnerOfferUpdate.AssertNotNull(nameof(partnerOfferUpdate));

            IList <PartnerOffer> allPartnerOffers = new List <PartnerOffer>(await RetrieveAsync().ConfigureAwait(false));

            new PartnerOfferNormalizer().Normalize(partnerOfferUpdate);

            PartnerOffer existingPartnerOffer = allPartnerOffers.FirstOrDefault(offer => offer.Id == partnerOfferUpdate.Id);

            if (existingPartnerOffer == null)
            {
                throw new PartnerDomainException(ErrorCode.PartnerOfferNotFound, Resources.OfferNotFound);
            }

            if (existingPartnerOffer.MicrosoftOfferId != partnerOfferUpdate.MicrosoftOfferId)
            {
                // we do not allow changing the Microsoft offer association since there may be existing purchases that purchased the original Microsoft offer
                throw new PartnerDomainException(ErrorCode.MicrosoftOfferImmutable, Resources.MicrosoftOfferImmutableErrorMessage);
            }

            allPartnerOffers[allPartnerOffers.IndexOf(existingPartnerOffer)] = partnerOfferUpdate;

            await UpdateAsync(allPartnerOffers).ConfigureAwait(false);

            return(partnerOfferUpdate);
        }
コード例 #6
0
        /// <summary>
        /// Applies business rules to a partner offer.
        /// </summary>
        /// <param name="partnerOffer">The partner offer to normalize.</param>
        public void Normalize(PartnerOffer partnerOffer)
        {
            partnerOffer.AssertNotNull(nameof(partnerOffer));

            // ensure the Microsoft offer ID and other required properties are set
            Guid offerId;

            if (!Guid.TryParse(partnerOffer.Id, out offerId))
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, "Id must be a valid GUID").AddDetail("Field", "Id");
            }

            if (string.IsNullOrWhiteSpace(partnerOffer.MicrosoftOfferId))
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, "MicrosoftOfferId must be set").AddDetail("Field", "MicrosoftOfferId");
            }

            partnerOffer.Title.AssertNotEmpty("Offer title");

            if (partnerOffer.Price <= 0)
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, "Offer price should be more than zero").AddDetail("Field", "Price");
            }

            partnerOffer.Features = PartnerOfferNormalizer.CleanupEmptyEntries(partnerOffer.Features);
            partnerOffer.Summary  = PartnerOfferNormalizer.CleanupEmptyEntries(partnerOffer.Summary);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="PurchaseLineItemWithOffer"/> class.
        /// </summary>
        /// <param name="purchaseLineItem">The purchase line item.</param>
        /// <param name="partnerOffer">The partner offer.</param>
        public PurchaseLineItemWithOffer(PurchaseLineItem purchaseLineItem, PartnerOffer partnerOffer)
        {
            purchaseLineItem.AssertNotNull(nameof(purchaseLineItem));
            partnerOffer.AssertNotNull(nameof(partnerOffer));

            this.PurchaseLineItem = purchaseLineItem;
            this.PartnerOffer     = partnerOffer;
        }
コード例 #8
0
        /// <summary>
        /// Retrieves a specific partner offer using its ID.
        /// </summary>
        /// <param name="partnerOfferId">The ID of the partner offer to look for.</param>
        /// <returns>The matching partner offer.</returns>
        public async Task <PartnerOffer> RetrieveAsync(string partnerOfferId)
        {
            partnerOfferId.AssertNotEmpty(nameof(partnerOfferId));
            PartnerOffer matchingPartnerOffer = (await RetrieveAsync().ConfigureAwait(false)).FirstOrDefault(offer => offer.Id == partnerOfferId);

            if (matchingPartnerOffer != null)
            {
                return(matchingPartnerOffer);
            }
            else
            {
                throw new PartnerDomainException(ErrorCode.PartnerOfferNotFound, Resources.OfferNotFound);
            }
        }
        /// <summary>
        /// Retrieves a specific partner offer using its ID.
        /// </summary>
        /// <param name="partnerOfferId">The ID of the partner offer to look for.</param>
        /// <returns>The matching partner offer.</returns>
        public async Task <PartnerOffer> RetrieveAsync(string partnerOfferId)
        {
            partnerOfferId.AssertNotEmpty(nameof(partnerOfferId));
            PartnerOffer matchingPartnerOffer = (await this.RetrieveAsync()).Where(offer => offer.Id == partnerOfferId).FirstOrDefault();

            if (matchingPartnerOffer != null)
            {
                return(matchingPartnerOffer);
            }
            else
            {
                throw new PartnerDomainException(ErrorCode.PartnerOfferNotFound, "Offer not found");
            }
        }
コード例 #10
0
        /// <summary>
        /// Adds a new partner offer to the repository.
        /// </summary>
        /// <param name="newPartnerOffer">The partner offer to add.</param>
        /// <returns>The added partner offer.</returns>
        public async Task <PartnerOffer> AddAsync(PartnerOffer newPartnerOffer)
        {
            newPartnerOffer.AssertNotNull(nameof(newPartnerOffer));

            newPartnerOffer.Id = Guid.NewGuid().ToString();

            ICollection <PartnerOffer> allPartnerOffers = new List <PartnerOffer>(await RetrieveAsync().ConfigureAwait(false));

            new PartnerOfferNormalizer().Normalize(newPartnerOffer);
            allPartnerOffers.Add(newPartnerOffer);

            await UpdateAsync(allPartnerOffers).ConfigureAwait(false);

            return(newPartnerOffer);
        }
コード例 #11
0
        /// <summary>
        /// Adds a new partner offer to the repository.
        /// </summary>
        /// <param name="newPartnerOffer">The partner offer to add.</param>
        /// <returns>The added partner offer.</returns>
        public async Task <PartnerOffer> AddAsync(PartnerOffer newPartnerOffer)
        {
            if (newPartnerOffer == null)
            {
                throw new ArgumentNullException(nameof(newPartnerOffer));
            }

            newPartnerOffer.Id = Guid.NewGuid().ToString();

            ICollection <PartnerOffer> allPartnerOffers = new List <PartnerOffer>(await RetrieveAsync().ConfigureAwait(false));

            new PartnerOfferNormalizer().Normalize(newPartnerOffer);
            allPartnerOffers.Add(newPartnerOffer);

            await UpdateAsync(allPartnerOffers).ConfigureAwait(false);

            return(newPartnerOffer);
        }
コード例 #12
0
        /// <summary>
        /// Records all the resulting subscriptions as well as their initial purchase history into persistence.
        /// </summary>
        /// <returns>A task.</returns>
        public async Task ExecuteAsync()
        {
            Tuple <Order, IEnumerable <PurchaseLineItemWithOffer> > inputs = AcquireInput.Invoke();
            Order partnerCenterPurchaseOrder = inputs.Item1;
            IEnumerable <PurchaseLineItemWithOffer> purchaseLineItems = inputs.Item2;

            ICollection <TransactionResultLineItem> transactionResultLineItems = new List <TransactionResultLineItem>();
            ICollection <IBusinessTransaction>      persistenceTransactions    = new List <IBusinessTransaction>();

            DateTime rightNow = DateTime.UtcNow;

            foreach (OrderLineItem orderLineItem in partnerCenterPurchaseOrder.LineItems)
            {
                PartnerOffer matchingPartnerOffer = purchaseLineItems.ElementAt(orderLineItem.LineItemNumber).PartnerOffer;

                // add a record new customer subscription transaction for the current line item
                persistenceTransactions.Add(new RecordNewCustomerSubscription(
                                                CustomerSubscriptionsRepository,
                                                new CustomerSubscriptionEntity(CustomerId, orderLineItem.SubscriptionId, matchingPartnerOffer.Id, rightNow.AddYears(1))));

                // add a record purchase history for the current line item
                persistenceTransactions.Add(new RecordPurchase(
                                                CustomerPurchasesRepository,
                                                new CustomerPurchaseEntity(CommerceOperationType.NewPurchase, Guid.NewGuid().ToString(), CustomerId, orderLineItem.SubscriptionId, orderLineItem.Quantity, matchingPartnerOffer.Price, rightNow)));

                // build the transaction result line item
                transactionResultLineItems.Add(new TransactionResultLineItem(
                                                   orderLineItem.SubscriptionId,
                                                   matchingPartnerOffer.Id,
                                                   orderLineItem.Quantity,
                                                   matchingPartnerOffer.Price,
                                                   matchingPartnerOffer.Price * orderLineItem.Quantity));
            }

            // bundle up all the transactions together
            bulkSubscriptionPersistenceTransaction = new SequentialAggregateTransaction(persistenceTransactions);

            // execute it!
            await bulkSubscriptionPersistenceTransaction.ExecuteAsync().ConfigureAwait(false);

            // store the reuslting transaction line items
            Result = transactionResultLineItems;
        }
コード例 #13
0
        /// <summary>
        /// Binds each purchase line item with the partner offer it is requesting.
        /// </summary>
        /// <param name="purchaseLineItems">A collection of purchase line items.</param>
        /// <returns>The requested association.</returns>
        private async Task <IEnumerable <PurchaseLineItemWithOffer> > AssociateWithPartnerOffersAsync(IEnumerable <PurchaseLineItem> purchaseLineItems)
        {
            // retrieve all the partner offers to match against them
            IEnumerable <PartnerOffer> allPartnerOffers = await ApplicationDomain.OffersRepository.RetrieveAsync().ConfigureAwait(false);

            ICollection <PurchaseLineItemWithOffer> lineItemToOfferAssociations = new List <PurchaseLineItemWithOffer>();

            foreach (PurchaseLineItem lineItem in purchaseLineItems)
            {
                if (lineItem == null)
                {
                    throw new ArgumentException("a line item is null");
                }

                PartnerOffer offerToPurchase = allPartnerOffers.FirstOrDefault(offer => offer.Id == lineItem.PartnerOfferId);

                // associate the line item with the partner offer
                lineItemToOfferAssociations.Add(new PurchaseLineItemWithOffer(lineItem, offerToPurchase));
            }

            return(lineItemToOfferAssociations);
        }
コード例 #14
0
 public async Task <PartnerOffer> UpdateOffer(PartnerOffer partnerOffer)
 {
     return(await ApplicationDomain.Instance.OffersRepository.UpdateAsync(partnerOffer).ConfigureAwait(false));
 }
コード例 #15
0
 public async Task <PartnerOffer> AddOffer(PartnerOffer newPartnerOffer)
 {
     return(await ApplicationDomain.Instance.OffersRepository.AddAsync(newPartnerOffer).ConfigureAwait(false));
 }
コード例 #16
0
        /// <summary>
        /// Gets the summary of subscriptions for a portal customer.
        /// </summary>
        /// <param name="customerId">The customer Id.</param>
        /// <returns>Subscription Summary.</returns>
        private async Task <SubscriptionsSummary> GetSubscriptionSummaryAsync(string customerId)
        {
            DateTime startTime = DateTime.Now;
            IEnumerable <CustomerSubscriptionEntity> customerSubscriptions = await ApplicationDomain.Instance.CustomerSubscriptionsRepository.RetrieveAsync(customerId).ConfigureAwait(false);

            IEnumerable <CustomerPurchaseEntity> customerSubscriptionsHistory = await ApplicationDomain.Instance.CustomerPurchasesRepository.RetrieveAsync(customerId).ConfigureAwait(false);

            IEnumerable <PartnerOffer> allPartnerOffers = await ApplicationDomain.Instance.OffersRepository.RetrieveAsync().ConfigureAwait(false);

            IEnumerable <MicrosoftOffer> currentMicrosoftOffers = await ApplicationDomain.Instance.OffersRepository.RetrieveMicrosoftOffersAsync().ConfigureAwait(false);

            // start building the summary.
            decimal summaryTotal = 0;

            // format all responses to client using portal locale.
            CultureInfo responseCulture = new CultureInfo(ApplicationDomain.Instance.PortalLocalization.Locale);
            List <SubscriptionViewModel> customerSubscriptionsView = new List <SubscriptionViewModel>();

            // iterate through and build the list of customer's subscriptions.
            foreach (CustomerSubscriptionEntity subscription in customerSubscriptions)
            {
                decimal subscriptionTotal = 0;
                int     licenseTotal      = 0;
                List <SubscriptionHistory> historyItems = new List <SubscriptionHistory>();

                // collect the list of history items for this subcription.
                IOrderedEnumerable <CustomerPurchaseEntity> subscriptionHistoryList = customerSubscriptionsHistory
                                                                                      .Where(historyItem => historyItem.SubscriptionId == subscription.SubscriptionId)
                                                                                      .OrderBy(historyItem => historyItem.TransactionDate);

                // iterate through and build the SubsriptionHistory for this subscription.
                foreach (CustomerPurchaseEntity historyItem in subscriptionHistoryList)
                {
                    decimal orderTotal = Math.Round(historyItem.SeatPrice * historyItem.SeatsBought, responseCulture.NumberFormat.CurrencyDecimalDigits);
                    historyItems.Add(new SubscriptionHistory()
                    {
                        OrderTotal    = orderTotal.ToString("C", responseCulture),                                // Currency format.
                        PricePerSeat  = historyItem.SeatPrice.ToString("C", responseCulture),                     // Currency format.
                        SeatsBought   = historyItem.SeatsBought.ToString("G", responseCulture),                   // General format.
                        OrderDate     = historyItem.TransactionDate.ToLocalTime().ToString("d", responseCulture), // Short date format.
                        OperationType = GetOperationType(historyItem.PurchaseType)                                // Localized Operation type string.
                    });

                    // Increment the subscription total.
                    licenseTotal += historyItem.SeatsBought;

                    // Increment the subscription total.
                    subscriptionTotal += orderTotal;
                }

                PartnerOffer partnerOfferItem  = allPartnerOffers.FirstOrDefault(offer => offer.Id == subscription.PartnerOfferId);
                string       subscriptionTitle = partnerOfferItem.Title;
                string       portalOfferId     = partnerOfferItem.Id;
                decimal      portalOfferPrice  = partnerOfferItem.Price;

                DateTime subscriptionExpiryDate = subscription.ExpiryDate.ToUniversalTime();
                int      remainingDays          = (subscriptionExpiryDate.Date - DateTime.UtcNow.Date).Days;
                bool     isRenewable            = remainingDays <= 30;
                bool     isEditable             = DateTime.UtcNow.Date <= subscriptionExpiryDate.Date;

                // TODO :: Handle Microsoft offer being pulled back due to EOL.

                // Temporarily mark this partnerOffer item as inactive and dont allow store front customer to manage this subscription.
                MicrosoftOffer alignedMicrosoftOffer = currentMicrosoftOffers.FirstOrDefault(offer => offer.Offer.Id == partnerOfferItem.MicrosoftOfferId);
                if (alignedMicrosoftOffer == null)
                {
                    partnerOfferItem.IsInactive = true;
                }

                if (partnerOfferItem.IsInactive)
                {
                    // in case the offer is inactive (marked for deletion) then dont allow renewals or editing on this subscription tied to this offer.
                    isRenewable = false;
                    isEditable  = false;
                }

                // Compute the pro rated price per seat for this subcription & return for client side processing during updates.
                decimal proratedPerSeatPrice = Math.Round(CommerceOperations.CalculateProratedSeatCharge(subscription.ExpiryDate, portalOfferPrice), responseCulture.NumberFormat.CurrencyDecimalDigits);

                SubscriptionViewModel subscriptionItem = new SubscriptionViewModel()
                {
                    SubscriptionId            = subscription.SubscriptionId,
                    FriendlyName              = subscriptionTitle,
                    PortalOfferId             = portalOfferId,
                    PortalOfferPrice          = portalOfferPrice.ToString("C", responseCulture),
                    IsRenewable               = isRenewable,                                                // IsRenewable is true if subscription is going to expire in 30 days.
                    IsEditable                = isEditable,                                                 // IsEditable is true if today is lesser or equal to subscription expiry date.
                    LicensesTotal             = licenseTotal.ToString("G", responseCulture),                // General format.
                    SubscriptionTotal         = subscriptionTotal.ToString("C", responseCulture),           // Currency format.
                    SubscriptionExpiryDate    = subscriptionExpiryDate.Date.ToString("d", responseCulture), // Short date format.
                    SubscriptionOrderHistory  = historyItems,
                    SubscriptionProRatedPrice = proratedPerSeatPrice
                };

                // add this subcription to the customer's subscription list.
                customerSubscriptionsView.Add(subscriptionItem);

                // Increment the summary total.
                summaryTotal += subscriptionTotal;
            }

            // Capture the request for the customer summary for analysis.
            Dictionary <string, string> eventProperties = new Dictionary <string, string> {
                { "CustomerId", customerId }
            };

            // Track the event measurements for analysis.
            Dictionary <string, double> eventMetrics = new Dictionary <string, double> {
                { "ElapsedMilliseconds", DateTime.Now.Subtract(startTime).TotalMilliseconds }, { "NumberOfSubscriptions", customerSubscriptionsView.Count }
            };

            ApplicationDomain.Instance.TelemetryService.Provider.TrackEvent("GetSubscriptionSummaryAsync", eventProperties, eventMetrics);

            // Sort List of subscriptions based on portal offer name.
            return(new SubscriptionsSummary()
            {
                Subscriptions = customerSubscriptionsView.OrderBy(subscriptionItem => subscriptionItem.FriendlyName),
                SummaryTotal = summaryTotal.ToString("C", responseCulture)      // Currency format.
            });
        }
コード例 #17
0
        /// <summary>
        /// Gets the subscriptions managed by customers and partners
        /// </summary>
        /// <returns>returns managed subscriptions view model</returns>
        private async Task <ManagedSubscriptionsViewModel> GetManagedSubscriptions()
        {
            DateTime startTime = DateTime.Now;

            string clientCustomerId = Principal.PartnerCenterCustomerId;

            // responseCulture determines decimals, currency and such
            CultureInfo responseCulture = new CultureInfo(ApplicationDomain.Instance.PortalLocalization.Locale);

            // localeSpecificApiClient allows pulling offer names localized to supported portal locales compatible with Offer API supported locales.
            IPartner localeSpecificPartnerCenterClient = ApplicationDomain.Instance.PartnerCenterClient.With(RequestContextFactory.Instance.Create(ApplicationDomain.Instance.PortalLocalization.OfferLocale));

            // Get all subscriptions of customer from PC
            ResourceCollection <Subscription> customerAllSubscriptions = await localeSpecificPartnerCenterClient.Customers.ById(clientCustomerId).Subscriptions.GetAsync().ConfigureAwait(false);

            IEnumerable <CustomerSubscriptionEntity> customerSubscriptions = await ApplicationDomain.Instance.CustomerSubscriptionsRepository.RetrieveAsync(clientCustomerId).ConfigureAwait(false);

            IEnumerable <PartnerOffer> allPartnerOffers = await ApplicationDomain.Instance.OffersRepository.RetrieveAsync().ConfigureAwait(false);

            IEnumerable <MicrosoftOffer> currentMicrosoftOffers = await ApplicationDomain.Instance.OffersRepository.RetrieveMicrosoftOffersAsync().ConfigureAwait(false);

            List <SubscriptionViewModel> customerSubscriptionsView = new List <SubscriptionViewModel>();

            // iterate through and build the list of customer's subscriptions.
            foreach (CustomerSubscriptionEntity subscription in customerSubscriptions)
            {
                PartnerOffer partnerOfferItem  = allPartnerOffers.FirstOrDefault(offer => offer.Id == subscription.PartnerOfferId);
                string       subscriptionTitle = partnerOfferItem.Title;
                string       portalOfferId     = partnerOfferItem.Id;
                decimal      portalOfferPrice  = partnerOfferItem.Price;

                DateTime subscriptionExpiryDate = subscription.ExpiryDate.ToUniversalTime();
                int      remainingDays          = (subscriptionExpiryDate.Date - DateTime.UtcNow.Date).Days;
                bool     isRenewable            = remainingDays <= 30;                                 // IsRenewable is true if subscription is going to expire in 30 days.
                bool     isEditable             = DateTime.UtcNow.Date <= subscriptionExpiryDate.Date; // IsEditable is true if today is lesser or equal to subscription expiry date.

                // Temporarily mark this partnerOffer item as inactive and dont allow store front customer to manage this subscription.
                MicrosoftOffer alignedMicrosoftOffer = currentMicrosoftOffers.FirstOrDefault(offer => offer.Offer.Id == partnerOfferItem.MicrosoftOfferId);

                if (alignedMicrosoftOffer == null)
                {
                    // The offer is inactive (marked for deletion) then dont allow renewals or editing on this subscription tied to this offer.
                    partnerOfferItem.IsInactive = true;
                    isRenewable = false;
                    isEditable  = false;
                }

                BrandingConfiguration portalBranding = await ApplicationDomain.Instance.PortalBranding.RetrieveAsync().ConfigureAwait(false);

                // Compute the pro rated price per seat for this subcription & return for client side processing during updates.
                decimal proratedPerSeatPrice = Math.Round(CommerceOperations.CalculateProratedSeatCharge(subscription.ExpiryDate,
                                                                                                         portalOfferPrice,
                                                                                                         portalBranding.BillingCycle),
                                                          Resources.Culture.NumberFormat.CurrencyDecimalDigits);

                SubscriptionViewModel subscriptionItem = new SubscriptionViewModel()
                {
                    SubscriptionId            = subscription.SubscriptionId,
                    FriendlyName              = subscriptionTitle,
                    PortalOfferId             = portalOfferId,
                    PortalOfferPrice          = portalOfferPrice.ToString("C", responseCulture),
                    IsRenewable               = isRenewable,
                    IsEditable                = isEditable,
                    SubscriptionExpiryDate    = subscriptionExpiryDate.Date.ToString("d", responseCulture),
                    SubscriptionProRatedPrice = proratedPerSeatPrice
                };

                // add this subcription to the customer's subscription list.
                customerSubscriptionsView.Add(subscriptionItem);
            }

            List <CustomerSubscriptionModel> customerManagedSubscriptions = new List <CustomerSubscriptionModel>();
            List <PartnerSubscriptionModel>  partnerManagedSubscriptions  = new List <PartnerSubscriptionModel>();

            // Divide the subscriptions by customer and partner
            foreach (Subscription customerSubscriptionFromPC in customerAllSubscriptions.Items)
            {
                SubscriptionViewModel subscription = customerSubscriptionsView.FirstOrDefault(sub => sub.SubscriptionId == customerSubscriptionFromPC.Id);

                // Customer managed subscription found
                if (subscription != null)
                {
                    CustomerSubscriptionModel customerSubscription = new CustomerSubscriptionModel()
                    {
                        SubscriptionId            = customerSubscriptionFromPC.Id,
                        LicensesTotal             = customerSubscriptionFromPC.Quantity.ToString("G", responseCulture),
                        Status                    = GetStatusType(customerSubscriptionFromPC.Status),
                        CreationDate              = customerSubscriptionFromPC.CreationDate.ToString("d", responseCulture),
                        FriendlyName              = subscription.FriendlyName,
                        IsRenewable               = subscription.IsRenewable,
                        IsEditable                = subscription.IsEditable,
                        PortalOfferId             = subscription.PortalOfferId,
                        SubscriptionProRatedPrice = subscription.SubscriptionProRatedPrice
                    };

                    customerManagedSubscriptions.Add(customerSubscription);
                }
                else
                {
                    PartnerSubscriptionModel partnerSubscription = new PartnerSubscriptionModel()
                    {
                        Id           = customerSubscriptionFromPC.Id,
                        OfferName    = customerSubscriptionFromPC.OfferName,
                        Quantity     = customerSubscriptionFromPC.Quantity.ToString("G", responseCulture),
                        Status       = GetStatusType(customerSubscriptionFromPC.Status),
                        CreationDate = customerSubscriptionFromPC.CreationDate.ToString("d", responseCulture),
                    };

                    partnerManagedSubscriptions.Add(partnerSubscription);
                }
            }

            ManagedSubscriptionsViewModel managedSubscriptions = new ManagedSubscriptionsViewModel()
            {
                CustomerManagedSubscriptions = customerManagedSubscriptions.OrderByDescending(customerManagedSubscription => customerManagedSubscription.CreationDate),
                PartnerManagedSubscriptions  = partnerManagedSubscriptions.OrderByDescending(partnerManagedSubscription => partnerManagedSubscription.CreationDate)
            };

            // Capture the request for customer managed subscriptions and partner managed subscriptions for analysis.
            Dictionary <string, string> eventProperties = new Dictionary <string, string> {
                { "CustomerId", clientCustomerId }
            };

            // Track the event measurements for analysis.
            Dictionary <string, double> eventMetrics = new Dictionary <string, double> {
                { "ElapsedMilliseconds", DateTime.Now.Subtract(startTime).TotalMilliseconds }, { "CustomerManagedSubscriptions", customerManagedSubscriptions.Count }, { "PartnerManagedSubscriptions", partnerManagedSubscriptions.Count }
            };

            ApplicationDomain.Instance.TelemetryService.Provider.TrackEvent("GetManagedSubscriptions", eventProperties, eventMetrics);

            return(managedSubscriptions);
        }
コード例 #18
0
        /// <summary>
        /// Normalizes an order to add seats to a subscription.
        /// </summary>
        /// <returns>Normalized order.</returns>
        public async Task <OrderViewModel> NormalizePurchaseAdditionalSeatsOrderAsync()
        {
            OrderViewModel order = Order;

            order.CustomerId.AssertNotEmpty(nameof(order.CustomerId));

            if (order.OperationType != CommerceOperationType.AdditionalSeatsPurchase)
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, Resources.InvalidOperationForOrderMessage).AddDetail("Field", "OperationType");
            }

            // create result order object prefilling it with operation type & customer id.
            OrderViewModel orderResult = new OrderViewModel()
            {
                CustomerId    = order.CustomerId,
                OrderId       = order.OrderId,
                OperationType = order.OperationType
            };

            order.Subscriptions.AssertNotNull(nameof(order.Subscriptions));
            List <OrderSubscriptionItemViewModel> orderSubscriptions = order.Subscriptions.ToList();

            if (orderSubscriptions.Count != 1)
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput).AddDetail("ErrorMessage", Resources.MoreThanOneSubscriptionUpdateErrorMessage);
            }

            string subscriptionId  = orderSubscriptions.First().SubscriptionId;
            int    seatsToPurchase = orderSubscriptions.First().Quantity;

            subscriptionId.AssertNotEmpty(nameof(subscriptionId)); // is Required for the commerce operation.
            seatsToPurchase.AssertPositive("seatsToPurchase");

            // grab the customer subscription from our store
            CustomerSubscriptionEntity subscriptionToAugment = await GetSubscriptionAsync(subscriptionId, order.CustomerId).ConfigureAwait(false);

            // retrieve the partner offer this subscription relates to, we need to know the current price
            PartnerOffer partnerOffer = await ApplicationDomain.Instance.OffersRepository.RetrieveAsync(subscriptionToAugment.PartnerOfferId).ConfigureAwait(false);

            if (partnerOffer.IsInactive)
            {
                // renewing deleted offers is prohibited
                throw new PartnerDomainException(ErrorCode.PurchaseDeletedOfferNotAllowed).AddDetail("Id", partnerOffer.Id);
            }

            // retrieve the subscription from Partner Center
            Subscriptions.ISubscription subscriptionOperations = ApplicationDomain.Instance.PartnerCenterClient.Customers.ById(order.CustomerId).Subscriptions.ById(subscriptionId);

            // if subscription expiry date.Date is less than today's UTC date then subcription has expired.
            if (subscriptionToAugment.ExpiryDate.Date < DateTime.UtcNow.Date)
            {
                // this subscription has already expired, don't permit adding seats until the subscription is renewed
                throw new PartnerDomainException(ErrorCode.SubscriptionExpired);
            }

            decimal proratedSeatCharge = Math.Round(CommerceOperations.CalculateProratedSeatCharge(subscriptionToAugment.ExpiryDate, partnerOffer.Price), Resources.Culture.NumberFormat.CurrencyDecimalDigits);
            decimal totalCharge        = Math.Round(proratedSeatCharge * seatsToPurchase, Resources.Culture.NumberFormat.CurrencyDecimalDigits);

            List <OrderSubscriptionItemViewModel> resultOrderSubscriptions = new List <OrderSubscriptionItemViewModel>
            {
                new OrderSubscriptionItemViewModel()
                {
                    OfferId                = subscriptionId,
                    SubscriptionId         = subscriptionId,
                    PartnerOfferId         = subscriptionToAugment.PartnerOfferId,
                    SubscriptionExpiryDate = subscriptionToAugment.ExpiryDate,
                    Quantity               = seatsToPurchase,
                    SeatPrice              = proratedSeatCharge,
                    SubscriptionName       = partnerOffer.Title
                }
            };

            orderResult.Subscriptions = resultOrderSubscriptions;

            return(orderResult);
        }
コード例 #19
0
        /// <summary>
        /// Normalizes an order to renew a subscription.
        /// </summary>
        /// <returns>Normalized order.</returns>
        public async Task <OrderViewModel> NormalizeRenewSubscriptionOrderAsync()
        {
            OrderViewModel order = Order;

            order.CustomerId.AssertNotEmpty(nameof(order.CustomerId));
            if (order.OperationType != CommerceOperationType.Renewal)
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, Resources.InvalidOperationForOrderMessage).AddDetail("Field", "OperationType");
            }

            // create result order object prefilling it with operation type & customer id.
            OrderViewModel orderResult = new OrderViewModel()
            {
                CustomerId    = order.CustomerId,
                OrderId       = order.OrderId,
                OperationType = order.OperationType
            };

            order.Subscriptions.AssertNotNull(nameof(order.Subscriptions));
            List <OrderSubscriptionItemViewModel> orderSubscriptions = order.Subscriptions.ToList();

            if (orderSubscriptions.Count != 1)
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, Resources.MoreThanOneSubscriptionUpdateErrorMessage);
            }

            string subscriptionId = orderSubscriptions.First().SubscriptionId;

            subscriptionId.AssertNotEmpty(nameof(subscriptionId)); // is Required for the commerce operation.

            // grab the customer subscription from our store
            CustomerSubscriptionEntity subscriptionToAugment = await GetSubscriptionAsync(subscriptionId, order.CustomerId).ConfigureAwait(false);

            // retrieve the partner offer this subscription relates to, we need to know the current price
            PartnerOffer partnerOffer = await ApplicationDomain.Instance.OffersRepository.RetrieveAsync(subscriptionToAugment.PartnerOfferId).ConfigureAwait(false);

            if (partnerOffer.IsInactive)
            {
                // renewing deleted offers is prohibited
                throw new PartnerDomainException(ErrorCode.PurchaseDeletedOfferNotAllowed).AddDetail("Id", partnerOffer.Id);
            }

            // retrieve the subscription from Partner Center
            Subscriptions.ISubscription subscriptionOperations = ApplicationDomain.Instance.PartnerCenterClient.Customers.ById(order.CustomerId).Subscriptions.ById(subscriptionId);
            PartnerCenter.Models.Subscriptions.Subscription partnerCenterSubscription = await subscriptionOperations.GetAsync().ConfigureAwait(false);

            List <OrderSubscriptionItemViewModel> resultOrderSubscriptions = new List <OrderSubscriptionItemViewModel>
            {
                new OrderSubscriptionItemViewModel()
                {
                    OfferId                = subscriptionId,
                    SubscriptionId         = subscriptionId,
                    PartnerOfferId         = subscriptionToAugment.PartnerOfferId,
                    SubscriptionExpiryDate = subscriptionToAugment.ExpiryDate,
                    Quantity               = partnerCenterSubscription.Quantity,
                    SeatPrice              = partnerOffer.Price,
                    SubscriptionName       = partnerOffer.Title
                }
            };

            orderResult.Subscriptions = resultOrderSubscriptions;

            return(orderResult);
        }