Alpha Five version 4 has so many wonderful features that I was initially tempted to use them all, or as many as I could fit into my application. I was particularly enamored of tabbed subforms, and decided to put all my major tables in 1 set and my entire application into 1 tabbed subform. The initial set design was this:
Fig. 1. The initial set design for WMS.
And then my initial tabbed subform design was something like this:
Fig. 2. Master tabbed subform.
The first tab page was an index page, used to select a patient. From then on everything related to an individual patient could be selected by going to another tab. For example, you could look at the patient's insurance information by clicking on Insurance:
Fig. 3. Viewing a patient's insurance policies.
To edit a individual insurance policy, I placed an entire policy on a subform on another page of the tabbed subform:
Fig. 4. An individual policy contained in an embedded subform placed on a tab page.
So far so good: all of a patient's information, from demographics, to insurance, bills, payments and medical notes, was just a tab page away.
I continued to enjoy working with Alpha Five version 4, adding more enhancements to the basic structure. Until I actually converted my office data from version 1 to version 4, and that's when the limitations of this "all-in-one" approach became apparent. If one of the office staff were entering a patient's insurance information, a bill or a payment, and another patient called to make an appointment or inquire about a bill, the tabbed subforms and drop-down menus allowed the staff member to leave the current data entry and move to another patient. As soon as the parent patient record changed, all the partially completed child and grandchild records would be saved. In the press of a busy office, it was all too easy to forget to return to the original patient's record to finish entering the partially completed child records. With a staff of 9 people in the office of varying degrees of computer semiliteracy it was apparent that mistakes were going to continue to be made unless I could force each user to save their changes before changing from one patient to another.
I understood I had to control every time a user could switch focus from an unsaved child record. After trying many ways to do this, I finally realized that there was no way I could do that in my large tabbed subform. As a first step, I had to redesign the set structure. I split the insurance and note tables off from the billing set. Instead of a one large tabbed subform from which the entire set can be accessed, the "index" page is based only on the patient.dbf table, and buttons will take me to other forms based on other tables:
Fig. 5. New "Index" page, based solely on the patient table.
The first benefit of basing the Patient Index form on only the patient table is that navigation through patients occurs much faster than before, since Alpha Five does not have to fetch multiple linked records over the network every time it fetches a new patient. I had not even realized how much of a performance penalty my large set entailed until I abandoned it.
The second benefit of my redesign is that I can now force the staff members to either commit or abandon each child record. The code for the Insurer button is illustrative. It opens a new form based solely on the patins table. In the new design, however, the patins table is not a child table in a set - it's a standalone table in its own form:
this.disable()
patient=table.current()
f=form.load("Patient Insurers")
f:Tables:patins.query_detach_all()
f:Tables:patins.filter_expression="account='"+patient.account+"'"
f:Tables:patins.order_expression="ins_no"
f:Tables:patins.query()
ix=f:Tables:patins.index_primary_get()
if ix.records_get()=0 then
f:Browse1.hide()
f:Text1.text="No insurers found"
end if
f:pname.text=trim(patient.last_name)+",
"+trim(patient.first_name)
f:pacct.text=patient.account
f.resynch()
f.show()
f.activate()
this.enable()
Script 1. Xbasic script for the Insurers button.
Notice that I also issued a this.disable() as the first line of the script. That's because people in my office tend to triple and quadruple click on the mouse, launching a second instance of the script before the first instance has had a chance to complete itself.
Script 1 opens the Patient Insurers form filtered on the current patient's account number. If there are any insurers, the form shows them in a read-only browse:
Fig. 6. Read-only browse of insurance records.
This form has its different System menu properties disabled, so the user can't resize it or close it, except through the Close button:
Fig. 7. Window properties for the Patient Insurers form
Double clicking on the browse will open another form for editing an individual insurance record:
this_rec=alltrim(str(table.current().recno()))
f=form.load("Patient
Insurance")
f:Tables:patins.filter_expression="between(recno(),"+this_rec+","+this_rec+")"
f:Tables:patins.query()
f.resynch()
f:ptname.text=parentform:pname.text
parentform.hide()
f.show()
f.activate()
Script 2. The OnRowDblClick script for the browse.
The Patient Insurance form, which is used to edit a single insurance record, also has its System Menu disabled. It has a "Close" button whose script is:
dim mode as c
mode=parentform.mode_get()
if mode="ENTER" .or.
mode="CHANGE" then
parentform.commit()
if
parentform.mode_get()<>"VIEW" then
ui_msg_box("Unable to save your changes",\
"Please correct and save, or discard your changes",\
UI_STOP_SYMBOL)
end if
end if
f=obj("Patient Insurers")
if is_object(f) then
f:Tables:patins.filter_expression="account='"\
+table.current().account+"'"
f:Tables:patins.order_expression="ins_no"
f:Tables:patins.query()
f:Tables:patins.fetch_last()
f:Text1.text="Double click on an insurer to edit or view it"
f:Browse1.show()
f.resynch()
f.show()
end if
parentform.close()
Script 3. The Close button script for the Patient Insurance form.
The code for the New Insurer button in the form shown in Fig. 6 creates a new insurer record in Xbasic and then opens the same Patient Insurance form to edit the newly created record:
t=table.open(table.current().name_get())
account=pacct
Text1.text="Double click on an
insurer to edit or view it"
Browse1.show()
query.filter="account='"+pacct+"'"
query.options="M"
query.order=""
ix=t.query_create()
nrecs=ix.records_get()
t.enter_begin()
t.account=pacct
t.ins_no=nrecs+1t.enter_end(.t.)this_rec=alltrim(str(t.recno()))
t.close()
f=form.load("Patient Insurance")
f:Tables:patins.filter_expression="between(recno(),"\
+this_rec+","+this_rec+")"
f:Tables:patins.query()
f.resynch()
f:ptname.text=parentform:pname.text
parentform.hide()
f.show()
f.activate()
Script 4. The New Insurer script.
As the user navigates from one form to another, only one form at a time can be maximized. Returning to a previously hidden form makes that form show in less than a fully maximized state, which is annoying. This can be corrected by placing a this.restore() in the OnActivate event of the form.
Let's trace the work flow for one patient:
Figure 8. The user starts with the Patient Index screen, then moves to the form containing the browse of insurers, then on to the individual insurance record. At each step only one form can be accessed. Oh, yes - it's important to point out that a controlpanel.hide() was issued at the beginning of the application, so that the user cannot access the control panel while the other forms are hidden.
Controlling the end user is time-consuming and sophisticated users may realize that it violates the spirit of Windows programming. However, in a system in which many people are working simultaneously on the same tables, it is often necessary to force the users to follow a prescribed path. My staff is much happier with the redesign of my application, because it takes the onus of remembering to commit changes off of them and places it in the application. There is no appreciable performance hit with this reorganization - indeed, with Network Optimize I found that loading forms locally is faster than moving large linked multirecord objects over the network.
To summarize:
6/28/99
Don't forget, we need your feedback to make this site better!