/// <summary>
            /// Get availability of items for specified stores.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="stores">The collection of store locations.</param>
            /// <param name="itemUnits">The collection of items.</param>
            /// <returns>
            /// The collection of store availability information.
            /// </returns>
            internal static Collection <OrgUnitAvailability> GetChannelAvailabiltiy(RequestContext context, IEnumerable <OrgUnitLocation> stores, IEnumerable <ItemUnit> itemUnits)
            {
                HashSet <ItemWarehouse> itemWarehouses = new HashSet <ItemWarehouse>();

                foreach (OrgUnitLocation storeLocation in stores)
                {
                    foreach (var itemUnit in itemUnits)
                    {
                        ItemWarehouse itemWarehouse = new ItemWarehouse
                        {
                            ItemId = itemUnit.ItemId,
                            VariantInventoryDimensionId = itemUnit.VariantInventoryDimensionId,
                            InventoryLocationId         = storeLocation.InventoryLocationId
                        };
                        itemWarehouses.Add(itemWarehouse);
                    }
                }

                var request  = new GetItemAvailabilitiesByItemWarehousesServiceRequest(QueryResultSettings.AllRecords, itemWarehouses);
                var response = context.Execute <GetItemAvailabilitiesByItemWarehousesServiceResponse>(request);

                ChannelAvailabilityHelper.ConvertUnitOfMeasure(context, response.ItemAvailabilities.Results, itemUnits);

                Collection <OrgUnitAvailability> storeAvailabilities = new Collection <OrgUnitAvailability>();

                foreach (OrgUnitLocation storeLocation in stores)
                {
                    List <ItemAvailability> itemAvailabilities = response.ItemAvailabilities.Results.Where(item => item.InventoryLocationId.Equals(storeLocation.InventoryLocationId, StringComparison.OrdinalIgnoreCase)).ToList();
                    storeAvailabilities.Add(new OrgUnitAvailability(storeLocation, itemAvailabilities));
                }

                return(storeAvailabilities);
            }
Exemplo n.º 2
0
            private static void SetSalesLineInventoryForPickup(RequestContext context, SalesTransaction transaction, IEnumerable <SalesLine> salesLines, Dictionary <string, decimal> salesLineInventoryQuantities)
            {
                ThrowIf.Null(context, "context");
                ThrowIf.Null(salesLines, "salesLines");

                NetTracer.Information("ItemAvailabilityHelper.SetSalesLineInventoryForPickup(): TransactionId = {0}, CustomerId = {1}", transaction.Id, transaction.CustomerId);

                // Get item availablities by item warehouses.
                IEnumerable <ItemAvailability> itemAvailabilities = ChannelAvailabilityHelper.GetItemAvailabilitiesByItemWarehouses(context, salesLines.Select(salesLine => salesLine.GetItemWarehouse()));

                // Set inventory for sales lines.
                SetSalesLineInventoryForPickup(context, salesLines, itemAvailabilities, salesLineInventoryQuantities);
            }
Exemplo n.º 3
0
            private static void SetSalesLineInventoryForShipping(RequestContext context, SalesTransaction transaction, IEnumerable <SalesLine> salesLines, Dictionary <string, decimal> salesLineInventoryQuantities)
            {
                ThrowIf.Null(context, "context");
                ThrowIf.Null(salesLines, "salesLines");

                NetTracer.Information("ItemAvailabilityHelper.SetSalesLineInventoryForShipping(): TransactionId = {0}, CustomerId = {1}", transaction.Id, transaction.CustomerId);

                // Calculate total converted quantities.
                Dictionary <ItemVariantInventoryDimension, ItemQuantity> itemQuantities = GetSalesLineItemQuantities(salesLines, salesLineInventoryQuantities);

                // Get item availablities by total item quantities.
                IEnumerable <ItemAvailability> itemAvailabilities = ChannelAvailabilityHelper.GetAllItemAvailablitiesByItemQuantities(context, transaction.CustomerId, itemQuantities.Values, GetMaxLinesPerItem(salesLines));

                // Set inventory for sales lines.
                SetSalesLineInventoryForShipping(context, salesLines, itemAvailabilities, salesLineInventoryQuantities);
            }
            /// <summary>
            /// Executes the workflow for getting item available quantities.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>The response.</returns>
            protected override GetItemAvailableQuantitiesResponse Process(GetItemAvailableQuantitiesRequest request)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.Null(request.ItemUnits, "request.ItemUnits");

                // Gets all distinct items.
                IEnumerable <ItemVariantInventoryDimension> distinctItems = request.ItemUnits.Select(itemUnit => itemUnit.GetItem()).Distinct();

                // Get item available quantities.
                var quantityRequest  = new GetItemAvailableQuantitiesByItemsServiceRequest(request.QueryResultSettings, distinctItems, request.CustomerAccountNumber);
                var quantityResponse = this.Context.Execute <GetItemAvailableQuantitiesByItemsServiceResponse>(quantityRequest);

                // Converts to requested unit of measure if possible.
                var convertedItemAvailableQuantities = ChannelAvailabilityHelper.ConvertUnitOfMeasure(this.Context, quantityResponse.ItemAvailableQuantities.Results, request.ItemUnits);

                // Get item available quantities.
                return(new GetItemAvailableQuantitiesResponse(convertedItemAvailableQuantities.AsPagedResult(quantityResponse.ItemAvailableQuantities.TotalCount)));
            }
            /// <summary>
            /// Convert unit of measure for item availabilities.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="itemAvailableQuantities">The item availabilities.</param>
            /// <param name="itemUnits">The desired item unit of measures.</param>
            /// <returns>The converted item available quantities.</returns>
            internal static IEnumerable <ItemAvailableQuantity> ConvertUnitOfMeasure(RequestContext context, IEnumerable <ItemAvailableQuantity> itemAvailableQuantities, IEnumerable <ItemUnit> itemUnits)
            {
                List <ItemAvailableQuantity> convertedItemAvailableQuantities = new List <ItemAvailableQuantity>();

                IEnumerable <ItemUnit>         distinctItemUnits  = itemUnits.Distinct();
                IEnumerable <ItemUnitQuantity> itemUnitQuantities = itemAvailableQuantities.Select(itemAvailableQuantity => itemAvailableQuantity.GetItemUnitQuantity());
                Dictionary <ItemUnitConversion, UnitOfMeasureConversion> conversions = GetUnitOfMeasureConversions(context, itemUnitQuantities, distinctItemUnits);

                ILookup <ItemVariantInventoryDimension, ItemUnit> itemUnitLookupByItem = distinctItemUnits.ToLookup(itemUnit => itemUnit.GetItem());

                foreach (ItemAvailableQuantity itemAvailableQuantity in itemAvailableQuantities)
                {
                    bool hasInventoryUnit = false;
                    foreach (ItemUnit itemUnit in itemUnitLookupByItem[itemAvailableQuantity.GetItem()])
                    {
                        ItemUnitConversion      itemUnitConversion = ChannelAvailabilityHelper.GetItemUnitConversion(itemAvailableQuantity.GetItemUnitQuantity(), itemUnit);
                        UnitOfMeasureConversion unitOfMeasureConversion;
                        if (!conversions.TryGetValue(itemUnitConversion, out unitOfMeasureConversion))
                        {
                            if (!StringDataHelper.Equals(itemAvailableQuantity.UnitOfMeasure, itemUnit.UnitOfMeasure))
                            {
                                context.Notify(new UnableToConvertUnitOfMeasureNotification(itemUnitConversion));
                            }

                            if (!hasInventoryUnit)
                            {
                                convertedItemAvailableQuantities.Add(itemAvailableQuantity);
                                hasInventoryUnit = true;
                            }
                        }
                        else
                        {
                            ItemAvailableQuantity convertedItemAvailableQuantity = new ItemAvailableQuantity();
                            convertedItemAvailableQuantity.CopyPropertiesFrom(itemAvailableQuantity);
                            convertedItemAvailableQuantity.AvailableQuantity = unitOfMeasureConversion.Convert(itemAvailableQuantity.AvailableQuantity);
                            convertedItemAvailableQuantity.UnitOfMeasure     = itemUnitConversion.ToUnitOfMeasure;
                            convertedItemAvailableQuantities.Add(convertedItemAvailableQuantity);
                        }
                    }
                }

                return(convertedItemAvailableQuantities.AsEnumerable());
            }
            internal static Dictionary <ItemUnitConversion, UnitOfMeasureConversion> GetUnitOfMeasureConversions(RequestContext context, IEnumerable <ItemUnitQuantity> itemUnitQuantities, IEnumerable <ItemUnit> itemUnits)
            {
                IEnumerable <ItemUnit>           distinctItemUnits      = itemUnits.Distinct();
                ILookup <string, ItemUnit>       itemUnitLookupByItemId = distinctItemUnits.ToLookup(itemUnit => itemUnit.ItemId);
                IEnumerable <ItemUnitConversion> itemUnitConversions    = itemUnitQuantities.SelectMany(itemUnitQuantity => ChannelAvailabilityHelper.GetItemUnitConversions(itemUnitQuantity, itemUnitLookupByItemId));

                return(GetUnitOfMeasureConversions(context, itemUnitConversions));
            }
            /// <summary>
            /// Processes the specified request.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>
            /// Available quantities of specified listings at the requested warehouse.
            /// </returns>
            protected override GetListingAvailableQuantitiesResponse Process(GetListingAvailableQuantitiesRequest request)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.NullOrEmpty(request.ProductIds, "No Ids have been provided");

                QueryResultSettings settings = QueryResultSettings.AllRecords;
                var productIds = request.ProductIds.Distinct().ToList();
                ProductSearchCriteria queryCriteria = new ProductSearchCriteria(this.Context.GetPrincipal().ChannelId)
                {
                    Ids       = productIds,
                    DataLevel = CommerceEntityDataLevel.Identity,
                };

                var productSearchResult = request.RequestContext.Runtime.Execute <ProductSearchServiceResponse>(
                    new ProductSearchServiceRequest(queryCriteria, settings), request.RequestContext).ProductSearchResult;

                if (productSearchResult.Results.IsNullOrEmpty())
                {
                    string nonResolvedProductIdsInfo = string.Join(" ", productIds);
                    //// This is a valid situtation for cross channel scenarios, wish lists for example.
                    NetTracer.Warning("None of the specified product ids were found on the current channel {0}. ProductIds = {1}", this.Context.GetPrincipal().ChannelId, nonResolvedProductIdsInfo);
                    return(new GetListingAvailableQuantitiesResponse());
                }

                var productMap = productSearchResult.Results.ToDictionary(p => p.RecordId, p => p);
                var items      = this.GetItemAndInventDimId(productIds, productSearchResult, productMap);

                settings = new QueryResultSettings(new PagingInfo(items.Count(), 0));
                var itemAvailabilities = new HashSet <ItemAvailability>();
                var itemUnits          = new HashSet <ItemUnit>();

                if (request.ChannelId == 0)
                {
                    var itemVariantInventoryDimensions = new HashSet <ItemVariantInventoryDimension>();
                    foreach (var item in items)
                    {
                        itemVariantInventoryDimensions.Add(new ItemVariantInventoryDimension(item.Item1, item.Item2));
                    }

                    var itemAvailableQuantitiesRequest  = new GetItemAvailableQuantitiesByItemsServiceRequest(settings, itemVariantInventoryDimensions, string.Empty);
                    var itemAvailableQuantitiesResponse = this.Context.Execute <GetItemAvailableQuantitiesByItemsServiceResponse>(itemAvailableQuantitiesRequest);
                    foreach (var quantity in itemAvailableQuantitiesResponse.ItemAvailableQuantities.Results)
                    {
                        if (quantity != null)
                        {
                            var productAvailableQuantity = new ItemAvailability
                            {
                                ItemId = quantity.ItemId,
                                VariantInventoryDimensionId = quantity.VariantInventoryDimensionId,
                                AvailableQuantity           = quantity.AvailableQuantity,
                                UnitOfMeasure = quantity.UnitOfMeasure
                            };

                            itemAvailabilities.Add(productAvailableQuantity);

                            var itemUnit = new ItemUnit
                            {
                                ItemId = quantity.ItemId,
                                VariantInventoryDimensionId = quantity.VariantInventoryDimensionId,
                                UnitOfMeasure = items.Where(i => i.Item1.Equals(quantity.ItemId) && i.Item2.Equals(quantity.VariantInventoryDimensionId)).SingleOrDefault().Item3
                            };

                            itemUnits.Add(itemUnit);
                        }
                    }
                }
                else
                {
                    var itemWarehouses = new HashSet <ItemWarehouse>();
                    foreach (var item in items)
                    {
                        var itemWarehouse = new ItemWarehouse()
                        {
                            ItemId = item.Item1,
                            VariantInventoryDimensionId = item.Item2,
                            InventoryLocationId         = this.Context.GetChannelConfiguration().InventLocation
                        };
                        itemWarehouses.Add(itemWarehouse);
                    }

                    var warehouseRequest  = new GetItemAvailabilitiesByItemWarehousesServiceRequest(settings, itemWarehouses);
                    var warehouseResponse = this.Context.Execute <GetItemAvailabilitiesByItemWarehousesServiceResponse>(warehouseRequest);
                    foreach (var quantity in warehouseResponse.ItemAvailabilities.Results)
                    {
                        if (quantity != null)
                        {
                            itemAvailabilities.Add(quantity);

                            var itemUnit = new ItemUnit
                            {
                                ItemId = quantity.ItemId,
                                VariantInventoryDimensionId = quantity.VariantInventoryDimensionId,
                                UnitOfMeasure = items.Where(i => i.Item1.Equals(quantity.ItemId) && i.Item2.Equals(quantity.VariantInventoryDimensionId)).SingleOrDefault().Item3
                            };

                            itemUnits.Add(itemUnit);
                        }
                    }
                }

                var itemAvailabilitiesList  = ChannelAvailabilityHelper.ConvertUnitOfMeasure(this.Context, itemAvailabilities.ToList(), itemUnits.ToList());
                var processedAvailabilities = this.ProcessItemAvailabilities(itemAvailabilitiesList, productIds, productSearchResult, productMap);

                return(new GetListingAvailableQuantitiesResponse(processedAvailabilities.AsPagedResult()));
            }
            /// <summary>
            /// Executes the workflow for a get nearby stores with availability.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>The response.</returns>
            protected override GetStoreProductAvailabilityResponse Process(GetStoreProductAvailabilityRequest request)
            {
                ThrowIf.Null(request, "request");

                string variantId = request.VariantId;

                if (request.Items == null || !request.Items.Any())
                {
                    if (string.IsNullOrWhiteSpace(request.ItemId) && string.IsNullOrWhiteSpace(request.Barcode))
                    {
                        throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ItemIdBarcodeMissing, "Please specify either an item id or a barcode.");
                    }

                    List <string> itemIds = new List <string>(1);
                    if (!string.IsNullOrWhiteSpace(request.Barcode))
                    {
                        GetProductBarcodeDataRequest dataRequest = new GetProductBarcodeDataRequest(request.Barcode)
                        {
                            QueryResultSettings = new QueryResultSettings(new ColumnSet("ITEMID"), PagingInfo.AllRecords)
                        };
                        ItemBarcode itemBarcode = this.Context.Runtime.Execute <GetProductBarcodeDataResponse>(dataRequest, this.Context).Barcode;

                        if (itemBarcode == null)
                        {
                            throw new DataValidationException(
                                      DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_BarcodeNotFound,
                                      string.Format("The specified barcode ({0}) was not found.", request.Barcode));
                        }

                        itemIds.Add(itemBarcode.ItemId);
                        variantId = itemBarcode.VariantId;
                    }
                    else
                    {
                        itemIds.Add(request.ItemId);
                    }

                    var getItemsRequest = new GetItemsDataRequest(itemIds)
                    {
                        QueryResultSettings = new QueryResultSettings(new ColumnSet("INVENTUNITID"), PagingInfo.AllRecords)
                    };
                    var getItemsResponse = request.RequestContext.Runtime.Execute <GetItemsDataResponse>(getItemsRequest, request.RequestContext);

                    ReadOnlyCollection <Item> items = getItemsResponse.Items;
                    if (items == null || items.Count == 0)
                    {
                        throw new DataValidationException(
                                  DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound,
                                  string.Format("No items were found for the specified item identifiers ({0}).", string.Join(", ", itemIds)));
                    }

                    if (items.Count > 1)
                    {
                        throw new DataValidationException(
                                  DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_DuplicateObject,
                                  string.Format("More than one item was found for the specified item identifiers ({0}).", string.Join(", ", itemIds)));
                    }

                    var getStoresRequest = new GetStoresDataRequest(this.Context.GetPrincipal().ChannelId, request.SearchArea, QueryResultSettings.AllRecords);
                    ReadOnlyCollection <OrgUnitLocation> storeLocations = this.Context.Execute <EntityDataServiceResponse <OrgUnitLocation> >(getStoresRequest).PagedEntityCollection.Results;

                    var productAvailabilityRequest  = new Services.GetStoreAvailabilityServiceRequest(items[0].ItemId, variantId ?? string.Empty);
                    var productAvailabilityResponse = this.Context.Execute <Services.GetStoreAvailabilityServiceResponse>(productAvailabilityRequest);

                    var storeAvailabilities = GetStoreAvailabilities(productAvailabilityResponse.ItemsAvailability, storeLocations, items[0].InventoryUnitOfMeasure);
                    return(new GetStoreProductAvailabilityResponse(storeAvailabilities.AsPagedResult()));
                }
                else
                {
                    ThrowIf.Null(request.Items, "request.Items");

                    var getStoresRequest = new GetStoresDataRequest(this.Context.GetPrincipal().ChannelId, request.SearchArea, QueryResultSettings.AllRecords);
                    ReadOnlyCollection <OrgUnitLocation> storeLocations = this.Context.Execute <EntityDataServiceResponse <OrgUnitLocation> >(getStoresRequest).PagedEntityCollection.Results;

                    var storeAvailabilities = ChannelAvailabilityHelper.GetChannelAvailabiltiy(this.Context, storeLocations, request.Items);
                    return(new GetStoreProductAvailabilityResponse(storeAvailabilities.AsPagedResult()));
                }
            }
Exemplo n.º 9
0
            /// <summary>
            /// Sets the sales line inventory for each sales line.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="transaction">Current transaction.</param>
            public static void SetSalesLineInventory(RequestContext context, SalesTransaction transaction)
            {
                ThrowIf.Null(context, "context");
                ThrowIf.Null(transaction, "transaction");

                NetTracer.Information("ItemAvailabilityHelper.SetSalesLineInventory(): TransactionId = {0}, CustomerId = {1}", transaction.Id, transaction.CustomerId);

                // Get channel configuration.
                ChannelConfiguration channelConfiguration = context.GetChannelConfiguration();

                if (!NeedInventoryChecking(channelConfiguration, transaction))
                {
                    return;
                }

                // Get unit of measure conversions from sales to inventory unit of measure.
                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                IEnumerable <ItemUnitConversion> itemUnitConversions = transaction.InventorySalesLines.Select(salesLine => salesLine.GetItemUnitConversion());
                Dictionary <ItemUnitConversion, UnitOfMeasureConversion> unitOfMeasureConversions = ChannelAvailabilityHelper.GetUnitOfMeasureConversions(context, itemUnitConversions);

                // Convert quantities from sales to inventory unit of measure.
                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                Dictionary <string, decimal> salesLineInventoryQuantities = GetSalesLineInventoryQuantities(context, transaction.InventorySalesLines, unitOfMeasureConversions);

                // Set sales line inventory for pickup or for return
                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                IEnumerable <SalesLine> linesForPickup = transaction.InventorySalesLines.Where(s => IsPickupOrReturn(s, channelConfiguration));

                if (linesForPickup.Any())
                {
                    SetSalesLineInventoryForPickup(context, transaction, linesForPickup, salesLineInventoryQuantities);
                }

                // Set sales line inventory for shipping
                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                IEnumerable <SalesLine> linesForShipping = transaction.InventorySalesLines.Where(s => !IsPickupOrReturn(s, channelConfiguration));

                if (linesForShipping.Any())
                {
                    SetSalesLineInventoryForShipping(context, transaction, linesForShipping, salesLineInventoryQuantities);
                }
            }