Пример #1
0
        /// <summary>
        /// Checks the warehouse assigned to an order line item to determine if it is a valid shipment fulfillment location
        /// (i.e. will ship items to a customer address)
        /// </summary>
        /// <param name="warehouse">The intended source warehouse.</param>
        /// <param name="lineItem">The order line item.</param>
        /// <param name="checkInventory">Set to false to override the check against current stock.</param>
        /// <returns>
        /// true if the warehouse can ship the item and a pickup method is not already chosen; otherwise false
        /// (i.e. the warehouse cannot ship the item, or the line item has an in-store pickup method selected).
        /// </returns>
        private bool IsValidFulfillment(IWarehouse warehouse, LineItem lineItem, CheckInventoryMode checkInventory)
        {
            if (warehouse == null || lineItem == null)
            {
                throw new ArgumentNullException();
            }

            var shippingMethod = ShippingManager.GetShippingMethod(lineItem.ShippingMethodId).ShippingMethod.FirstOrDefault();

            if (shippingMethod != null && ShippingManager.IsHandoffShippingMethod(shippingMethod.Name))
            {
                return(false);
            }

            var lineApplicationId = lineItem.Parent.Parent.ApplicationId;

            if ((warehouse.ApplicationId != lineApplicationId) || !warehouse.IsActive || !warehouse.IsFulfillmentCenter)
            {
                return(false);
            }

            if (checkInventory == CheckInventoryMode.Ignore)
            {
                return(true);
            }

            var catalogKey = new Mediachase.Commerce.Catalog.CatalogKey(lineItem.Parent.Parent.ApplicationId, lineItem.CatalogEntryId);
            var inventory  = InventoryService.Service.Get(catalogKey, warehouse);

            return(IsEnoughQuantity(inventory, lineItem));
        }
Пример #2
0
        /// <summary>
        /// Matches the shipment information attached to an order line item to against the pickup warehouses to find the source warehouse, or
        /// validates the assigned warehouse if one is set already
        /// </summary>
        /// <param name="orderGroup">The order group parent for the line item.</param>
        /// <param name="lineItem">The item being processed.</param>
        /// <param name="checkInventory">Set to false to override the check against current stock.</param>
        /// <returns>the matching warehouse if all of the following are true: the shipment is set, is a valid active pickup location, and matches the line
        /// item warehouse (if set); otherwise null.</returns>
        private IWarehouse GetValidPickupFromShipment(OrderGroup orderGroup, LineItem lineItem, CheckInventoryMode checkInventory)
        {
            var shippingMethod = ShippingManager.GetShippingMethod(lineItem.ShippingMethodId).ShippingMethod.FirstOrDefault();

            if (shippingMethod == null || !ShippingManager.IsHandoffShippingMethod(shippingMethod.Name))
            {
                return(null);
            }

            var pickupAddress = orderGroup.OrderAddresses.ToArray().FirstOrDefault(x => x.Name == lineItem.ShippingAddressId);

            if (pickupAddress == null)
            {
                return(null);
            }

            var pickupWarehouse = ShippingManager.GetHandoffLocationFromAddressName(pickupAddress.Name);

            if (pickupWarehouse == null || (!string.IsNullOrEmpty(lineItem.WarehouseCode) && (pickupWarehouse.Code != lineItem.WarehouseCode)))
            {
                return(null);
            }

            var lineApplicationId = lineItem.Parent.Parent.ApplicationId;

            if ((pickupWarehouse.ApplicationId != lineApplicationId) || !pickupWarehouse.IsActive || (!pickupWarehouse.IsPickupLocation && !pickupWarehouse.IsDeliveryLocation))
            {
                return(null);
            }

            if (checkInventory == CheckInventoryMode.Check)
            {
                var catalogKey      = new Mediachase.Commerce.Catalog.CatalogKey(lineApplicationId, lineItem.CatalogEntryId);
                var pickupInventory = InventoryService.Service.Get(catalogKey, pickupWarehouse);

                if (pickupInventory == null)
                {
                    return(null);
                }
                if (!IsEnoughQuantity(pickupInventory, lineItem))
                {
                    pickupWarehouse = null;
                }
            }

            return(pickupWarehouse);
        }
Пример #3
0
        /// <summary>
        /// Validates the current warehouse selection for a line item or assigns and returns the appropriate warehouse if needed
        /// </summary>
        /// <param name="orderGroup">The order group that contains the line item.</param>
        /// <param name="lineItem">The item being purchased.</param>
        /// <returns>The Code of the warehouse that should provide the line item, or the empty string if no warehouse can supply the item</returns>
        /// <remarks>
        /// This is the main processing step for the fulfillment process.  Customer implementations that wish to override the fulfillment process
        /// with custom logic (e.g. to enable true multi-warehouse fulfillment) should start by looking here.
        /// IsValidPickupByShipment() and CanBeValidPickupByLineItem() are necessary to support the in-store pickup feature and generally will not
        /// require changes, but please verify that for your implementation.  IsValidPickupByShipment() checks line items that already have a shipment
        /// set, and CanBeValidPickupByLineItem() validates that an order with no shipment can be fulfilled as a pickup.  CanBeValidPickupByLineItem()
        /// can generate false positives so it may need a closer look.
        /// IsValidFulfillment() is a straightforward check to validate that a (non-pickup) order with a warehouse already assigned can actually
        /// provide the goods.  The only tricky point is that it does check that the line item is not configured for pickup, and that may not be
        /// appropriate in some custom setups.
        /// TryDetermineFulfillmentWarehouse() is the holder for the actual warehouse assignment when it has not been assigned already (by the UI or
        /// in an earlier pass) and will hold the key logic for any custom fulfillment implementation.
        /// </remarks>
        private string GetFulfillmentWarehouseForLineItem(OrderGroup orderGroup, LineItem lineItem)
        {
            var returnCode = String.Empty;

            var        catalogKey = new Mediachase.Commerce.Catalog.CatalogKey(lineItem.Parent.Parent.ApplicationId, lineItem.CatalogEntryId);
            IWarehouse warehouse  = null;

            if (!String.IsNullOrEmpty(lineItem.WarehouseCode))
            {
                // If a warehouse is already assigned, still need to validate whether it can fulfill the order (e.g. activity, fulfillment/pickup) or not
                // Allowing the logic in CheckInventoryActivity deal with the inventory issues, as otherwise the double evaluation can lead to larger order amounts being rejected
                warehouse = WarehouseRepository.Service.Get(lineItem.WarehouseCode);
                if (IsValidPickupFromShipment(orderGroup, lineItem, CheckInventoryMode.Ignore) ||
                    IsValidPickupFromLineItem(orderGroup, lineItem, CheckInventoryMode.Ignore) ||
                    IsValidFulfillment(warehouse, lineItem, CheckInventoryMode.Ignore))
                {
                    return(warehouse.Code);
                }
                // Redundant to the inventory amount that will be shown too, but provides useful detail
                Warnings.Add("LineItemUnfulfilled-" + lineItem.Id.ToString(), String.Format("Item \"{0}\" cannot be added to the cart because the assigned warehouse cannot fulfill or provide pickup services for the request.", lineItem.DisplayName));
                return(string.Empty);
            }

            warehouse = GetValidPickupFromShipment(orderGroup, lineItem, CheckInventoryMode.Check);
            if (warehouse != null)
            {
                return(warehouse.Code);
            }
            warehouse = GetFulfillmentWarehouse(lineItem, checkInventory: CheckInventoryMode.Check);
            if (warehouse != null)
            {
                return(warehouse.Code);
            }
            warehouse = GetFulfillmentWarehouse(lineItem, CheckInventoryMode.Ignore);
            if (warehouse != null)
            {
                return(warehouse.Code); //TODO: Validate - effectively gets overridden in CheckInventoryActivity, but lets us set an error for the bad config
            }

            // No fallback assignment, this should be an error case not a best guess
            Warnings.Add("LineItemUnfulfilled-" + lineItem.Id.ToString(), String.Format("Item \"{0}\" cannot be added to the cart because of a configuration error.", lineItem.DisplayName));

            return(string.Empty);
        }
Пример #4
0
        /// <summary>
        /// Checks the warehouse assigned to an order line item to determine if it is a valid pickup location
        /// </summary>
        /// <param name="orderGroup">The order group parent for the line item.</param>
        /// <param name="lineItem">The item being processed.</param>
        /// <param name="checkInventory">Set to false to override the check against current stock.</param>
        /// <returns>true if the assigned warehouse is an active pickup location; otherwise false.</returns>
        /// <remarks>
        /// With no shipping method selected, this can only be a "could be valid" check rather than an
        /// explicit "is valid", and thus this can report positives that will eventually be processed for
        /// delivery instead of pickups.
        /// </remarks>
        private bool IsValidPickupFromLineItem(OrderGroup orderGroup, LineItem lineItem, CheckInventoryMode checkInventory)
        {
            if (string.IsNullOrEmpty(lineItem.WarehouseCode))
            {
                return(false);
            }

            var lineApplicationId = lineItem.Parent.Parent.ApplicationId;
            var lineItemWarehouse = WarehouseRepository.Service.List()
                                    .Where(w => w.ApplicationId == lineApplicationId &&
                                           w.Code == lineItem.WarehouseCode &&
                                           w.IsActive &&
                                           (w.IsPickupLocation || w.IsDeliveryLocation) // TODO: Validate that should be both rather than just pickup
                                           )
                                    .FirstOrDefault();

            if (lineItemWarehouse == null)
            {
                return(false);
            }

            // In case lineItem shipping method is InStorePickup , check if lineItemWarehouse is pickup location
            // If not, return false.
            if (lineItem.ShippingMethodName == ShippingManager.PickupShippingMethodName && !lineItemWarehouse.IsPickupLocation)
            {
                return(false);
            }
            if (checkInventory == CheckInventoryMode.Ignore)
            {
                return(true);
            }

            var catalogKey      = new Mediachase.Commerce.Catalog.CatalogKey(lineApplicationId, lineItem.CatalogEntryId);
            var pickupInventory = InventoryService.Service.List(catalogKey).FirstOrDefault(i => i.WarehouseCode.Equals(lineItem.WarehouseCode, StringComparison.OrdinalIgnoreCase));

            return(IsEnoughQuantity(pickupInventory, lineItem));
        }
        /// <summary>
        /// Checks the warehouse assigned to an order line item to determine if it is a valid pickup location
        /// </summary>
        /// <param name="orderGroup">The order group parent for the line item.</param>
        /// <param name="lineItem">The item being processed.</param>
        /// <param name="checkInventory">Set to false to override the check against current stock.</param>
        /// <returns>true if the assigned warehouse is an active pickup location; otherwise false.</returns>
        /// <remarks>
        /// With no shipping method selected, this can only be a "could be valid" check rather than an 
        /// explicit "is valid", and thus this can report positives that will eventually be processed for
        /// delivery instead of pickups.
        /// </remarks>
        private bool IsValidPickupFromLineItem(OrderGroup orderGroup, LineItem lineItem, CheckInventoryMode checkInventory)
        {
            if (string.IsNullOrEmpty(lineItem.WarehouseCode))
            {
                return false;
            }

            var lineApplicationId = lineItem.Parent.Parent.ApplicationId;
            var lineItemWarehouse = WarehouseRepository.Service.List()
                .Where(w => w.ApplicationId == lineApplicationId
                    && w.Code == lineItem.WarehouseCode
                    && w.IsActive
                    && (w.IsPickupLocation || w.IsDeliveryLocation) // TODO: Validate that should be both rather than just pickup
                    )
                .FirstOrDefault();

            if (lineItemWarehouse == null)
            {
                return false;
            }

            // In case lineItem shipping method is InStorePickup , check if lineItemWarehouse is pickup location
            // If not, return false.
            if (lineItem.ShippingMethodName == ShippingManager.PickupShippingMethodName && !lineItemWarehouse.IsPickupLocation)
            {
                return false;
            }
            if (checkInventory == CheckInventoryMode.Ignore)
            {
                return true;
            }

            var catalogKey = new Mediachase.Commerce.Catalog.CatalogKey(lineApplicationId, lineItem.CatalogEntryId);
            var pickupInventory = InventoryService.Service.List(catalogKey).FirstOrDefault(i=>i.WarehouseCode.Equals(lineItem.WarehouseCode, StringComparison.OrdinalIgnoreCase));
            return IsEnoughQuantity(pickupInventory, lineItem);
        }
        /// <summary>
        /// Checks the warehouse assigned to an order line item to determine if it is a valid shipment fulfillment location
        /// (i.e. will ship items to a customer address)
        /// </summary>
        /// <param name="warehouse">The intended source warehouse.</param>
        /// <param name="lineItem">The order line item.</param>
        /// <param name="checkInventory">Set to false to override the check against current stock.</param>
        /// <returns>
        /// true if the warehouse can ship the item and a pickup method is not already chosen; otherwise false
        /// (i.e. the warehouse cannot ship the item, or the line item has an in-store pickup method selected).
        /// </returns>
        private bool IsValidFulfillment(IWarehouse warehouse, LineItem lineItem, CheckInventoryMode checkInventory)
        {
            if (warehouse == null || lineItem == null) { throw new ArgumentNullException(); }

            var shippingMethod = ShippingManager.GetShippingMethod(lineItem.ShippingMethodId).ShippingMethod.FirstOrDefault();
            if (shippingMethod != null && ShippingManager.IsHandoffShippingMethod(shippingMethod.Name))
            {
                return false;
            }

            var lineApplicationId = lineItem.Parent.Parent.ApplicationId;
            if ((warehouse.ApplicationId != lineApplicationId) || !warehouse.IsActive || !warehouse.IsFulfillmentCenter)
            {
                return false;
            }

            if (checkInventory == CheckInventoryMode.Ignore)
            {
                return true;
            }

            var catalogKey = new Mediachase.Commerce.Catalog.CatalogKey(lineItem.Parent.Parent.ApplicationId, lineItem.CatalogEntryId);
            var inventory = InventoryService.Service.Get(catalogKey, warehouse);
            return IsEnoughQuantity(inventory, lineItem);
        }
        /// <summary>
        /// Matches the shipment information attached to an order line item to against the pickup warehouses to find the source warehouse, or 
        /// validates the assigned warehouse if one is set already
        /// </summary>
        /// <param name="orderGroup">The order group parent for the line item.</param>
        /// <param name="lineItem">The item being processed.</param>
        /// <param name="checkInventory">Set to false to override the check against current stock.</param>
        /// <returns>the matching warehouse if all of the following are true: the shipment is set, is a valid active pickup location, and matches the line 
        /// item warehouse (if set); otherwise null.</returns>
        private IWarehouse GetValidPickupFromShipment(OrderGroup orderGroup, LineItem lineItem, CheckInventoryMode checkInventory)
        {
            var shippingMethod = ShippingManager.GetShippingMethod(lineItem.ShippingMethodId).ShippingMethod.FirstOrDefault();
            if (shippingMethod == null || !ShippingManager.IsHandoffShippingMethod(shippingMethod.Name))
            {
                return null;
            }

            var pickupAddress = orderGroup.OrderAddresses.ToArray().FirstOrDefault(x => x.Name == lineItem.ShippingAddressId);
            if (pickupAddress == null)
            {
                return null;
            }

            var pickupWarehouse = ShippingManager.GetHandoffLocationFromAddressName(pickupAddress.Name);
            if (pickupWarehouse == null || (!string.IsNullOrEmpty(lineItem.WarehouseCode) && (pickupWarehouse.Code != lineItem.WarehouseCode)))
            {
                return null;
            }

            var lineApplicationId = lineItem.Parent.Parent.ApplicationId;
            if ((pickupWarehouse.ApplicationId != lineApplicationId) || !pickupWarehouse.IsActive || (!pickupWarehouse.IsPickupLocation && !pickupWarehouse.IsDeliveryLocation))
            {
                return null;
            }

            if (checkInventory == CheckInventoryMode.Check)
            {
                var catalogKey = new Mediachase.Commerce.Catalog.CatalogKey(lineApplicationId, lineItem.CatalogEntryId);
                var pickupInventory = InventoryService.Service.Get(catalogKey, pickupWarehouse);

                if (pickupInventory == null)
                {
                    return null;
                }
                if (!IsEnoughQuantity(pickupInventory, lineItem))
                {
                    pickupWarehouse = null;
                }
            }

            return pickupWarehouse;
        }
        /// <summary>
        /// Validates the current warehouse selection for a line item or assigns and returns the appropriate warehouse if needed
        /// </summary>
        /// <param name="orderGroup">The order group that contains the line item.</param>
        /// <param name="lineItem">The item being purchased.</param>
        /// <returns>The Code of the warehouse that should provide the line item, or the empty string if no warehouse can supply the item</returns>
        /// <remarks>
        /// This is the main processing step for the fulfillment process.  Customer implementations that wish to override the fulfillment process
        /// with custom logic (e.g. to enable true multi-warehouse fulfillment) should start by looking here.
        /// IsValidPickupByShipment() and CanBeValidPickupByLineItem() are necessary to support the in-store pickup feature and generally will not
        /// require changes, but please verify that for your implementation.  IsValidPickupByShipment() checks line items that already have a shipment
        /// set, and CanBeValidPickupByLineItem() validates that an order with no shipment can be fulfilled as a pickup.  CanBeValidPickupByLineItem() 
        /// can generate false positives so it may need a closer look.
        /// IsValidFulfillment() is a straightforward check to validate that a (non-pickup) order with a warehouse already assigned can actually 
        /// provide the goods.  The only tricky point is that it does check that the line item is not configured for pickup, and that may not be 
        /// appropriate in some custom setups.
        /// TryDetermineFulfillmentWarehouse() is the holder for the actual warehouse assignment when it has not been assigned already (by the UI or
        /// in an earlier pass) and will hold the key logic for any custom fulfillment implementation.
        /// </remarks>
        private string GetFulfillmentWarehouseForLineItem(OrderGroup orderGroup, LineItem lineItem)
        {
            var returnCode = String.Empty;

            var catalogKey = new Mediachase.Commerce.Catalog.CatalogKey(lineItem.Parent.Parent.ApplicationId, lineItem.CatalogEntryId);
            IWarehouse warehouse = null;

            if (!String.IsNullOrEmpty(lineItem.WarehouseCode))
            {
                // If a warehouse is already assigned, still need to validate whether it can fulfill the order (e.g. activity, fulfillment/pickup) or not
                // Allowing the logic in CheckInventoryActivity deal with the inventory issues, as otherwise the double evaluation can lead to larger order amounts being rejected
                warehouse = WarehouseRepository.Service.Get(lineItem.WarehouseCode);
                if (IsValidPickupFromShipment(orderGroup, lineItem, CheckInventoryMode.Ignore) ||
                    IsValidPickupFromLineItem(orderGroup, lineItem, CheckInventoryMode.Ignore) ||
                    IsValidFulfillment(warehouse, lineItem, CheckInventoryMode.Ignore))
                {
                    return warehouse.Code;
                }
                // Redundant to the inventory amount that will be shown too, but provides useful detail
                Warnings.Add("LineItemUnfulfilled-" + lineItem.Id.ToString(), String.Format("Item \"{0}\" cannot be added to the cart because the assigned warehouse cannot fulfill or provide pickup services for the request.", lineItem.DisplayName));
                return string.Empty;
            }

            warehouse = GetValidPickupFromShipment(orderGroup, lineItem, CheckInventoryMode.Check);
            if (warehouse != null)
            {
                return warehouse.Code;
            }
            warehouse = GetFulfillmentWarehouse(lineItem, checkInventory: CheckInventoryMode.Check);
            if (warehouse != null)
            {
                return warehouse.Code;
            }
            warehouse = GetFulfillmentWarehouse(lineItem, CheckInventoryMode.Ignore);
            if (warehouse != null)
            {
                return warehouse.Code; //TODO: Validate - effectively gets overridden in CheckInventoryActivity, but lets us set an error for the bad config
            }

            // No fallback assignment, this should be an error case not a best guess
            Warnings.Add("LineItemUnfulfilled-" + lineItem.Id.ToString(), String.Format("Item \"{0}\" cannot be added to the cart because of a configuration error.", lineItem.DisplayName));

            return string.Empty;
        }