You can create a trigger procedure in the Procedure Editor, but you attach it to your database in the Data Dictionary, which provides an editor designed to let you code your triggers there as well.

Note: Alternatively, you can do similar steps in the DB Structure view of Progress Developer Studio for OpenEdge.
To access triggers:
  1. From the Procedure Editor menu, select Tools > Data Dictionary and then select a database table such as OrderLine.
  2. Click Table Properties, then click the Triggers button. The Table Triggers dialog box appears:
  3. To see the WRITE trigger that calculated the ExtendedPrice for the OrderLine when it changed, select WRITE from the Event drop-down list:

When you create a new trigger procedure in the Data Dictionary, it supplies the header statement for you. Then you simply write the rest of the code for the procedure. You must give the procedure a name and specify a pathname either in the Procedure field or by selecting the Files button. It is this name that the Data Dictionary associates with the procedure so that the AVM executes it at the right times. As you can see, the trigger procedures for the Sports2020 database are located in the directory sports2020trgs under the install directory for OpenEdge. The WRITE trigger for the OrderLine table is called wrordl.p. You can use any naming convention you want for your trigger procedures.

You can click the Help button to access descriptions of all the other buttons here. This section mentions just the two Options that are shown as toggle boxes in the corner of the dialog box.

A trigger procedure provides a certain measure of security that a validation check, or an effect elsewhere in the database, occurs reliably whenever a certain type of update occurs. If you wish to protect yourself against a trigger procedure being replaced by another procedure that does not do the same job, you can check the Check CRC toggle box. If this option is on, then the AVM stores in the metaschema, along with the trigger procedure name, a unique Cyclic Redundancy Check (CRC) identifier for the compiled version of the trigger procedure. The AVM raises an error if the r-code file it encounters at run time does not match or if there is no compiled version of the procedure.

You can check on the other toggle box, Overridable, if you want to let the trigger procedure be overridable by a trigger local to a specific application procedure, called a session trigger. Session triggers are discussed briefly in the Session triggers section. Session triggers allow you to provide the effects of a trigger but without making it global to the entire application. Among other things, they let you override a trigger procedure with behavior more appropriate to a particular application module. If you don’t check on the Overridable toggle box, then the AVM raises an error if a session trigger executes that tries to override the behavior of this trigger procedure.

Having said all this, look at the statement in wrordl.p that calculates the ExtendedPrice:
ExtendedPrice = Price * Qty * (1 - (Discount / 100)).

Is this a good use for a trigger? Probably not, because it definitely violates the guideline that trigger procedures should not contain real business logic. In any real application, this kind of price calculation is complex and variable, depending on any number of factors. It is probably better to provide access to the price calculation algorithm in the application module that controls OrderLine maintenance and to make sure that your application is put together in such a way that the code is always executed when it needs to be. Burying the code in a trigger is not a good thing. Generally, the Sports2020 trigger procedures can serve as examples of how to WRITE triggers, but are often not good examples. This is partly because the database is simplified in ways that are not always realistic and, partly because many of these example procedures predate the architecture for distributed applications and other features that have changed the way you build applications. (Many contain MESSAGE statements, for example, which is definitely a bad idea.)