Duplicating a set

In Xbasic for Everyone, I illustrated a short script to duplicate any record of any table. But what if you need to duplicate the active records of an entire set that spans two or more tables?
A typical example would be an invoice set, in which there is an invoice header and multiple detail lines. In some applications it is not unusual for many customers to repeat the exact same order time after time. In this short article, I'll illustrate how to duplicate the records in a set.

The table structures


Structure of Inv_hdr.dbf
Field Type Width
Inv_no C 6
Date D 8
Total N 8, 2 decimal

The set, invoice.set, consists of 2 tables, inv_hdr.dbf and inv_items.dbf, linked on a one-to-many linkage through an inv_no field.

Structure of Inv_items.dbf
Field Type Width
Inv_no C 6
Desc C 20
Amount N 7, 2 decimal

And the link properties of invoice.set are:

Linkage for invoice.set

As is typical in an invoice application, the inv_no field in the parent table, inv_hdr, is an autoincrement field, that is, Alpha Five will assign a consecutive unique value to each newly created invoice. You define the field as an autoincrement field in the Field Rules under Field Type:

Autoincrement field definition

For the purposes of this article, the inv_no field does not have to be an autoincrement field. In fact, this article would be easier if it were not an autoincrement field! But we'll do the autoincrement case here, and if you have a different way of assigning invoice numbers, you can use that in your application.
Our next step is to create the default form for invoice.set:default form for invoice set
Now, add a new button to the form. We'll place a script on the button to duplicate the invoice, so make the text label "Duplicate this invoice" and make it a custom button. Right-click on the button, choose Actions, and then switch to the Xbasic editor. Enter the following code:
''XBasic
dim hdr as p
dim items as p
dim old_parent as b
dim count_items as n
dim i as n

hdr=table.current(1)
items=table.current(2)
count_items = count(Inv_Items->Inv_No,GRP->INV_HDR)
if count_items>0 then
dim records[count_items] as b
i=0
items.fetch_first()
while i<count_items
i=i+1
records[i]=items.record_data_get()
items.fetch_next()

end while
end if 'count_items>0
old_parent=hdr.record_data_get()
hdr.enter_begin()
' note, in this table the inv_no is
' an autoincrement field
' a new inv_no is assigned by the field rule,
' which overrides
' the Xbasic record_data_set()
hdr.record_data_set(old_parent)
hdr.enter_end(.t.)
if count_items>0 then
for i=1 to count_items
items.enter_begin()
items.record_data_set(records[i])
items.inv_no=hdr.inv_no
items.enter_end(.t.)
next
end if ' count_items>0
parent.resynch()
end

Let's discuss this script in some detail. First, note the use of the count()function in the script. Because the script uses the set structure of invoice.set, it is possible to use count() over a grouping level to get the total number of separate invoice items for the chosen invoice. I could also have coded a while..end while loop, but if Alpha's developers have already coded a function, why not use it?

After determining how many invoice items are present, the script next establishes an array, records[count_items], of blob variables. A blob, or binary large object, can be used to store entire records. The script then loads the different elements of the array with the individual line item records, using the record_data_get()method.

Next, the script loads the values in the old invoice header into another blob variable, old_parent. It then creates a new invoice header record and stuffs it with the data contained in old_parent. Note that since the inv_no field is an autoincrement field, the autoincrement field rule will override the old inv_no with a new, unique value.

Finally, the script enters new line item records. It stuffs each new line item with the values stored in the records[] array. Because the line items in the records[] array all contain the old inv_no, the script explicity sets the inv_no field of the new records to the value of inv_no in the new header record.

The very last line, parent.resynch(), simply synchronizes the form to the newly created duplicate invoice.
6/11/98 - pkw

Return to home page