Item Relations

There are many behaviors in complex forms that must be enforced dynamically at runtime, either to adhere to the field-level validation model of Oracle Applications, or to enforce specific business rules.

You should model your form's item and event handlers after these examples.

Disabled Items and WHEN-VALIDATE-ITEM Trigger

In most of the item relations you are dynamically disabling and enabling items. For your disabled items, note these Oracle Forms coding issues:

Dependent Items

To create a text item, check box, or poplist that is enabled only when a master item is populated, use the procedure APP_FIELD.SET_ DEPENDENT_FIELD. This routine enforces the following behaviors:

Create the item handler procedures as shown below and then call the procedures from the specified triggers.

Attention: These routines do not apply to display-only text items. To conditionally grey out display-only text items, use the routine APP_ITEM_PROPERTY.SET_VISUAL_ ATTRIBUTE.

See: APP_ITEM_PROPERTY: Property Utilities

In the following example, a block order has items item_type and item_name. Item_name is dependent on item_type, thus item_name is enabled only when item_type is NOT NULL.

  1. Create your item handler procedures as follows:

     PACKAGE BODY ORDER IS
         PROCEDURE ITEM_TYPE(EVENT VARCHAR2) IS
         BEGIN
           IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
             --- Any validation logic goes here.
             ITEM_NAME('INIT');
           ELSE
              fnd_message.debug('Invalid event passed to
                  ORDER.ITEM_TYPE: ' || EVENT);
           END IF;
         END ITEM_TYPE;
    
     PROCEDURE ITEM_NAME(EVENT VARCHAR2) IS
         BEGIN
           IF ((EVENT = 'PRE-RECORD') OR
               (EVENT = 'INIT')) THEN
             APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                           'ORDER.ITEM_TYPE',
                                           'ORDER.ITEM_NAME');
           ELSE
              fnd_message.debug('Invalid event passed to
                  ORDER.ITEM_NAME: ' || EVENT);
           END IF;
         END ITEM_NAME;
       END ORDER;
    
  2. Call your item handler procedures in:

    Trigger: WHEN-VALIDATE-ITEM on item_type:
    
    order.item_type('WHEN-VALIDATE-ITEM');
    
    Trigger: PRE-RECORD on order (Fire in Enter-Query Mode: No):
    
    order.item_name('PRE-RECORD');
    
  3. If your master and dependent items are in a multi-row block, or if they are items in a single-row block that is a detail of a master block, you must call SET_DEPENDENT_FIELD for the POST-QUERY event as well.

    PROCEDURE ITEM_NAME(EVENT VARCHAR2) IS
         BEGIN
           IF ((EVENT = 'PRE-RECORD') OR
               (EVENT = 'INIT') OR
               (EVENT = 'POST-QUERY')) THEN
              APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                           'ORDER.ITEM_TYPE',
                                           'ORDER.ITEM_NAME');
           ELSE
              fnd_message.debug('Invalid event passed to
                  ORDER.ITEM_NAME: ' || EVENT);
           END IF;
         END ITEM_NAME;
    

    Add another call to your item handler procedure in:

    Trigger: POST-QUERY
    
    ORDER.ITEM_NAME('POST-QUERY');
    

    Attention: In a multi-record block, if the dependent item is the last item in the record, the cursor navigates to the next record when tabbing from the master. To work around this behavior, code a KEY-NEXT-ITEM trigger that does a VALIDATE(Item_scope) and then a NEXT_ITEM.

    Attention: If the dependent item is a required list or option group, set the "invalidate" parameter in the call to APP_FIELD.SET_DEPENDENT_FIELD to TRUE. When this flag is TRUE, the dependent item is marked as invalid rather than cleared.

Conditionally Dependent Item

A conditionally dependent item is enabled or disabled depending on the particular value of the master item. In this example, the block order has items item_type and item_size. Item_size is enabled only when item_type is "SHOES."

  1. Create your item handler procedures as follows. Note that this item handler is very similar to the simple master/dependent situation, but you specify the condition instead of the name of the master item.

    PACKAGE BODY order IS
     PROCEDURE ITEM_TYPE(EVENT VARCHAR2) IS
     BEGIN
       IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
         size('INIT');
       ELSE 
          fnd_message.debug('Invalid event passed to
            ORDER.ITEM_TYPE: ' || EVENT); 
       END IF;
     END item_type;
     PROCEDURE size(EVENT VARCHAR2) IS
     BEGIN
       IF ((EVENT = 'PRE-RECORD') OR
           (EVENT = 'INIT')) THEN
         APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                      (:order.item_type = 'SHOES'),
                                       'ORDER.SIZE');
       ELSE 
          fnd_message.debug('Invalid event passed to
            ORDER.SIZE: ' || EVENT); 
       END IF;
     END size;
    END order;
    
  2. Call your item handler procedures in:

    Trigger: PRE-RECORD on order (Fire in Enter-Query Mode: No):
    
    order.item_size('PRE-RECORD');
    
    Trigger: WHEN-VALIDATE-ITEM on item_type:
    
    order.item_type('WHEN-VALIDATE-ITEM');
    

Multiple Dependent Items

There are cases where multiple items are dependent on a single master item. For example, only certain item_types can specify a color and size. Therefore, the color and size fields are dependent on the master field item_type, and they are enabled only when item_type is "RAINCOAT."

  1. Create your item handler procedures as follows:

    PACKAGE BODY order IS
     PROCEDURE item_type(EVENT VARCHAR2) IS
     BEGIN
       IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
        color('INIT');
        size('INIT');
       ELSE
          fnd_message.debug('Invalid event passed to
            ORDER.ITEM_TYPE: ' || EVENT);
       END IF;
     END item_type;
     PROCEDURE color(EVENT VARCHAR2) IS
     BEGIN
       IF (EVENT = 'PRE-RECORD') OR
          (EVENT = 'INIT') THEN
           APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                              (:order.item_type = 'RAINCOAT'),
                              'ORDER.COLOR');
       ELSE
          fnd_message.debug('Invalid event passed to
            ORDER.COLOR: ' || EVENT);
       END IF;
     END color;
     PROCEDURE size(EVENT VARCHAR2) IS
     BEGIN
       IF (EVENT = 'PRE-RECORD') OR
          (EVENT = 'INIT') THEN
         APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                              (:order.item_type = 'RAINCOAT'),
                              'ORDER.SIZE');
       ELSE
          fnd_message.debug('Invalid event passed to
            ORDER.SIZE: ' || EVENT);
       END IF;
     END size;
    END order;
    
  2. Call your item handler procedures in:

    Trigger: WHEN-VALIDATE-ITEM on order.item_type:
    
    order.item_type('WHEN-VALIDATE-ITEM');
    
    Trigger: PRE-RECORD (Fire in Enter-Query Mode: No):
    
    order.color('PRE-RECORD');
    order.size('PRE-RECORD');
    

Two Master Items and One Dependent Item

There may also be cases where an item is dependent on two master items. Suppose that different sizes of sweaters come in different colors. You cannot fill in the color of the sweater until you have filled in both item_type and size. The validation of block.dependent is controlled by the content of both master_1 and master_2.

  1. Create your item handler procedures as follows:

    PACKAGE BODY order IS
         PROCEDURE item_type(EVENT VARCHAR2) IS
         BEGIN
           IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
             color('INIT'):
       ELSE
          fnd_message.debug('Invalid event passed to
            ORDER.ITEM_TYPE: ' || EVENT);
           END IF;
         END item_type;
         PROCEDURE size(EVENT VARCHAR2) IS
         BEGIN
           IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
             color('INIT');
       ELSE
          fnd_message.debug('Invalid event passed to
            ORDER.SIZE: ' || EVENT);
           END IF;
         END size;
         PROCEDURE color(EVENT VARCHAR2) IS
         BEGIN
           IF (EVENT = 'PRE-RECORD') OR
              (EVENT = 'INIT') THEN
              APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                    ((:order.item_type IS NOT NULL) AND
                     (:order.size IS NOT NULL)),
                      'ORDER.COLOR');
           ELSE
            fnd_message.debug('Invalid event passed to
            ORDER.COLOR: ' || EVENT);
           END IF;
         END color;
    
    END order;
    
  2. Call your item handler procedures in:

    Trigger: WHEN-VALIDATE-ITEM on order.item_type:
    
    order.item_type('WHEN-VALIDATE-ITEM');
    
    Trigger: WHEN-VALIDATE-ITEM on order.size:
    
    order.size('WHEN-VALIDATE-ITEM');
    
    Trigger: PRE-RECORD (Fire in Enter-Query Mode: No):
    
    order.color('PRE-RECORD');
    

Cascading Dependence

With cascading dependence, item_3 depends on item_2, which in turn depends on item_1. Usually all items are in the same block.

For example, the block order contains the items vendor, site, and contact.

The list of valid sites depends on the current vendor.

The list of valid contacts depends on the current site.

To code the correct behavior for these dependent items, follow these steps.

  1. Create your item handler procedures as follows:

    PACKAGE BODY order IS
    PROCEDURE vendor(EVENT VARCHAR2) IS
    BEGIN
       IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
          SITE('INIT');
       ELSE
          fnd_message.debug('Invalid event passed to
            ORDER.VENDOR: ' || EVENT);
       END IF;
    END VENDOR;
    PROCEDURE SITE(EVENT VARCHAR2) IS
    BEGIN
       IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
          CONTACT('INIT');
       ELSIF (EVENT = 'PRE-RECORD') OR
             (EVENT = 'INIT') THEN
          APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                        'ORDER.VENDOR',
                                        'ORDER.SITE');
          CONTACT(EVENT);
       ELSE
          fnd_message.debug('Invalid event passed to
            ORDER.SITE: ' || EVENT);
       END IF;
    END SITE;
    PROCEDURE CONTACT(EVENT VARCHAR2) IS
    BEGIN
       IF (EVENT = 'PRE-RECORD') OR
          (EVENT = 'INIT') THEN
          APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                       'ORDER.SITE',
                                       'ORDER.CONTACT');
       ELSE
          fnd_message.debug('Invalid event passed to
            ORDER.CONTACT: ' || EVENT);
       END IF;
    END CONTACT;
    END ORDER;
    
  2. Call your item handler procedures in:

    Trigger: WHEN-VALIDATE-ITEM on vendor:
    
    order.vendor('WHEN-VALIDATE-ITEM');
    
    Trigger: WHEN-VALIDATE-ITEM on site:
    
    order.site('WHEN-VALIDATE-ITEM');
    
    Trigger: PRE-RECORD on order (Fire in Enter-Query Mode: No):
    
    order.site('PRE-RECORD');
    order.contact('PRE-RECORD');
    

    Remember that the following chain of events occurs whenever the VENDOR field is validated:

Mutually Exclusive Items

Use the procedure APP_FIELD.SET_EXCLUSIVE_FIELD to code two items where only one item is valid at a time.

The key to coding an item handler procedure for mutually exclusive items is to realize that mutually exclusive items are logically one item. Whenever one of a pair of mutually exclusive items is dependent on or depended upon by another item, they both are. Their relationship to other items is always identical. Therefore, code a single item handler procedure for the single logical item.

If both mutually exclusive items are NULL, then both items are navigable. If one item is populated, then the other item is unnavigable (you can still click there), and any value in that item is cleared.

If one item must be not null, set the REQUIRED property of both items to be Yes in the Oracle Forms Developer. If both items may be null, set the REQUIRED property of both items to be No. APP_FIELD.SET_ EXCLUSIVE_FIELD reads the initial REQUIRED property and dynamically manages the REQUIRED properties of both items.

You can also use the procedure APP_FIELD.SET_EXCLUSIVE_FIELD for a set of three mutually exclusive items. For more than three items, you must write your own custom logic.

Attention: Mutually exclusive check boxes and required lists require mouse operations.

For example, a block lines has mutually exclusive items credit and debit.

  1. Call your item handler procedures in:

     PACKAGE BODY lines IS
       PROCEDURE credit_debit(EVENT VARCHAR2) IS
       BEGIN
          IF ((EVENT = 'WHEN-VALIDATE-ITEM') OR
              (EVENT = 'PRE-RECORD')) THEN
             APP_FIELD.SET_EXCLUSIVE_FIELD(EVENT,
                                           'LINES.CREDIT',
                                           'LINES.DEBIT');
          ELSIF (EVENT = 'WHEN-CREATE-RECORD') THEN
             SET_ITEM_PROPERTY('lines.credit', ITEM_IS_VALID,
                                PROPERTY_TRUE);
             SET_ITEM_PROPERTY('lines.debit', ITEM_IS_VALID,
                                PROPERTY_TRUE);
          ELSE
            fnd_message.debug('Invalid event passed to
            Lines.credit_debit: ' || EVENT);
          END IF;
       END credit_debit;
    END lines;
    
  2. Create your item handler procedures as follows:

    Trigger: WHEN-VALIDATE-ITEM on credit:
    
    lines.credit_debit('WHEN-VALIDATE-ITEM');
    
    Trigger: WHEN-VALIDATE-ITEM on debit:
    
    lines.credit_debit('WHEN-VALIDATE-ITEM');
    
    Trigger: PRE-RECORD on lines (Fire in Enter-Query Mode: No):
    
    lines.credit_debit('PRE-RECORD');
    
    Trigger: WHEN-CREATE-RECORD on lines:
    
    lines.credit_debit('WHEN-CREATE-RECORD');
    

    You only need the WHEN-CREATE-RECORD trigger if the resulting one of your mutually-exclusive fields is required. This trigger initially sets all the mutually-exclusive fields of the set to be required. The fields are then reset appropriately once a user enters a value in one of them.

Mutually Inclusive Items

Use APP_FIELD.SET_INCLUSIVE_FIELD to code a set of items where, if any of the items is not null, all of the items are required.

The item values may be entered in any order. If all of the items are null, then the items are optional.

You can use the procedure APP_FIELD.SET_INCLUSIVE_FIELD for up to five mutually inclusive items. For more than five items, you must write your own custom logic.

This example shows a block payment_info with mutually inclusive items payment_type and amount.

  1. Create your item handler procedures as follows:

     PACKAGE BODY payment_info IS
       PROCEDURE payment_type_amount(EVENT VARCHAR2) IS
       BEGIN
          IF ((EVENT = 'WHEN-VALIDATE-ITEM') OR
              (EVENT = 'PRE-RECORD')) THEN
             APP_FIELD.SET_INCLUSIVE_FIELD(EVENT,
                                     'PAYMENT_INFO.PAYMENT_TYPE',
                                     'PAYMENT_INFO.AMOUNT');
          ELSE
             fnd_message.debug('Invalid event to
               payment_info.payment_type_ amount: ' || EVENT);
          END IF;
       END payment_type_amount;
    END payment_info;
    
  2. Call your item handler procedures in:

    Trigger: WHEN-VALIDATE-ITEM on payment_info.payment_type:
    payment_info.payment_type_amount('WHEN-VALIDATE-ITEM');
    
    Trigger: WHEN-VALIDATE-ITEM on payment_info.amount:
    
    payment_info.payment_type_amount('WHEN-VALIDATE-ITEM');
    
    Trigger: PRE-RECORD on payment_info (Fire in Enter-Query Mode: No):
    
    payment_info.payment_type_amount('PRE-RECORD');
    

Mutually Inclusive Items with Dependent Items

There are cases where items are dependent on master items, where the master items are mutually inclusive.

See: Item Relations.

This example shows a block payment_info with mutually inclusive items payment_type and amount, just as in the previous example. The block also contains two regions, one for check information and one for credit card information. Check Information has a single item, check_number. Credit Card Information has five items: credit_type, card_holder, number, expiration_date, and approval_code.

Payment Type can be Cash, Check, or Credit.

  1. Create your item handler procedures as follows:

    PACKAGE BODY payment_info IS
    PROCEDURE payment_type_amount(EVENT VARCHAR2) IS
    BEGIN
       IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
          APP_FIELD.SET_INCLUSIVE_FIELD(EVENT,
     
                                       'PAYMENT_INFO.PAYMENT_TYPE',
                                       'PAYMENT_INFO.AMOUNT');
          IF (:SYSTEM.CURSOR_ITEM =
              'payment_info.payment_type') THEN
             check_info('INIT');
             credit_info('INIT');
          END IF;
       ELSIF (EVENT = 'PRE-RECORD') THEN
          APP_FIELD.SET_INCLUSIVE_FIELD(EVENT,
                                  'PAYMENT_INFO.PAYMENT_TYPE',
                                  'PAYMENT_INFO.AMOUNT');
       ELSE
          fnd_message.debug('Invalid event in
            payment_info.payment_type_amount: ' || EVENT);
       END IF;
    END payment_type_amount;
    
     PROCEDURE check_info IS
    BEGIN
       IF ((EVENT = 'PRE-RECORD') OR
           (EVENT = 'INIT')) THEN
          APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
            (:payment_info.payment_type = 'Check'),
            'PAYMENT_INFO.CHECK_NUMBER');
       ELSE
          fnd_message.debug('Invalid event in
             payment_info.check_info: ' || EVENT);
       END IF;
    END check_info;
    PROCEDURE credit_info IS
       CONDITION BOOLEAN;
    BEGIN
       IF ((EVENT = 'PRE-RECORD') OR
           (EVENT = 'INIT')) THEN
          CONDITION := (:payment_info.payment_type = 'Credit');
          APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                    CONDITION,
                                   'PAYMENT_INFO.CREDIT_TYPE');
          APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                    CONDITION,
                                    'PAYMENT_INFO.NUMBER');
          APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                    CONDITION,
                                    'PAYMENT_INFO.CARD_HOLDER');
          APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                    CONDITION,
                                   'PAYMENT_INFO.EXPIRATION_DATE');
          APP_FIELD.SET_DEPENDENT_FIELD(EVENT,
                                   CONDITION,
                                   'PAYMENT_INFO.APPROVAL_CODE');
       ELSE
         fnd_message.debug('Invalid event in
             payment_info.credit_info: ' || EVENT);
       END IF;
    END credit_info;
    END payment_info;
    
  2. Call your item handler procedures in:

    Trigger: WHEN-VALIDATE-ITEM on payment_info.payment_type:
    payment_info.payment_type_amount('WHEN-VALIDATE-ITEM');
    
    Trigger: WHEN-VALIDATE-ITEM on payment_info.amount:
    payment_info.payment_type_amount('WHEN-VALIDATE-ITEM');
    
    Trigger: PRE-RECORD on payment_info (Fire in Enter-Query Mode: No):
    payment_info.payment_type_amount('PRE-RECORD');
    payment_info.check_info('PRE-RECORD');
    payment_info.credit_info('PRE-RECORD');
    

Conditionally Mandatory Items

Use the procedure APP_FIELD.SET_REQUIRED_FIELD to code an item that is only mandatory when a certain condition is met. If the condition is FALSE, the dependent item is optional. Any value in the dependent item is not cleared. If an item is both conditionally required and dependent, call APP_FIELD.SET_DEPENDENT_FIELD before calling APP_FIELD.SET_REQUIRED_FIELD.

An example demonstrates using APP_FIELD.SET_REQUIRED_FIELD.

A block purchase_order has items total and vp_approval. Vp_approval is required when total is more than $10,000. (Note: quantity * unit_price = total).

  1. Create your item handler procedures as follows:

    PACKAGE BODY purchase_order IS
    PROCEDURE vp_approval(EVENT VARCHAR2) IS
    BEGIN
       IF ((EVENT = 'PRE-RECORD') OR
           (EVENT = 'INIT')) THEN
          APP_FIELD.SET_REQUIRED_FIELD(EVENT,
                                 (:purchase_order.total > 10000),
                                  'PURCHASE_ORDER.VP_APPROVAL');
       ELSE
          fnd_message.debug('Invalid event in
            purchase_order.vp_approval: ' || EVENT);
       END IF;
    END vp_approval;
    PROCEDURE total(EVENT VARCHAR2) IS
    BEGIN
       IF (EVENT = 'INIT') THEN
          :purchase_order.total := :purchase_order.quantity *
                                   :purchase_order.unit_price;
          vp_approval('INIT');
       ELSE
          fnd_message.debug('Invalid event in purchase_order.total: ' || EVENT);
    END total;
    PROCEDURE quantity(EVENT VARCHAR2) IS
    BEGIN
       IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
          total('INIT');
       ELSE
          fnd_message.debug('Invalid event in
            purchase_order.quantity: ' || EVENT);
       END IF;
    END quantity;
    PROCEDURE unit_price(EVENT VARCHAR2) IS
    BEGIN
       IF (EVENT = 'WHEN-VALIDATE-ITEM') THEN
          total('INIT');
       ELSE
          fnd_message.debug('Invalid event in
            purchase_order.unit_price: ' || EVENT);
       END IF;
    END unit_price;
    END purchase_order;
    
  2. Call your item handler procedures in:

    Trigger: PRE-RECORD on purchase_order (Fire in Enter-Query Mode: No):
    
    purchase_order.vp_approval('PRE-RECORD');
    
    Trigger: WHEN-VALIDATE-ITEM on quantity:
    
    purchase_order.quantity('WHEN-VALIDATE-ITEM');
    
    Trigger: WHEN-VALIDATE-ITEM on unit_price:
    
    purchase_order.unit_price('WHEN-VALIDATE-ITEM');