Managing ATP allocations and order splits

This document outlines the end-to-end flow for reserving order items using the ATP (Available to Promise) system, focusing on the processes for both on-hand inventory and backordered items. This includes detailed steps on how the system interacts with the Inventory PBC API to manage inventory efficiently, ensuring optimal customer satisfaction.

Allocating order items with On-Hand inventory

The existing command must be updated to call our Inventory PBC API for the allocate operation, which allocates order items that have on-hand inventory available.

API Call
  • URL:
    {{host:port/inventory/api/v1/available-inventories/allocate?store=Ruby&processAllOrNone=false&splitFulfillment=true}}
  • Method: POST
  • Request Body:
    {
    
    "inventories": [
    
    {
    
    "partNumber": "LR-FNTR-0001-0001",
    
    "fulfillmentCenterId": "R00B2C",
    
    "requestedQuantity": 7
    
    }
    
    ]
    
    }
  • **Command affected:
    **com.hcl.commerce.fulfillment.commands.AllocateExistingInventoryCmdImpl
Example
  1. Initial Inventory Status
    Consider an example where the on-hand inventory for part number X is five units in inventory location Y.
  2. User Action
    Inventory gets allocated during the addToCart action itself. So, once the user adds seven quantities of item X to the cart from inventory location Y, we call our inventory PBC API for inventory allocation.
  3. Triggering Allocation Command
    The system triggers the command AllocateExistingInventoryCmdImpl, initiating the allocation of the requested quantity from the available on-hand inventory.
  4. OnHand Inventory Allocation Logic
    • The command checks the available on-hand inventory:
      • Since only five units are available, the command allocates these five units to the order.
      • The remaining quantity to allocate is two (seven requested - five allocated).

POST On-hand inventory allocation

  1. Post-allocation evaluation
    • After the allocation, the system conducts an evaluation:
      • Requested Quantity: Seven
      • Allocated Quantity: Five
      • Unallocated Quantity: Two
  2. Eligibility for Split
    • The system checks the minSplitQty configuration in the STOREITEM table:
      • If two >= minSplitQty, the item is eligible for a split.
      • A new orderitemId is created for the unallocated quantity of two, which is then marked as unallocated.
      • These two quantities come from expectedInventory to be allocated using our allocate API.
  3. Deallocation (if not eligible for split)
    • If the unallocated quantity does not meet the minimum split quantity:
      • The command DeallocateExistingInventoryCmdImpl is invoked to deallocate the previously allocated five units.
      • The command will call out Inventory PBC API as below.
      • URL: {{host:port/inventory/api/v1/available-inventories/deallocate?store=Ruby&processAllOrNone=false}}
      • Method: POST.
      • Request Body:
        {
        
        "inventories": [
        
        {
        
        "partNumber": "LR-FNTR-0001-0001",
        
        "fulfillmentCenterId": "R00B2C",
        
        "requestedQuantity": 5
        
        }
        
        ]
        
        
      • The system updates the order status to reflect that no items have been allocated.
      • After deallocating, the whole seven quantities become eligible for allocation from backorder.

Allocate order items from expected inventory

The system must also be updated to call our Inventory PBC API for the allocation operation to allocate order items for which there is no sufficient on-hand inventory.

API Call
  • URL: {{host:port/inventory/api/v1/expected-inventories/allocate?store=Ruby&processAllOrNone=false}}
  • Method: POST.
    {
    
    "inventories": [
    
    {
    
    "partNumber": "LR-FNTR-0001-0001",
    
    "fulfillmentCenterId": "R00B2C",
    
    "requestedQuantity": 2
    
    }
    
    ]
    
    }
  • **Command affected: **com.hcl.commerce.fulfillment.commands.AllocateExpectedInventoryCmdImpl

Let's continue with the previous example

  1. Scenario of Unallocated Items
    • Following the previous example, two quantities of item X remain unallocated after the initial add-to-cart process due to insufficient on-hand inventory.
    • The system now aims to allocate these two quantities from expected inventory.
  2. Executing the Allocation Command
    • The system calls the command AllocateExpectedInventoryCmdImpl to attempt to allocate the unallocated quantities from the expected inventory.
    • This command checks the expected inventory levels for item X.
  3. Backorderable Check
    • If the items are backorderable:
      • The system proceeds with the allocation from the expected inventory.
      • The inventory levels are updated accordingly to reflect the new reservations.
    • If the items are not backorderable:
      • An exception is returned, indicating that the requested items cannot be allocated.
      • The user is notified about the inability to fulfill the order due to unavailability.
Example outcome
Successful Allocation
  • If the system successfully allocates the split order item of two from the expected inventory, it updates the order status to indicate that these items are now assigned.
  • The user receives confirmation that the items will be fulfilled as soon as they become available.
Unsuccessful Allocation
  • If the allocation fails because the items are back orderable, the user is informed that the items cannot be allocated, and they may need to adjust their order or wait for inventory replenishment.

Place order after allocation

After this allocation happens, we go and place the order. The order status will be code 'B,' indicating backorder status, as we have one order item with quantity two, which is from backorder.

Once the order is placed, there is a scheduler job, ProcessBackordersCmdImpl, that runs periodically to process backorders as described in the following steps:

Process backorder inventory by ProcessBackorder scheduler job when placed order
Let us see how our order will be processed
Take the following scenario, in which an order has been placed with split order items:
  • Order Item one: five quantities allocated from on-hand inventory.
  • Order Item two: two quantities allocated from backorder.
The order status has become BO since the second item was allocated from the backorder. The order cannot be processed for release until both order items are fully allocated from on-hand inventory.
ProcessBackorderCmdImpl job execution
When the ProcessBackordersCmdImpl job runs, it performs the following steps:
  1. The Commerce Order System fetches order items with an inventory status of BO (backorder) based on the time placed.
  2. After selecting the order items, it calls the AllocateInventoryCmd command, which performs these actions:
    1. DeAllocate Backorder Inventory
      This command calls DeallocateExpectedInventoryCmdImpl to deallocate the backorder inventory for the backordered items. Here, two quantities will be deallocated to expected inventory.
      API Call:
      • URL: {{host:port/inventory/api/v1/expected-inventories/deallocate?store=Ruby&processAllOrNone=false}}

      • Method: POST
      • Request Body:
        {
        
        "inventories": [
        
        {
        
        "partNumber": "LR-FNTR-0001-0001",
        
        "fulfillmentCenterId": "R00B2C",
        
        "unitOfMeasure": "C62",
        
        "allocatedQuantityOnHand": 0,
        
        "allocatedQuantityBackordered": 2,
        
        "allocationDetails": [
        
        {
        
        "expectedInventoryId": 1,
        
        "quantityAllocated": 2
        
        }
        
        ]
        
        }
        
        ]
        
        }
      • allocatedQuantityOnHand is the value in QTYALLOBACKORDER against that order item.
      • Command affected:
        com.hcl.commerce.fulfillment.commands.DeallocateExpectedInventoryCmdImpl
    2. Allocate from On-Hand inventory
      The job then calls the AllocateExistingInventoryCmdImpl command, passing splitFulfillment='false' (this will be passed only for backorder items; otherwise, the value will be 'true') to allocate the entire required quantity from a single Inventory Location (IL) for those backordered items.
      Note: When splitFulfillment='false', if the entire required quantity is available in that inventory location , allocate it, and do not proceed to the next step C, otherwise, do not perform the partial allocation.
    3. Allocate from Expected Inventory
      When processing the backorder inventory, two scenarios may arise:
      1. Scenario 1: Partial On-Hand inventory available
        If you need two on-hand quantities but only one has arrived by the time you process the backorder via the scheduler, this one quantity will be allocated and placed into the QTYALLOBACKORDERED column instead of the QTYINPROCESS column (which is the expected behavior when allocating quantity from on-hand inventory).
        Thus, QTYALLOBACKORDER holds allocated on-hand inventory for backorder items when complete inventory is not available. The remaining one will then be allocated from the expected inventory.
        Note: When this order item is processed again by the backorder scheduler, the deallocatBackOrder API will look like following:
        • API Call:
          • URL: {{host:port/inventory/api/v1/expected-inventories/deallocate?store=Ruby&processAllOrNone=false}}
          • Method: POST
          • Request Body:
            {
            
            "inventories": [
            
            {
            
            "partNumber": "LR-FNTR-0001-0001",
            
            "fulfillmentCenterId": "R00B2C",
            
            "unitOfMeasure": "C62",
            
            "allocatedQuantityOnHand": 1,
            
            "allocatedQuantityBackordered": 1,
            
            "allocationDetails": [
            
            {
            
            "expectedInventoryId": 1,
            
            "quantityAllocated": 1
            
            }
            
            ]
            
            }
            
            ]
            
            }
          • allocatedQuantityOnHand is the value in QTYALLOBACKORDER against that order item.
          • Command affected:
            com.hcl.commerce.fulfillment.commands.DeallocateExpectedInventoryCmdImpl
      2. Scenario 2: No On-Hand Inventory Available
        If you need two on-hand quantities but have zero available, the system will allocate the two from the expected inventory. In this case, the order items will revert to backorder status.
      Continuous Allocation Process
      This process continues until all backorder items are entirely allocated from on-hand inventory. The scheduler job runs at specified intervals to ensure that any newly available inventory is allocated to fulfill the backorder items, ultimately updating their status to indicate successful allocation and readiness for release.
    4. Release to Fulfillment scheduler job execution
      After updating the order status to M, the ReleaseToFulfillment scheduler job runs. This job performs the following actions:
      • It picks the orders that are in statuses like M or 'C (completed) and calls the HardReserve API to hard reserve inventory of the items.
      • API Call:
        • URL: {{host:port/inventory/api/v1/available-inventories/reserve?store=Ruby&processAllOrNone=false}}
        • Method: POST
        • Request Body:
          {
          
          "inventories": [
          
          {
          
          "partNumber": "LR-FNTR-0001-0001",
          
          "fulfillmentCenterId": "R00B2C",
          
          "requestedQuantity": 7
          
          }
          
          ]
          
          }
      • Command affected:
        • com.ibm.commerce.inventory.commands.DoInventoryActionCmdImpl
        • Once the reservation is successful, the order status is updated to R (ready), indicating that these orders are prepared for the pick batch and shipment process.

API list

Shopper APIs
The shopper starts by browsing items in a store and checking their availability.
  • The shopper interacts with the inventory system to view available products and their locations.
  • The shopper adds an item to the cart and proceeds to checkout.
  • Get Item Inventories: GET /inventory/api/v1/item-inventories?store=Ruby, retrieves the list of item inventory details for the given store. This API is used only on the storefront side to display available inventory for shoppers browsing products in the store.
  • Get Inventory Locations: GET /inventory/api/v1/inventory-locations?store=Ruby, fetches the inventory details for physical stores. This API is used on the storefront side to display available inventory locations where items are located in different stores for customers to view.
  • POST to Create or Update Inventory Location: POST /inventory/api/v1/inventory-locations?store=Ruby, creates or updates inventory locations in the store. This API is used to add new locations or update the details of existing inventory locations within the store.

Checkout APIs

During checkout, the system checks the availability of the items in the cart.
  • The checkout process continues if the items are available and the shopper can purchase them.
  • Check Available Inventories (Add to Cart): POST /inventory/api/v1/available-inventories/check?store=Ruby, verifies the availability of items in the cart for the given store.
  • Checkout Order (API called in sequence from ts-app when OrderItem inventory status is NALLC (Not Allocated))
    • Inventory Validation: POST /inventory/api/v1/available-inventories/check?store=Ruby, confirms the availability of items before proceeding with the checkout.
    • Allocate On-Hand Inventory (No Partial Allocation): POST /inventory/api/v1/available-inventories/allocate?store=Ruby&splitFulfillment=false, tries to allocate the entire quantity from on-hand inventory at the given inventory location . No partial allocation is allowed.
    • Allocate On-Hand Inventory (Partial Allocation Allowed): POST /inventory/api/v1/available-inventories/allocate?store=Ruby&splitFulfillment=true, tries to allocate whole or partial quantity from on-hand inventory at the given inventory location . Partial allocation is allowed.
    • Allocate Expected Inventory: POST /inventory/api/v1/expected-inventories/allocate?store=Ruby, tries to allocate the whole from the expected inventory at the inventory location . Partial allocation is not allowed.
  • Submit Order (API called in sequence from ts-app when OrderItem inventory status is ALLC (Allocated) or BO (Backordered))
    • Deallocate On-Hand Inventory: POST /inventory/api/v1/available-inventories/deallocate?store=Ruby, tries to de-allocate on-hand inventory from the given inventory location .
    • Deallocate Expected Inventory: POST /inventory/api/v1/expected-inventories/deallocate?store=Ruby, tries to de-allocate expected inventory from the given inventory location .
    • Check Available Inventories: POST /inventory/api/v1/available-inventories/check?store=Ruby, confirms the availability of items before proceeding with the checkout.
    • Allocate On-Hand Inventory (No Partial Allocation): POST /inventory/api/v1/available-inventories/allocate?store=Ruby&splitFulfillment=false, tries to allocate the entire quantity from on-hand inventory at the given inventory location . No partial allocation is allowed.
    • Allocate On-Hand Inventory (Partial Allocation Allowed): POST /inventory/api/v1/available-inventories/allocate?store=Ruby&splitFulfillment=true, tries to allocate whole or partial quantity from on-hand inventory at the given inventory location . Partial allocation is allowed.
    • Allocate Expected Inventory: POST /inventory/api/v1/expected-inventories/allocate?store=Ruby, tries to allocate the whole quantity from the expected inventory at the inventory location . Partial allocation is not allowed.

Shopper and Checkout API endpoints

Shopper APIs
  • GET /inventory/api/v1/item-inventories?store=Ruby
  • GET /inventory/api/v1/inventory-locations?store=Ruby
  • POST /inventory/api/v1/inventory-locations?store=Ruby
Checkout endpoints
  • POST /inventory/api/v1/available-inventories/check?store=Ruby
  • POST /inventory/api/v1/available-inventories/deallocate?store=Ruby
  • POST /inventory/api/v1/expected-inventories/deallocate?store=Ruby
  • POST /inventory/api/v1/available-inventories/allocate?store=Ruby&splitFulfillment=false
  • POST /inventory/api/v1/available-inventories/allocate?store=Ruby&splitFulfillment=true
  • POST /inventory/api/v1/expected-inventories/allocate?store=Ruby