/// <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); }
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); }
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())); } }
/// <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); } }