Skip to end of metadata
Go to start of metadata

Every document in KFS has metadata associated with it, and the metadata for maintenance documents exists in the document's data dictionary configuration. The data dictionary does practically everything for a maintenance document: it declares the user interface for the form, ties rules and document authorizers to the document as well as the document's workflow and financial document types. Because the data dictionary is so central, we should start our expedition of putting maintenance documents together by taking apart the data dictionaries for a maintenance document or two.

For an excellent example of a maintenance document's data dictionary, let's take a look at the maintenance document for Accounting Periods. org/kuali/module/chart/datadictionary/docs/ is the directory which contains the data dictionary configuration for the AccountingPeriodMaintenanceDocument, and here it is:

Note that all data dictionary document configurations, maintenance or transactional, belong in the org/kuali/module/{the module of the business object}/datadictionary/docs directory. Now, there's a lot to this data dictionary configuration, so we'll examine it one piece at a time.

Data Dictionary File Name


Maintenance document data dictionary file names should be the document type name (in the <documentTypeName> tag) suffixed with the ".xml" file extension. For example, the Accounting Period maintenance document should be named "AccountingPeriodMaintenanceDocument.xml".

The basic setup

The first thing we can notice from the example, is that everything is wrapped within a dictionaryEntry tag and then within a maintenanceDocument tag; naturally, the maintenanceDocument tag is why the data dictionary interprets this file as one for a maintenance document. After those tags, we see several basic classes get set up:

The most basic thing we do in the maintenance document is set the business object that will be maintained by the document. In this case, it's a, and we set that with the businessObjectClass tag. That was easy.

We also need to set up a maintainable class. Those will be covered in more detail in another document. Most maintenance documents don't implement their own maintainable, and therefore, they copy this line of XML verbatim, using the default org.kuali.core.maintenance.KualiMaintainableImpl.

A full discussion of the rules and authorizations can be found at the Document Data Dictionary page.

Easy rules

The next maintenance document specific tag is "defaultExistenceChecks." Certain document validations are so omnipresent that they can simply be declared - typically validations that certain fields of a document are required. Sadly, the Accounting Period Maintenance Document doesn't declare any such rules, so we'll take a look at how they're defined in the Object Code Maintenance Document:

Here we have a list of default existence checks. Default existence checks make sure that the associated business object for the document actually exists. For instance, for this Object Code maintenance document, if a user enters the code for a chart that doesn't exist, the default existence check will catch that and display an error message next to the chartOfAccountsCode attribute field.

Here we see that within a defaultExistenceCheck tag, we create several reference tags, each one having an attribute name - the attribute of the related business object that will be tested if it has a value or not - and the attributeToHighlightOnFail - the attribute on the form that should be highlighted if there was no value in the business object attribute.

Of course, for this to work correctly, the foreign keys to the fields must be specified as required. We'll see how to do that when specifying the UI.

Locking keys

Since maintenance documents edit one or more business objects, we have to worry about race conditions. Let's imagine, for instance, that we create an accounting period record, using the Accounting Period Maintenance Document, for period 01 - 2009. Let's say that across campus, some rogue Accounting Period maintainer is also creating a record for 01-2009. As our record goes through routing, we don't want the scoundrel maintainer to be able to have their changes wipe out ours. Therefore, KFS does the basic thing to prevent this race condition: it creates a lock on each business object going through workflow as part of a maintenance document. And, typically, it uses the lockingKeys tag of the data dictionary for the maintenance document to create that locking representation. Here's the locking representation configuration for the Accounting Period Maintenance Document:

Not surprisingly, these are also the primary keys for the AccountingPeriod business object. At any rate, this prevents any one else on campus from either creating a new AccountingPeriod record or editing the AccountingPeriod with for Period 01 of 2009 while our maintenance document goes through workflow. The fields used in a locking key can be anything, as long as it marks the business object uniquely. It makes sense, then, that most locking keys are simply the primary keys for the business object.

Defining the UI

Finally, the largest part of the data dictionary: the definition of the UI through the maintenanceSections tag:

The UI of a maintenance document is made up of one or more maintainable sections (Account, for instance, uses several maintenance sections). Each section is named, and each section creates a new tab as its visual representation on the web form.

The maintainableSection, in turn, is made up of a list of maintainableFields wrapped in a maintainableItems tag. Each maintainableField tag lists the attribute that should be shown; that attribute itself has typically been defined in the data dictionary configuration for the business object. For instance, the data dictionary for the AccountingPeriod business object should be a text box with the size of 4, and therefore, when the maintenance framework renders the page for this document, it will display a text field 4 characters wide. We can also make any given field required which forces extra validation, though all validations described in the attributes of the business object will also be checked.

Note, by the way, that every field needed to be updated needs to be on the form. Huh? This refers specifically to the versionNumber field. versionNumber is a hidden field. We never want users to be able to edit that field directly. However, it needs to be hidden in the form to make sure that it gets updated, so the optimistic locking algorithm of the O/RM layer still works. In short: don't forget to add versionNumber as a field on your maintenance document!

While attributes default to using the definition set up in the data dictionary for a given field, there are a couple of behavior modifications that can be made. We can see examples of all of these behavior modifications in the AccountingMaintenanceDocument data dictionary configuration. We can, for instance, set default values for any field, like so:

Here, a boolean indicator is default set to true within the Account Maintenance Document UI.

We can also set new values finder classes for fields:

Here, the accountEffectiveDate is pre-filled with the current account open date.

Finally, we can override the lookup behavior of certain fields:

In this case, we want the financialIcrSeriesIdentifier field to lookup based on IcrAutomatedEntry business objects, and then covert the primary key of the IcrAutomatedEntry record to the financialIcrSeriesIdentifier for the document. This kind of override will mostly be used for attributes that are not declared foreign keys to other tables but which should still typically retrieve their values from another authoritative table.


Some maintenance documents include collections of business objects. The classic cases are the global maintenance documents, where the information for a business object may be split into several collections. For instance, the Account Global has several fields - fiscal officer id, organization code, and many more - "Global Account Maintenance" tab. Then, below, is the "Edit List of Accounts" tab, which has fields for chart and account number. This is a collection: several chart/account number combinations will be associated with the document, and the changes listed in the "Global Account Maintenance" tab will be applied to each of those accounts.

How does the Global Account maintenance document do this? It's actually very easy, and again, set up through the data dictionary. The document has two maintainable sections. The first is pretty normal, and includes all the fields in the "Global Account Maintenance" tab. The second is where the twist comes in:

Here, all of the maintainableFields are wrapped inside a maintainableCollection tag. That tag declares a name for the collection and the businessObjectClass that is represented within the collection. This is what allows the document to present a collection of business objects to edit.

Multiple Value Lookups


In the above <maintainableCollection> definition, the attributes "sourceClassName", "sourceAttributeName", and "template" are required for enabling multiple value lookups. If multiple value lookups are not required, then these attributes are not required. Consult this page for more information.


The business object which is the subject of the maintenance document should have a getter/setter pair of methods for the property named in the maintainableCollection which returns a List. For instance, AccountGlobal has properties named getAccountGlobalDetails() and setAccountGlobalDetails(), so the collection can save the data in the list.

We also see a couple extra declarations: the summaryFields and the duplicateIndentificationFields. Once a new record is added to a collection in the document, that record is shown as a summary, and therefore each of the fields with an associated summaryField tag are displayed as added. The duplicateIndentificationFields act like mini-locks: they prevent the same record from being added twice within the same collection. In this document, we couldn't add the account BL-1031400 more than once to the collection. Finally, notice that each record in the collection must have a hidden field for its versionNumber and a field "newCollectionRecord" to tell the framework that the fields represent a new record or not.

There are also two declarations that we can add to collections to give them a bit more flexibility. Both of them can be seen here, in the OrganizationReversionDetail collection

The organization reversion details are based on OrganizationReversionCategories; there should be one detail record per category - no more and no less. Because of this, we don't want users to be able to either add or delete lines. We simply want to create the full list of details, one per category (that list is created in org.kuali.module.chart.maintenance.OrganizationReversionMaintainableImpl's setBusinessObject() method), with no ability to delete a line and no ability to add a line.

We prevent adding new lines on line 3 of the XML segment, the maintainableCollection declaration. Here we see the easy and fun to interpret attribute, "includeAddLine," which in this case was set to false, which means that no new records will be able to be added to this collection. This attribute defaults to true, which means that in most cases, we can simply omit it.

On line 8, we see a maintainableField with a name of "newCollectionRecord." When this field is present, it tells the maintenance framework that any records currently existing in the collection are permanent - that is, there should not be delete buttons associated with them. This is precisely what we want for this case with the Organization Reversion records: we don't want users to be able to delete any records we've already added to the collection. However, if we had includeAddLine="false" (or had omitted it) in the <maintainableCollection> tag above, we could add new lines to the collection and each of the new lines could be deleted (though the old lines could not be deleted).

Finally, we'll take note of lines 4 and 6: both fields have been set so that the "readOnly" attribute is true. This is because the categoryCode should be visible to the user but should never be changed. Likewise, the "organizationReversionObject.financialObjectCodeName" should be read but not editable. Once the user enters a value into the "organizationReversionObjectCode" field, some AJAX functionality will update the financialObjectCodeName field (or, alternatively, if the page is saved, the field will update as well). It's a helpful feature to give the users immediate feedback, one that deserves further investigation. We'll discuss it presently.


Finally, we want to make our maintenance documents cool. AJAX is cool. (On the other hand, Ajax isn't cool. And Ajax started cool, but then fought with Ulysses and wasn't cool anymore. Is 1404 Ajax cool? Only scientists know for certain, but we must imagine so). Adding AJAX events to our documents is covered elsewhere, but we can take a quick look at AccountMaintenanceDocument to see how it uses AJAX. In this example, the Account Maintenance Document wants to instantly give an error to users if the sub fund group assigned to the account is restricted, based on other values of the account.

First, we import JavaScript files.

The ../scripts/chart/accountDocument.js is a JavaScript file that defines the functions onblur_subFundGroup and checkRestrictedStatusCode_Callback. onblur_subFundGroup uses the SubFundGroupService, and to that successfully, DWR needs to create a JavaScript/Java bridge for that access. That's the purpose of the inclusion of the ../dwr/interface/SubFundGroupService.js file: it's not a real JavaScript file at all, but instead a bridge created on the fly by DWR.

Maintainable fields can then trip off the AJAX call when certain events happen:

Here, when the user leaves the UI field for the sub-fund group code, the onblur_subFundGroup JavaScript function will be called, and that should populate the name of the sub-fund group in the page under the UI field.

Unable to render {include} The included page could not be found.
  • No labels