Chapter 14

Human Resources: Benefits Maintenance


CONTENTS


Introduction

In Chapter 13, "Employee Files," you set out to create an application that can create, update, and delete the employee files. In this chapter, you design and implement an application that takes this concept one step further. It not only extends the use of the infamous employee table, but it also allows the users of your intranet to make changes to their company benefits.

This chapter follows the same format as the last chapter. This chapter covers the following topics in regard to the Benefits Maintenance application:

This four step format is used throughout all of the sample application chapters. Hopefully it provides you with valuable insight and ideas for creating your own intranet applications.

Application Design

This application, not unlike the Employee Files, is semi-modeless. The user can flow through the program and only some options are available, depending on the state of the current row. The application prompts the user to store any unsaved changes he has made. This is done before any actions such as New or Choose are processed.

Figure 14.1 is the proposed user interface for the Benefits Maintenance program.

Figure 14.1 The benefits maintenance user interface.

This application requires a parent row to exist in the employee table. This forces you to make the user choose an employee to work with. After choosing that employee, this application allows the user to manipulate four benefits-related data items:

This program also reuses the employee pick list. This pick list is stored in the jif.common package so that it can be reused easily.

Database Design

This application is responsible for manipulating employee benefit rows. These rows should be stored in a single table. Because not all employees have benefits to track, this information belongs in its own table. The table used in this sample application is called the employee benefits table.

The information stored in the employee benefits table corresponds to the information that is to be edited, as described earlier. Table 14.1 shows the columns needed to store in the benefits table.

Table 14.1. The employee benefits table layout.

DescriptionColumn Name TypeCan Be Null? Default
Employee ID emp_idnumber(5) NNone
Number of Exemptions exemptions_nbr number(2)N 0
Married Indicator married_ind char(1)N 'N'
Plan Participant Indicator plan_part_ind char(1)N 'N'
Payroll Deduction Percentage payroll_ded_pct number(3)N 0

Figure 14.2 shows the entity relationship diagram for the database as it stands in this chapter. As you get deeper into the sample applications, you see the entity relationship diagram grow to encompass all the tables.

Figure 14.2 : The entity relationship diagram including the benefits table

Note
Entity relationship diagrams are discussed in Chapter 15.

In addition to creating a table, you create a database synonym for the table. This allows everyone to access the table with the same name and not have to worry about the schema in which the table resides.

This table is going to be a child of the employee table, which means that no rows can exist in this table unless there is a corresponding row in the employee table. Enforcing the parent-child relationship is called referential integrity. It ensures the integrity of the references in the database. Referential integrity is most often achieved through the use of foreign keys.

A foreign key is a link from a child table back to a parent table. This link allows the database to impose restrictions on many database actions, such as the following:

Tip
There is one exception to the first foreign key restriction. Some databases allow you to perform what is called a delete cascade. This means that the deletion of a parent row cascades down and deletes all child rows. This can be very useful in complex database structures where tens or hundreds of child rows exist. However, with power comes danger and responsibility. You can do some serious harm to the database if this is done unwittingly. Be careful how you use the delete cascade

Listing 14.1 shows the SQL commands to create the employee benefits table.


Listing 14.1. The employee benefits table creation SQL.
/*    Create the table */
create table emp_benft_t
(
    emp_id                number( 5 ) not null,
    exemptions_nbr        number( 2 ) default 0 not null,
    married_ind            char( 1 ) default 'N' not null,
    plan_part_ind        char( 1 ) default 'N' not null,
    payroll_ded_pct    number( 3 ) default 0 not null
);

/*    Create a primary key */
alter table emp_benft_t
    add
    (
        primary key
        (
            emp_id
        )
    );

/*    Create a foreign key */
alter table emp_benft_t
    add
    (
        foreign key
        (
            emp_id
        )
        references emp_t
    );

/*    Grant access for the table to the user role */
grant select,insert,delete,update on emp_benft_t to ia_user_r ;

/*    Drop any existing public synonym */
drop public synonym emp_benft ;

/*    Create a public synonym for our table */
create public synonym emp_benft for emp_benft_t ;

Note
The SQL in Listing 14.1 is quite generic, but it might not work on every database. This particular SQL has been tested with Oracle.

The first SQL clause creates the table emp_benft_t. The second clause creates a primary key using the emp_id column. Making this the primary key ensures that the values in the column are unique across all rows.

The third SQL clause creates the foreign key. The foreign key in this table is the emp_id column. This column points back to, or references, the emp_id column in the emp_t table.

Lastly, the public synonym emp_benft is created for the table emp_benft_t.

After you create this table, you are ready to build the application.

Caution
You must create the emp_t (Employee) table before you can create the emp_benft_t (Employee Benefits) table. Otherwise, the emp_benft_t SQL fails!

Implementation

The rest of this chapter discusses the implementation of the Benefits Maintenance program. The first feature discussed is the user interface and how it is created. Secondly, the database access used in the program is discussed. Finally, some of the programming considerations that come up during the application construction are discussed.

Each sample application in this book uses a different approach to developing the user interface. This variety shows you the different ways you can do your own interfaces. Hopefully, you get a cross-section of many different styles and can choose the one that suits you the best.

User Interface

The screen layout for this application is presented in a manner that is achieved through the use of a GridBagLayout. This is exactly the same approach that was taken in the Employee Files application. The difference here is that a Label variable for each label is not created.

Listing 14.2 shows the user interface construction code for the Employee Benefits program.


Listing 14.2. The Employee Benefits interface construction source code.
//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    JifTextField                 emp_id;
    JifTextField                 full_name;
    JifTextField                exemptions_nbr;
    JifCheckbox                    married_ind;
    JifCheckbox                    plan_part_ind;
    JifTextField                payroll_ded_pct;

//****************************************************************************
//* Constructor                                          ;                     *
//****************************************************************************

    public
    BenefitsUI( SimpleDBJiflet jiflet )
    {
        super( jiflet );

        GridBagLayout gbl = new GridBagLayout();

        int cw[] = { 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
            14, 14, 14, 14 }; // 17
        int rh[] = { 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14 }; // 12

        double rc14_0[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        gbl.columnWidths = new int[ 17 ];
        gbl.rowHeights = new int[ 12 ];

        gbl.columnWeights = new double[ 17 ];
        gbl.rowWeights = new double[ 12 ];

        System.arraycopy( cw, 0, gbl.columnWidths, 0, 17 );
        System.arraycopy( cw, 0, gbl.rowHeights, 0, 7 );

        System.arraycopy( rc14_0, 0, gbl.columnWeights, 0, 17 );
        System.arraycopy( rc14_0, 0, gbl.rowWeights, 0, 7 );

        setLayout( gbl );

        //    Do the labels...
        addWithConstraints( new Label( "Employee ID:", Label.RIGHT ),
            "anchor=east;x=0;y=0" );

        addWithConstraints( new Label( "Name:", Label.RIGHT ),
            "anchor=east;x=0;y=1" );

        addWithConstraints( new Label( "Exemptions:", Label.RIGHT ),
            "anchor=east;x=0;y=3" );

        addWithConstraints( new Label( "Married:", Label.RIGHT ),
            "anchor=east;x=0;y=4" );

        addWithConstraints( new Label( "Plan Participant:", Label.RIGHT ),
            "anchor=east;x=0;y=5" );

        addWithConstraints( new Label( "Payroll Deduction:", Label.RIGHT ),
            "anchor=east;x=0;y=6" );

        //    Add some fields...
        emp_id = new JifTextField( "", "emp_id", true );
        emp_id.disable();
        addWithConstraints( emp_id, "x=1;y=0;width=5;fill=horizontal" );

        full_name = new JifTextField();
        full_name.disable();
        addWithConstraints( full_name, "x=1;y=1;width=13;fill=horizontal" );

        exemptions_nbr = new JifTextField( "", "exemptions_nbr" );
        addWithConstraints( exemptions_nbr, "x=1;y=3;width=7;fill=horizontal" );

        married_ind = new JifCheckbox( "married_ind" );
        addWithConstraints( married_ind, "x=1;y=4" );

        plan_part_ind = new JifCheckbox( "plan_part_ind" );
        addWithConstraints( plan_part_ind, "x=1;y=5" );

        payroll_ded_pct = new JifTextField( "", "payroll_ded_pct" );
        addWithConstraints( payroll_ded_pct, "x=1;y=6;width=7;fill=horizontal" );

        //    Disable buttons...
        saveButton.disable();
        chooseButton.disable();
        deleteButton.disable();

        //    Add the buttons...
        addWithConstraints( newButton, "x=15;y=0;width=2;fill=horizontal" );
        addWithConstraints( saveButton, "x=15;y=2;width=2;fill=horizontal" );
        addWithConstraints( deleteButton, "x=15;y=4;width=2;fill=horizontal" );
        addWithConstraints( chooseButton, "x=15;y=6;width=2;fill=horizontal" );

        //    Tell which are numeric...
        emp_id.setNumeric( true );
        exemptions_nbr.setNumeric( true );
        payroll_ded_pct.setNumeric( true );

        //    Set the focus to the first field...
        setFocus( exemptions_nbr );
    }

A notable item about this user interface (and other GridBagLayout jiflets in this book) is that the grid settings are hard-coded. A permanent 12 row by 17 column grid is used for the user interface. Components are placed within the grid only after the grid is set.

Also of note is the fact that you disable the employee ID and name JifTextField components:

        emp_id.disable();
        full_name.disable();

This forces them into a read-only mode. They accept no input and cannot be changed. In addition, their background color is shaded to indicate the disablement.

You disable these fields because they are references from the parent row. This parent row is chosen by using the Choose button and the employee pick list.

The Employee Pick List Revisited

The EmployeePickList class is developed for the Employee Files application in Chapter 15. However, it can be reused without modification in this application as well.

The EmployeePickList object is created and displayed in the main program.

When the user presses the Choose button-which is one of the JifMessages (JifMessage.chOOSE)-an ACTION_EVENT event is generated and sent to the parent of the panel. After it is received, you need to open up the employee pick list. Before you can switch employees though, you must store any changes the user has made to the current row. The following code snippet is from the action() method of the Benefits program:

        if ( event.target == getUIPanel() )
        {
            switch ( ( ( Integer )arg ).intValue() )
            {
                case JifMessage.chOOSE:
                    if ( getDBRecord().didDataChange() )
                    {
                        chgDlg = new ResponseDialog( this,
                        "Data Change",
                        "The record has changed.\n" +
                        "Do you wish to save your changes?",
                        "Yes,No,Cancel" );

                        chgDlg.show();
                    }
                    else
                    {
                        chooseEmployee();
                    }
                    return( true );
            }
        }

If there are no changes to save, the method chooseEmployee() is called. This method creates and displays an EmployeePickList object.

When the user selects a pick list item or closes the pick list window, it generates an ACTION_EVENT event. You capture this event and act accordingly. Here's what this looks like:

        //    Handle picklist events...
        if ( event.target instanceof EmployeePickList )
        {
            int                    rv = ( ( Integer )arg ).intValue();
             EmployeePickList     epl = ( EmployeePickList )event.target;

            if ( rv != -1 )
            {
                //    Disable save on choose...
                getUIPanel().saveButton.disable();

                //    Enable delete...
                getUIPanel().deleteButton.enable();

                //    Display it on the screen...
                EmployeeRecord er = epl.getRecord( rv );

                //    Get a benefits record...
                BenefitsRecord br = getBenefitsRow( er );

                //    Set it in there...
                setDBRecord( ( DBRecord )br );
                getUIPanel().moveToScreen();
            }

            //    Kill the dialog box...
            epl.hide();
            epl.dispose();

            //    Reset the focus...
            getUIPanel().requestFocus();

            //    We handled it...
            return( true );
        }

If the pick list returns a -1 value, you know that the user has canceled his selection. Otherwise, the value returned is the row number that is selected. You retrieve the EmployeeRecord at that row, make it the current row, and request that the user interface display it.

Finally, a little cleanup is in order. You hide() and dispose() of the pick list window and then reset the focus back to your window.

Moving Data to the Screen

Each SimpleDBUI derived class has a moveToScreen() method that moves the data from the instance variables to the screen. The BenefitsUI class is no different. However, there are characteristics of this user interface that require special programming:

  1. The use of indicator columns. These columns hold a yes or no value. However, you want them to be represented on the screen as a checkbox.
  2. Some of the displayed information is from a second table (the employee table). You need to retrieve this information and display it.

Let's examine how you accomplish each of these programming tasks.

Indicator Columns
An indicator column is one that typically holds a Boolean value. Usually, this is represented by a Y or an N for yes or no, respectively. However, to make it easy for the user to interact with this format, a checkbox can be used.
The checkbox is a binary representation as well. It can be checked or not checked. You extend this use to say that if the checkbox is checked, it is a Y, or yes. If it is not checked, this is an N, or no.
So setting the checkbox to the right value is as simple as checking the value of the indicator. The code for checking the marriage indicator is as follows:
if ( er.married_ind != null )
    married_ind.setState( er.married_ind.equalsIgnoreCase( "Y" ) );
else
    married_ind.setState( false );
If the er.married_ind variable is not null, you set the state of the checkbox to checked if the married indicator is equal to Y. Otherwise, you set it to N.
You must distinguish this null value from N because a NullPointerException is thrown if it is null, and you try to compare its value with Y.
Displaying Parent Record Values
The second interesting programming technique used in this application is displaying information from a second database table.
As you know, your employee benefits table is a child table of the employee table. This means that no real employee information is stored in the benefits table. Because you want to display the name of the employee you're working with, you need to retrieve that employee's name from the employee table.
You can easily retrieve the employee name using the CodeLookerUpper class. This handy class accepts the following information in its constructor:
Armed with this information, the CodeLookerUpper concatenates it to build an SQL select statement. The select statement ends up like this:
select <string columns> from <table name> where <key column> = ?
The bracketed items are filled in with information from the constructor. The question mark (?) is filled in when the SQL is actually used. The CodeLookerUpper contains a single method called lookupCode(). This is where the SQL statement is completed and executed.
Therefore, the code to initialize a CodeLookerUpper is as follows:
CodeLookerUpper clu = new CodeLookerUpper( getJiflet().getConnector(),
    "emp", "emp_id", "first_name || ' ' || last_name" );
You initialize it with the connector from your current jiflet, the employee table's synonym emp, the emp table's primary key emp_id, and the two columns you need to have concatenated at the server.
Then you ask the object to retrieve the data for you:
full_name.setText( clu.lookupCode( er.emp_id ) );
You pass the lookupCode() method the employee ID from your current EmployeeRecord. The result is moved directly into the full_name JifTextField.

Caution
The CodeLookerUpper expects that the first column returned by a query is a string column. If it is not, an SQLException is thrown. You can return multiple columns as you have done here. However, they must be concatenated at the database level.

Database Access

The Employee Benefits program communicates with the database through the use of an EmployeeBenefitsRecord object. This DBRecord derivation knows how to create, read, update, and delete rows from the employee benefits table. The following are the instance variables of this class:
//****************************************************************************
//* Constants                                         & nbsp;                      *
//****************************************************************************

    public final static String     TABLE_NAME = "emp_benft";

//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    //    A variable for each table column...
    public int                    emp_id = -1;
    public int                    exemptions_nbr = 0;
    public String                married_ind = "N";
    public String                plan_part_ind = "N";
    public int                    payroll_ded_pct = 0;

Note
The EmployeeBenefitsRecord and other database classes are reused in several other applications. They are placed in their own package along with other shared code. This package is called jif.common. It contains all the common classes between all the applications.

Because the pick list returns an EmployeeRecord, you need to retrieve the BenefitsRecord if one exists for this employee. This is done like so:
//****************************************************************************
//* getBenefitsRow                                        &n bsp;                  *
//****************************************************************************

    public BenefitsRecord
    getBenefitsRow( EmployeeRecord er )
    {
        BenefitsRecord     br = new BenefitsRecord();
        String            sql = "";

        sql = "select * from emp_benft where " +
            "emp_id = " + Integer.toString( er.emp_id );

        try
        {
            if ( getConnector().getStatement().execute( sql ) )
            {
                ResultSet rs = getConnector().getStatement().getResultSet();

                if ( rs.next() )
                    br.parseResultSet( rs );
                else
                {
                    br.clear();
                    br.emp_id = er.emp_id;
                }
            }
        }
        catch ( SQLException e )
        {
            errorLog( sql + " generated: " + e.toString() );
            return( null );
        }

        //    Return the record...
        return( br );
    }

Given an EmployeeRecord, you search the table for an associated row in the employee benefits table. If it is found, you allow the BenefitsRecord to parse the results. Otherwise, the default values are returned.

Programming Considerations

This application builds upon your base of Employee Files and adds more functionality. It provides your intranet users with the ability to modify their benefits parameters at will. You again used the stock SimpleDBUI and SimpleDBJiflet classes as a base, allowing you to quickly put together this application.

This application introduces the following Java intranet programming topics:

Summary

This chapter introduces you to the second sample application in the intranet application suite-the Benefits Maintenance application. This program is responsible for creating, updating, and deleting rows from the employee benefits table, and it is useful for normal employees who want to manage their own benefits.
In Chapter 15, "Conference Room Scheduling," you design and create an application that allows employees to schedule conference rooms for meetings well into the future.