Use Order Management Extensions to Apply Holds

Use an order management extension to apply a hold on the order header or on the order line.

  • Use an order management extension when you have a business requirement that you can't meet through Oracle Order Management's predefined hold behavior.
  • Use the On Start of Submission Request or the On Save extension point to apply your hold.
  • Make sure you have the Manage Order Management Extensions (FOM_MANAGE_ORDER_MANAGEMENT_EXTENSIONS_PRIV) privilege.

You use the applyHold method to apply a hold. Your extension must check for these conditions when you use applyHold:

Apply a Hold Only Method That You Can Use to Meet Condition
On a new sales order or a sales order that's in Draft status. You can't use applyHold with a change order or a sales order that you already submitted to fulfillment.

isFirstDraftOrder

When the order line isn't a child line of a model or kit, and when you haven't already applied a hold on the line. canApplyHold

You might also find these methods useful.

  • holdExists
  • isChildLine
  • Debug

For details, see Methods That You Can Use with Order Management Extensions.

We use the CustomerPONumber attribute as a test to make sure the extension runs only on sales orders that are on hold. In your test environment, you add the text ApplyHold to an attribute. We use the customer's purchase order number because it is a text attribute that's always available on the order header. You can use a different attribute to meet your needs.

Make sure you remove this test before you publish the extension to your production environment:

def poNumber = header.getAttribute("CustomerPONumber")

if (poNumber != "ApplyHold") return;

Set Up Profile

If you want to apply a hold on the order header, then you must enable a profile option. It's optional if you want to apply a hold on the order line. If you enable the profile, then Order Management uses a faster and more efficient way to process your holds. We recommend that you enable it even if you apply a hold only on the order line.

  1. Create a profile option.
    • Go to the Setup and Maintenance work area, click Tasks > Search, then search for Manage Profile Options.
    • On the Manage Profile Options page, in the search results, click Actions > New.
    • On the Create Profile Option page, set the values.
      Attribute Value
      Profile Option Code FOM_NEW_HOLDS_PROCESSING
      Profile Display Name FOM New Hold Processing
      Application Order Management
      Module Manage Orders
      SQL Validation select MEANING, LOOKUP_CODE from FND_LOOKUPS where LOOKUP_TYPE='YES_NO’
      Start Date Today or a date in the future.
    • In the Profile Option Levels area, set the values.
      Attribute Value
      Level Site
      Enabled Contains a check mark.
      Updatable Contains a check mark.
    • Click Save and Close.
  2. Administer the profile value.
    • On the Overview page, search for and open the Manage Administrator Profile Values task.
    • On the Manage Administrator Profile Values page, search for the value.
      Attribute Value
      Profile Option Code FOM_NEW_HOLDS_PROCESSING
    • In the Profile Values area, set the values, then click Save and Close.
      Attribute Value
      Profile Level Site
      Profile Value Yes

Apply Hold on Order Header

Increase your efficiency. Apply a single hold on the order header when your sales order has more than one order line instead of applying a separate hold on each line. This is particularly helpful when your order has hundreds or even thousands of lines. You can apply a single hold on the order header instead of applying a separate hold on each line.

This example applies a hold on an order header.

import oracle.apps.scm.doo.common.extensions.ValidationException;
import oracle.apps.scm.doo.common.extensions.Message;
import oracle.apps.scm.doo.common.extensions.HoldResult;

// Run the extension only on sales orders that are on hold.
def poNumber = header.getAttribute("CustomerPONumber")
if (poNumber != "ApplyHeaderHold") return;

if (!isFirstDraftOrder()) {
    // Make sure the order is in draft status, and not already submitted to fulfillment.
    debug("order is not in draft status");
    return;
}

// Make sure the current user has the privilege they need to apply hold. You can remove this code to meet your needs.
if (!context.isUserInRole("ORA_DOO_ORDER_MANAGER_JOB")) {
    debug("user does not have privilege");
    // Display a warning message that the user does doesn't have the privilege to apply a hold. As an option, you can display an error message or 
    // silently return from the extension without raising any error or warning.
    throw new ValidationException(new Message(Message.MessageType.WARNING, "User " + context.getUserName() + " does not have privilege to apply hold"));
}

if (canApplyHold(header, "DOO_SHIP_ALL")) {
    debug("Applying hold");
    def holdResult = header.applyHold("DOO_SHIP_ALL", "Apply Header Hold through Extension"); // Create a hold and use the DOO_SHIP_ALL hold code.
    def status = holdResult.getStatus();
    def message = holdResult.getMessage();
    if ("FAILURE".equals(status)) {
        throw new ValidationException(message);
    }
}

Apply Hold on Order Line

If you don't enable the FOM_NEW_HOLDS_PROCESSING profile, then you must explicitly set the value for the HoldComments attribute:
def hold = line.applyHold("DOO_RSRV");
                    hold.setAttribute("HoldComments", "comment"

This example applies a hold on an order line where the line's quantity is greater than 10.

import oracle.apps.scm.doo.common.extensions.ValidationException;
import oracle.apps.scm.doo.common.extensions.Message;

// Run the extension only on sales orders that are on hold.
def poNumber = header.getAttribute("CustomerPONumber")

if (poNumber != "ApplyHold") return;

if (!isFirstDraftOrder()) {
  // Make sure the order is in draft status, and not already submitted to fulfillment.
  debug("order is not in draft status");
  return;
}

// Make sure the current user has the privilege they need to apply hold. You can remove this code to meet your needs.
if (!context.isUserInRole("ORA_DOO_ORDER_MANAGER_JOB")) {
  debug("user does not have privilege");
  // Display a warning message to explain that the user doesn't have the privilege they need to apply a hold. You can remove this code to meet your needs.
  throw new ValidationException(new Message(Message.MessageType.WARNING, "The " + context.getUserName() + " user doesn't have the privilege they need to apply a hold."));
}

def lines = header.getAttribute("Lines");

while (lines.hasNext()) {
  def line = lines.next();

  // Make sure the ordered quantity is greater than 10.
  BigDecimal qty = line.getAttribute("OrderedQuantity");
  if (qty.compareTo(new BigDecimal(10)) > 0) {
    if (canApplyHold(line, "DOO_RSRV")) {
      debug("Applying hold");
      def hold = line.applyHold("DOO_RSRV"); // Create a hold and use the DOO_RSRV hold code.
      // Display text that explains what we're doing.
      hold.setAttribute("HoldComments", "We're holding this line so we can manually review and approve it.");
    }
  }
}

Apply Hold on Order Line when You Enable the Profile

If you enable the FOM_NEW_HOLDS_PROCESSING profile, then you can implicitly set the value for the HoldComments attribute: line.applyHold("DOO_RSRV", "comment");.

For example:

 
import oracle.apps.scm.doo.common.extensions.ValidationException;
import oracle.apps.scm.doo.common.extensions.Message;
import oracle.apps.scm.doo.common.extensions.HoldResult;
def poNumber = header.getAttribute("CustomerPONumber")
if(poNumber != "ApplyHold") return;
if( !isFirstDraftOrder() ) {
    debug("order is not in draft status");
    return;
}
if( !context.isUserInRole("ORA_DOO_ORDER_MANAGER_JOB") ) {
    debug("user does not have privilege");
    throw new ValidationException( new Message(Message.MessageType.WARNING, "User " + context.getUserName() + " does not have privilege to apply hold") );
}  

def lines = header.getAttribute("Lines");

while(lines.hasNext()) {
    def line = lines.next();
    BigDecimal qty = line.getAttribute("OrderedQuantity");
    if(qty.compareTo(new BigDecimal(10)) > 0) {
        if(canApplyHold(line, "DOO_RSRV")) {
            debug("Applying hold");
            def holdResult = line.applyHold("DOO_RSRV", "We're holding this line so we can manually review and approve it."); // Create a hold and use the DOO_RSRV hold code
            def status = holdResult.getStatus();
            def message = holdResult.getMessage();
            if("FAILURE".equals(status)) {
                throw new ValidationException(message);
            }
        }
    }
}

Apply Hold on Shipment Set

This example applies a hold on all lines in a shipment set where the ordered quantity is greater than 10.

import oracle.apps.scm.doo.common.extensions.ValidationException;
import oracle.apps.scm.doo.common.extensions.Message;

// These two lines are only for testing. They make sure the extension applies only when customer purchase order number is ApplyHold
def poNumber = header.getAttribute("CustomerPONumber")
if(poNumber != "ApplyHold") return;

if( !isFirstDraftOrder() ) {
    // Make sure the sales order is in draft status.
    debug("order is not in draft status");
    return;
}

// Make sure the current user has the privilege they need to apply a hold. This is optional.
if( !context.isUserInRole("ORA_DOO_ORDER_MANAGER_JOB") ) {
    debug("user does not have privilege");
    // Display a warning message here to explain that the user doesn't have the privilege to apply a hold. This is optional.
    throw new ValidationException( new Message(Message.MessageType.WARNING, "User " + context.getUserName() + " does not have privilege to apply hold") );
}  

// Map the lines that are on hold. We will populate this map while we put lines on hold.
Set linesOnHold = new HashSet<Long>();
Set shipsetsToHold = new HashSet<String>();

def lines = header.getAttribute("Lines");

while(lines.hasNext()) {
    def line = lines.next();
    
    // Make sure the line is eligible to apply a hold. Make sure the line has an ordered quantity that's greater than 10.
    BigDecimal qty = line.getAttribute("OrderedQuantity");
    if(qty.compareTo(new BigDecimal(10)) > 0) {
        if(canApplyHold(line, "DOO_RSRV")) {
            debug("Applying hold");
            def hold = line.applyHold("DOO_RSRV"); // Create a hold and use the DOO_RSRV hold code.
            hold.setAttribute("HoldComments", "We're holding this line so we can manually review and approve it.");
            Long fulfillLineId = line.getAttribute("FulfillmentLineIdentifier");
            linesOnHold.add(fulfillLineId);
          
            String shipsetName = line.getAttribute("ShipSetName");
            if( shipsetName != null ) {
                // The line that we're hold is part of a shipment set. Let's remember the shipset name in a set. Later we will make another pass over the lines to find other
                // lines in the same shipment set and hold them too.
                shipsetsToHold.add(shipsetName);
            }
        }
    }
}

// We are done making one pass over the lines. Some of the lines that we held might be part of a shipment set. Let's make another pass over all lines and hold the other lines
// from the shipment sets.

lines = header.getAttribute("Lines");

while(lines.hasNext()) {
    def line = lines.next();
    Long fulfillLineId = line.getAttribute("FulfillmentLineIdentifier");
    if( linesOnHold.contains(fulfillLineId) ) {
        // We already held this line during the first pass. Let's continue to the next line in the loop.
        continue; 
    }
  
    String shipsetName = line.getAttribute("ShipSetName");
    if( shipsetName != null ) {
        // The line is in a shipment set. Let's see whether we should hold this shipment set. If yes, then we will hold the line.
        if( shipsetsToHold.contains(shipsetName) ) {
            if(canApplyHold(line, "DOO_RSRV")) {
                debug("Applying hold");
                def hold = line.applyHold("DOO_RSRV"); // Create a hold and use the DOO_RSRV hold code
                hold.setAttribute("HoldComments", "We're holding this line so we can manually review and approve it.");
            }
        }
    }
}