Creating a task for Axiell Collections
Certain bulk editing procedures hard coded in the now deprecated Adlib for Windows have gotten a more flexible setup and functionality in Axiell Collections. We call these bulk operations "tasks" and you can set them up via Axiell Designer in combination with custom adapls and screens. Tasks must be defined per data source in an application structure definition (.pbk). You can specify as many tasks as required.
A good example is the Change locations task (similar to the procedure in Adlib for Windows) which is meant to allow the user to change the current location of a selection of objects at once and update the location history of those object records at the same time. From model application 4.5.2, this task is already present by default, so only for older applications you would need to set it up yourself if you wanted to make such an application Collections-compatible. However, it's good to see how this task needs to be set up, as a general example of setting up tasks.
We already made the required adapl and screen (available for download here). You only still need to set up the task in your application if the Change locations task is not available in your Collections application yet.
However, let's start at the beginning to show you how everything comes together. Imagine a Run task icon being available in the context toolbar above the result set in Collections, with which you can select and run any of the specified tasks. If multiple tasks have been specified, a tasks list opens to allow you to select the desired task, while if just a single task has been specified then the relevant task screen opens immediately. If no records have been marked, the task will run on the currently selected record, while if records have been marked, the task will run on the marked records.
After a task has started, a screen will open allowing the user to enter data relevant to the task, after which clicking OK will run an adapl program that will be executed for the selected or marked records in the result set, processing the entered data into each record. So the adapl must fill in the new current location and update the location history. To get entered data from the task screen to the adapl we must define so-called parameters per task as if they were fields and use these parameters as task screen field tags on the screen and as source field tags in a special PARAMETERS FACS declaration in the adapl. Although these parameters look a lot like normal fields, they must be set up in the application definition, per task, instead of in the database table definition and therefore they have no relation nor interference with the normal database fields.
1. | In the case of the change locations procedure we rebuilt the old Adlib for Windows dialog as a screen and assigned some parameters as field tags. We chose P1 for the new checkbox and existing Collect field tags for the other fields (because we've copied those fields anyway), but we could have chosen any twelve two-character parameters for this particular purpose: no need to check the database definition of Collect per se. We called the screen changelocationtask.fmt but any other descriptive name would have been fine too. Note that during operation the Location field is hidden as soon as the user marks the To normal location checkbox, because of the field suppress condition P1 = $true. |
2. | We created a ChangeLocation.ada/bin adapl to process the data from these parameters into each selected record. The .ada is readable in a text editor like Notepad++ and documented well, so you can open it if you'd like to see how it is programmed or if you'd like to make changes to it. Without going into too much detail here about the adapl, it suffices to say that the adapl is structured like an output adapl: it is run for each record separately and therefore has direct read access to all field tags from the processed record. To be able to write altered data to these tags, we could have used a FACS declaration but instead we chose to write to the _LOCAL FACS buffer, which has the same result but is a more concise way of programming this adapl. The only FACS definition really required is the one for PARAMETERS (always use this name): fdstart PARAMETERS '' P1 is PARAMETERS_tonormal 2A is PARAMETERS_location 2a is PARAMETERS_locationlref 2C is PARAMETERS_date 2G is PARAMETERS_time 2R is PARAMETERS_executor 2E is PARAMETERS_suitability 2F is PARAMETERS_authoriser 2f is PARAMETERS_authoriserlref 2D is PARAMETERS_locnotes mT is PARAMETERS_method mt is PARAMETERS_methodlref mR is PARAMETERS_reference mC is PARAMETERS_contact mc is PARAMETERS_contactlref mN is PARAMETERS_movnotes fdend As you can see, the FACS declaration treats the sixteen parameters as if they were regular field tags and assigns aliases to them so it's easier to work with them. There's no need to OPEN this FACS declaration since it is not a real database: after this declaration we can just use the FACS parameter aliases as if they were normal FACS field tag aliases. |
3. | In the Designer Application browser, open your application definition and the data source under which the task must become available, the Internal object catalogue for example (and/or the External object catalogue and the Archives data sources, when it comes to the change locations procedure). Right-click the data source and select New > Task in the pop-up menu. |
4. | Select the new task and on the Task properties tab, set the path to the screen and the adapl created for this task. Also set a descriptive menu text in as many translations as your application needs to be available in. Make sure that at least the English menu text is unique, so there shouldn't be another task in this data source with the same English name. |
5. | Finally, define all used task parameters underneath the Fields header of the new task in the tree view. For each parameter that has no correlating field definition in a database definition somewhere, right-click the Fields header underneath your new task and select New > Field in the pop-up menu to create a new field definition, like you would for P1. Task field definitions which can be copies of existing database field definitions, can simply be copied from those database definitions and pasted underneath the task Fields list, also using the right-click pop-up menu: linked fields still need a linked database and a link reference field and you will have to define their Linked field mapping (merged-in) destination fields in the task field list as well. Fill out or check the appropriate parameter properties as you would for database fields. P1 for example, is a checkbox, so its Data type must be Logical (Boolean). If tag 2R (executor) is a plain field, you could consider setting the Type option on the Default values properties tab to User name if you’d like the Executor entry field to contain the (overwritable) user login name as soon as the Change locations window is opened, if the executor of the physical move is likely to be the same person who registers the move. If the executor is always one and the same person, you could set the Type property to Value instead and enter his or her name in the Text list underneath it. |
6. | To copy your new task to another data source, simply right-click the relevant task and select Copy in the pop-up menu. Then right-click the other data source and select Paste in the pop-up menu. |
7. | Save your changes to the .pbk after you've specified all parameters for this task. Then recycle the application pool for Axiell Collections so that the application definition can be reloaded. |
This change locations functionality still has the shortcoming that users won't get any warnings about errors or skipped records, neither will any log be created, so a manual check of the processed records afterwards may be required.
General comments on the tasks functionality
• | A task screen doesn't necessarily need to have fields on it. Since the screen will still contain an OK and Cancel button, you can use it as a confirmation dialog for the user, before actually starting the bulk operation. You can just place a label on the screen or specify a screen title containing your message. If the task screen contains no fields, you don't need to provide a PARAMETERS FACS declaration in your adapl: after all, there is no data to transfer from the task screen to the adapl. |
• | The &I and &B[1] reserved variables for ADAPL works as expected: if you execute a task on multiple marked records, &I will contain the sequential number of the currently processed record, while &B[1] will contain the total number of marked records. So if you'd like to execute some code only for the first processed record you could enclose it in if (&I=1) {/*code} and code to be executed for the last processed record could be enclosed in if (&I=&B[1]) {/*code}. |
• | In the adapl, data which you write to fields of an opened FACS database will persist in the FACS buffer as long as records are being processed and the relevant FACS database isn't opened again. That way you can collect data from all marked records and only do something with it during the processing of the last record. |
Example task using &I, &B[1] and FACS buffer persistence
Say you want to create a task for the Internal object catalogue which links all marked records to a new transport record, then the following .pbk task parameters list definition, task screen and adapl would suffice:
* The following tags will be written to the new Transport record
fdstart TRANSPORT '../data+transpor>transport'
%0 is TRANS_priref
G7 is TRANS_despatch_or_entry
EA is TRANS_expected_entry_date
E1 is TRANS_entry_method
l1 is TRANS_entry_method_lref
E9 is TRANS_reason
IN is TRANS_linked_object
l4 is TRANS_linked_object_lref
EI is TRANS_shipper
l5 is TRANS_shipper_lref
fdend
fdstart PARAMETERS ''
a2 is PARAMETERS_due_date
E1 is PARAMETERS_entry_method
l1 is PARAMETERS_entry_method_lref
IN is PARAMETERS_linked_object
l4 is PARAMETERS_linked_object_lref
EI is PARAMETERS_shipper
l5 is PARAMETERS_shipper_lref
fdend
* Do only if the Due date field on the task screen has been filled in
if (PARAMETERS_due_date <> '') {
if (&I = 1) {
/* open the FACS db only once, for the first processed object record.
open TRANSPORT noreset
if &E {
errorm 'Error opening database TRANSPORT, code = '+ &E
end
}
}
/* Do for every processed object record: write the record number of the
/* marked object records to occurrences of the linked reference tag of the
/* linked object field in the TRANSPORT FACS buffer.
TRANS_linked_object_lref[&I] = %0
if (&I = &B[1]) { /* do only for last processed object record
/* fill transport record
TRANS_despatch_or_entry = 'ENTRY'
TRANS_expected_entry_date = PARAMETERS_due_date
TRANS_entry_method_lref = PARAMETERS_entry_method_lref
TRANS_reason = 'PURCHASE'
TRANS_shipper_lref = PARAMETERS_shipper_lref
/* Write the contents of the TRANSPORT FACS buffer to a new
/* Transport database record.
write TRANSPORT next
if (&E){
errorm 'Error writing transport record ' + %0 + '. Error: ' + &E
}
}
}
end