by Dr. Peter Wayne
| This article teaches you how program your own incrementing field. You also learn how to use the form's Timer property and OnTimer event, and are exposed to real-life multiuser programming consideraions. |
Let's review basics for a minute. If you define an autoincrement field for a table, then Alpha Five assigns a new, consecutive numeric or character value to each new record added to the table. For most applications, Alpha Five's built-in autoincrement field type is the ideal field type to use to create unique identifiers for each record. Customer numbers, invoice numbers, job numbers, ticket numbers - all are successfully represented by autoincrement fields.
There are occasions, however, in which an autoincrement field just won't do. There are 3 circumstances in which you might want to change the built-in autoincrement field for one of your own. These are:
Alpha Five stores the autoincrement field information in the table's
.dbf file and data dictionary. We're going to create a different kind of
field - let's call it simply an incrementing field. For a simple
example, let's take a ticket agency that sells consecutive series of
tickets to a number of events.
As we develop this one-table application, we'll also learn a little bit
about multiuser programming, error trapping and using the form's OnTimer
event.
For this article, we'll begin with a simple table, ticket_sales.dbf:
| Field Name | Type | Width |
|---|---|---|
| Date | Date | 8 |
| Agent | Character | 10 |
| Amount | Numeric | 8.2 |
| Sell_to | Character | 10 |
| Ticket_Number | Numeric | 6.0 |
We'll make the Ticket_Number field into an incrementing
field. We need a table to store the last value of ticket_number.
We'll call this table ticket_counter.dbf. It has one field, ticket_number,
defined as Numeric, width 6, 0 decimal places.
Every time we create a new entry in ticket_sales, we'll obtain
the last ticket number from ticket_counter and use that number as
the default value for the ticket_number field in ticket_sales.
There will only be one record in the ticket_counter table.
Where should we place the script that does this? The script should be
attached to the OnEnterRecord event for the ticket_sales
table. Here is the first rendition of the script:
| dim tbl as p dim counter as p dim ticket_num as n dim ix as p tbl=table.get("ticket sales") counter=table.open("ticket_counter",file_rw_exclusive) ix=counter.index_primary_put() if ix.records_get()=0 then |
||
| counter.enter_begin() | ||
| counter.ticket_number=1 | ||
| counter.enter_end(.t.) | ||
| else | ||
| counter.change_begin() | ||
| counter.ticket_number=counter.ticket_number+1 | ||
| counter.change_end(.t.) | ||
| end if ticket_num=counter.ticket_number counter.close() tbl.ticket_number=ticket_num end |
||
You'll notice that this program first checks to see if the ticket_counter
table is empty. If it is indeed empty, the script creates the sole record
and assigns a ticket_number of 1 to the ticket series. If the table is not
empty, then the script reads the value of the last ticket number,
increments it by one, writes it back to the counter table, and then uses
that value in the current ticket_sales record.
| Special considerations for multiuser programming |
Alpha Five's programmers have taken care to check for conflicts whenever
two interactive users want to change the same record: Alpha Five gives
precedence to the first user and locks the second one out until the first
one has finished. We have to exercise the same care when we write an
Xbasic script that changes a record. That's why we opened the ticket_counter
table in file_rw_exclusive mode. We have to be sure that only one
user at a time can make changes to the counter record. Once we finish
updating that counter record, we immediately close the ticket_counter
table so that another user can access it.
But what happens if one user tries to access the ticket_counter
table during the instant that another user has locked it?
Ooops - the script will fail.
Script 1 requires a run-time error trap at
the line,
counter=table.open("ticket_counter.dbf",file_rw_exclusive)
to recover from a failure to obtain exclusive access to ticket_counter.
What we will do is conceptually very simple: if we can't obtain
exclusive access to ticket_counter, we'll wait a second and try
again. We'll make up to 3 tries. If we can't get access by the 3rd try,
ask the user if she wants to keep trying or if she wants to quit, since
there may be some system error preventing her from getting access to the
table. Here's the script:
| dim tbl as p dim counter as p dim ticket_num as n dim ix as p dim tries as n tries=0 tbl=table.get("ticket sales") on error goto no_open counter=table.open("ticket_counter",file_rw_exclusive) on error goto 0 ix=counter.index_primary_put() if ix.records_get()=0 then |
||
| counter.enter_begin() | ||
| counter.ticket_number=1 | ||
| counter.enter_end(.t.) | ||
| else | ||
| counter.change_begin() | ||
| counter.ticket_number=counter.ticket_number+1 | ||
| counter.change_end(.t.) | ||
| end if ticket_num=counter.ticket_number counter.close() tbl.ticket_number=ticket_num end no_open: on error goto 0 if tries<3 then |
||
| sleep(1) tries=tries+1 resume 0 |
||
| else | ||
| reply=ui_msg_box("Unable to
update counter","Keep trying?",UI_YES_NO) if reply=UI_YES_SELECTED then |
||
| tries=0 resume 0 |
||
| else | ||
| end | ||
| end if | ||
| end if | ||
| Create a form for ticket entries |
Now, let's create a form for ticket entries. On the form, we'll display
the next available ticket number as a calculated field on the form. The
calculated field's expression is
LOOKUP("ticket_counter", "recno()=1","ticket_number")+1
The lookup()function is new to Alpha Five version 3. It can
take the place of the lookupc(), lookupn(), and lookupd()functions
in previous Alpha products. The expression for this calculated field gets
the value of the ticket_number field in ticket_counter's
first (and only) record, by applying the filter recno()=1.
We'll also place a button on the form to allow the user to reset the value for the next available ticket number. Don't worry about the script for this button yet - we'll get to it later. The form should look like this:

| Create a script to allow the user to change the next ticket number |
Now we have to program the button to allow the user to reset the ticket_number
field in ticket_counter. The code to do that is fairly
straightforward:
| dim counter as p dim next_num as n dim cnext as c dim tries as n next_num=lookup("ticket_counter.dbf","recno()=1","ticket_number")+1 cnext=alltrim(str(next_num,6,0)) cnext=ui_get_number("Reset ticket number","Enter new number",cnext) if val(cnext)<1 then |
||
| end | ||
| else | ||
| tries=0 on error goto table_in_use counter=table.open("ticket_counter.dbf",file_rw_exclusive) on error goto 0 counter.change_begin() |
||
| counter.ticket_number=val(cnext)-1 | ||
| counter.change_end(.t.) counter.close() parent.resynch() |
||
| end if end table_in_use: on error goto 0 tries=tries+1 sleep(1) if tries<3 then |
||
| resume 0 | ||
| else | ||
| ui_msg_box("Unable to lock ticket
counter table","Try later, please!") end |
||
| end if | ||
The parent.resynch() in this button script makes sure that
the form displays the newly updated Next available ticket number
field on the form. Be aware that in a multiuser environment, the
calculated field shown as Next available ticket number may be
inaccurate. It will not be updated automatically as new tickets are
assigned by other users. If this is a problem for you, don't despair -
I'll show you how to fix it.
| Use the form's OnTimer event to keep the calculated field current |
Each form has a Timer property, which is nothing more than an
internal clock that is set to go off at whatever interval you, the
programmer, define. We'll set the Timer property of this form to 3
seconds:
And now let's give the calculated field that displays the next ticket number a meaningful name, like Next_in_series:

Finally, we're ready. We will write a one-line script for the OnTimer
event of the form that updates the Next_in_series field. The
script is simplicity itself:
The single line, Next_In_Series.refresh(), will force the
calculated field to recalculate itself.
You may not realize that you can run Alpha Five version 3 in multiuser
mode even if you only have 1 computer. Simply open two or more copies of
Alpha Five on your Windows desktop. You can experiment with entering new
tickets and resetting the ticket numbers and see how the OnTimer
event keeps updating each separate Alpha Five session.
6/20/98 - pkw
Don't forget, we need your feedback to make this site better!