Skip to end of metadata
Go to start of metadata

The vast majority of transactional documents that come packaged with KFS are "Accounting Documents": documents that, in some way, create accounting lines as an essential part of the document. (Labor and Purchasing/Accounts Payable also include documents with accounting lines, and they build on the Accounting Document framework as well. This document will concentrate, though, upon Financial Documents.) Given this, it makes sense that KFS provides a lot of functionality for dealing with documents with accounting lines. Implementing new financial documents is made much easier by having a full idea of what functionality exists for us to build upon.

Accounting Documents

What specific features does an accounting document support? To answer that question, let's take a look at org.kuali.kfs.document.AccountingDocument. Traditionally, documents in the document hierarchy are declared as interfaces, with the default implementation in the same package, with the name {name of document interface}Base. This is just a long way of saying we need to take a look at org.kuali.kfs.document.AccountingDocumentBase as well.

The most basic functionality that implementations of the AccountingDocument interface must perform is that of tracking accounting lines. AccountingDocument actually keeps track of two sets of accounting lines - the source accounting lines and target accounting lines. Therefore, for each of those two groups there are methods to:

  • get all of the accounting lines in the group as a List: getSourceAccountingLines() and getTargetAccountingLines()
  • set a group of accounting lines to those lines in a List: setSourceAccountingLines() and setTargetAccountingLines()
  • add a new accounting line to the List of accounting lines in the group: addSourceAccountingLine() and addTargetAccountingLine()
  • return the accounting line at a given index in the list: getSourceAccountingLine() and getTargetAccountingLine()
  • get the section name of each group of accounting lines: getSourceAccountingLinesSourceTitle() and getTargetAccountingLinesSourceTitle(). The Distribution of Income and Expense names its Source group of accounting lines "From" and its Target group "To", which can be seen when a DI document is viewed.
  • gets the sum monetary total for all the accounting lines in a group: getSourceTotal() and getTargetTotal()
  • gets the class of the accounting lines in each group: getSourceAccountingLineClass() and getTargetAccountingLineClass()
  • gets the name of an accounting line entry for each group to be displayed to the user
  • and finally, getters and setters for the next index for the source or target accounting lines

That leaves but one last method in the AccountingDocument interface: getAccountingLineParser(). We'll take a closer look at in the section on Accounting Line Parsers.

For the most part, this functionality makes a lot of sense: an AccountingDocument implementation needs to keep track of source and target accounting lines, and therefore declares a bunch of getters and setters to do that. But notice, too, we've got a lot of flexibility in how to present accounting lines in a document: we can change the name of their section or their entities name, or even the class used to represent accounting lines (naturally, AccountingLine defaults to and for source and target lines respectively; less naturally, for getSourceAccountingLine() or getTargetAccountingLine() to work, any class used for source accounting lines must extend SourceAccountingLine and the same is true for TargetAccountingLine. This allows documents with "unusual" accounting lines, such as those for the Budget Adjustment document, to still use a great deal of the functionality that the framework gives you.

What if my accounting document only has one set of accounting lines?


Traditionally, documents with only one set of accounting lines - such as the documents in the Cash Receipt family (CashReceiptDocument and CreditCardReceiptDocument) or Journal Voucher - use just the source accounting lines and then don't display the target accounting lines; we'll take a look at how to do that when we look at the HTML side of this framework.

How did we get here?

The AccountingDocument interface extends several other interfaces, each of which adds functionality to the document. The three we want to concentrate on currently are org.kuali.kfs.document.LedgerPostingDocument, which extends TransactionalDocument and therefore is based on KNS functionality, and org.kuali.kfs.document.GeneralLedgerPostingDocument, which extends LedgerPostingDocument. AccountingDocument, in turn, extends GeneralLedgerPostingDocument...but what does that mean in terms of functionality?

LedgerPostingDocument defines some basic methods relating to posting. Very basic. Indeed, it defines six methods - three getter/setter pairs - for the properties that all posting would need to know: the posting year, the posting period code, and then the related AccountingPeriod record, which uses the posting period code and posting year as keys. That's it. However, all entries that post to a ledger need to have information relating to when they were posted, so these are obviously important.

LedgerPostingDocument is then extended to post to the general ledger: GeneralLedgerPostingDocument to post to the general ledger. (There is also a org.kuali.module.labor.document.LaborLedgerPostingDocument which surprisingly does not extend LedgerPostingDocument but rather AccountingDocument. Examining that hierarchy is beyond the scope of this particular document.) GeneralLedgerPostingDocument has several methods related to generating pending entries for the document, including getting a list of pending entries, determining if bank cash offsets are required, and sending sufficient funds checks back a list of pending entries related to the document. This is, of course, critical functionality: each accounting line document should generate pending entries to send to the scrubber and poster.

What are accounting lines?

Accounting lines come in many, many different varieties - just take a look at any of the financial processing documents. Basically, though, an accounting line simply represents a charge to an account, which can then be turned into an explicit and offset general or labor ledger entry. Every accounting line requires a chart, an account, an object code (which may be either an income, expense, asset or liability object code, therefore changing how the amount of the line would be interpreted in the context of the document - whether it's a debit or a credit), and an amount; accounts can also be associated with sub-accounts and object codes can be associated with sub-object codes. Accounting lines also contain a Project code and Org Ref ID which can provide additional information about the accounting line. Some documents include additional fields, such as Line Desc, Ref Origin Code, Ref Number which further expands information contained in the accounting line. The Journal Voucher allows a huge amount of freedom in entering information; for instance, a user can set the object type and balance type for an accounting line, which gives an unbelievable amount of control over the ledger and is consequently only available for use by a couple of people. Most financial processing documents, though, follow very basic accounting rules and are simply easier ways to enter accounting information.

Extending AccountingLine

Several accounting documents need more information than is stored by AccountingLine. For instance, BudgetAdjustmentDocument needs to know what accounting periods certain adjustments hit in. LaborJournalVoucherDocument needs to keep a lot of extra information about its accounting lines. For that reason, these documents extend SourceAccountingLine and TargetAccountingLine to hold the extra information. (And, for the curious, there may be other reasons to extend accounting lines as well; take a look at the comments in GECSourceAccountingLine.)

Of course, extending the accounting line classes aren't cost free. There's a number of things that need to be done to get the document to use the new accounting line types and otherwise get the new accounting lines to work:

  1. Each of the new accounting lines need a brand new OJB mapping. That involves a lot of copying an pasting, so that you're certain that all the previously existing attributes in AccountingLine still work.
  2. The document needs to use the new accounting line class in its own OJB mappings, for the sourceAccountingLines and targetAccountingLines mappings.
  3. Finally, the accounting document has two methods: getSourceAccountingLineClass() and getTargetAccountingLineClass(). Obviously, the document with the extended accounting line tags needs to return the extended source accounting line class and the extended target accounting line class.

This need also caused some interesting issues to pop up in workflow document type defining; we'll cover why that was below.

Document Totaling

Since each accounting line has a monetary amount associated with it, we often think in terms of the entire document having a total. Furthermore, it's often useful to search for accounting documents based on its total amount. For that reason, the org.kuali.core.document.AmountTotaling interface is defined in KNS. That interface declares one method, getTotalDollarAmount(), which will return the total dollar amount for the accounting document.

AccountingDocumentBase defines a default getTotalDollarAmount implementation, based on the assumption - true in the majority of accounting documents - that the total of the source lines should be equal to the total of the target lines, and therefore, if it returns one of those totals, it will be the total amount of the document.

Not the sum!


AccountingDocumentBase returns either the source or the target amount, but not the sum of them. This is because on most documents, that sum would be zero - ie, the source and target lines would cancel each other out - and therefore, one side alone determines the total of the document, though which side doesn't really matter, as they're going to equal out.

AccountingDocumentBase doesn't implement just defines the single method that AmountTotaling requires of implementations. Therefore, documents which inherit from AccountingDocumentBase can define themselves as AmountTotaling documents and simply inherit that functionality from AccountingDocumentBase. Or they can avoid defining themselves as AmountTotaling, and not be picked up by workflow as a document with a total (though, in current KFS practice, this is a very rare situation).

Naturally, documents inheriting from AccountingDocumentBase can certainly define their own versions of getTotalDollarAmount(). Journal Voucher, for instance, has a completely different algorithm for determining the total of the document, seeing as how JV's only use source accounting lines, and those accounting lines equal out through credits and debits. Here is the heart of the JV's document totaling algorithm, as defined in

As we can see, in this algorithm, the balance type of the document is checked to see if it's a financial offset generation indicator, and dependent on that, decides whether to use the credit total or the debit total. Several other documents also reimplement the getTotalDollarAmount method: CashReceiptDocument and BudgetAdjustmentDocument are also interesting examples of different document totaling algorithms, based on the functional needs of each of the documents.

Accounting Line Parsers

Most Accounting Documents allow the uploading of accounting lines into either the source or the target section. This way, an entire file of accounting lines can be uploaded at once, which makes filling out documents much easier. To accomplish this, AccountingDocument returns implementations of to actually parse the documents.

Currently, .CSV files are the only files that AccountingLineParser implementations can parse (though could be extended to deal with, say, Excel spreadsheets or some other sort of text delimited files). Basically, an implementation of AccountingLineParserBase sets up a format of the imported data - basically, which accounting line fields are in which column of the .CSV file. In AccountingLineParserBase, for instance, the fields are assumed to be in the order: chart code, account number, sub account number, financial object code, financial sub-object code, project code, organization reference id, and amount. This format - an array of String field names - is returned by the getSourceAccountingLineFormat() and getTargetAccountingLineFormat() methods. Therefore, to change the default behavior of the AccountingLineParser, one need only extend AccountingLineParserBase and then return different arrays from the getSourceAccountingLineFormat() and getTargetAccountingLineFormat(). In effect, this is what BudgetAdjustmentAccountingLineParser does. AccountingLineParserBase also defines two methods, performCustomSourceAccountingLinePopulation and performCustomTargetAccountingLinePopulation, which allow classes which extend AccountingLineParserBase to parse the accounting lines in whatever way they see fit.

Accounting Document Rules

Compared to fairly standard practice of inheriting behavior through the document hierarchy, looking at the rules framework for accounting documents is minorly crazy-making. While a full examination of how rules are implemented in Kuali Rice is well beyond the scope of this document, we can remember that rules in Kuali tend to be based off of events - save, route, approve...if you can imagine an event occurring to a document, then there's likely a rule associated with it. Therefore, looking at the events that will occur because we've added accounting lines sounds like a great way to start examining the rules for accounting documents.

First of all, and most basically, accounting documents allow the user to add accounting lines (at least source accounting lines, but likely both source and target accounting lines. We note, though, that none of the rule methods in the accounting document rule framework differentiate between source and target accounting lines. Thankfully, the AccountingLine interface declares two methods: isSourceAccountingLine() and isTargetAccountingLine() which allow rules to differentiate the two.

If we can add accounting lines, there's a decent change we'll input bad data and want to wipe out an entire accounting line. Therefore, we've got a delete accounting line event as well. And too, we may not delete the whole line but rather correct the fields that are in error. Therefore, we've also got an update accounting line event. There also exists a "review" event, which is generated for accounting lines that were not updated. This allows rules to be processed on the accounting lines that may be in error because of events that occurred within the rest of the document.

Finally, we've got three less obvious events. First of all, there are common things that we're going to want to check on every accounting line whenever any event is called - is the chart correct?, can the object code be used on this accounting line?, and so on. These rules are common to all accounting line events, so let's call this "AccountingLineEvent" and have all other events extend it. And then we've got two events that deal with creating the pending ledgers for a document: generate pending ledgers and generate general ledger document pending entries. These rule events allow the separation of the logic to generate pending ledger entries from the documents themselves, though, admittedly, its logic has very little to do with actual validation.

When an accounting document is saved, the save will generate the the appropriate accounting line events in the generateSaveEvents() method. However, some of the methods in AccountingDocumentActionBase, such as addAccountingLine, also create appropriate accounting line events, so that accounting lines are validated when the user first tries to enter them.

For each of these events, KFS has a rule to validate the document upon the event. And of course, the accounting document framework provides base implementations of all of rule interfaces. It implements them all in one class, called org.kuali.kfs.rules.AccountingDocumentRuleBase. This class works like the other rule classes in KFS: each document can extend it and add rules to suit its own purposes. Furthermore, like regular KFS rules, there are "customRules" which, in the base implementation, do nothing, but which are the perfect place for us to put our rules. Let's take a look at the important methods there.

  • processCustomAddAccountingLineBusinessRules - Any special rules that should be called when an accounting line is added. It gets the accounting document and the specific accounting line to check as parameters.
  • processCustomDeleteAccountingLineBusinessRules - Any special rules that should be called when an accounting line has been deleted. It gets the accounting document and the specific accounting line to check as parameters, and an extra parameter that lets us know if the line was already deleted.
  • processCustomUpdateAccountingLineBusinessRules - Do we sense a pattern? Yes, this is where we'll put the special rules for our document when an accounting line is being updated. Once again, we get the accounting doc as a parameter and then two accounting lines: the original version of the line and the updated version of the line.
  • processCustomReviewAccountingLineBusinessRules - Our special accounting line review rules, and once again, we get the doc and the accounting line to review.
  • customizeExplicitGeneralLedgerPendingEntry and customizeOffsetGeneralLedgerPendingEntry - The customize pending entry methods are a bit more complex. Basically, the rule generates the entries and then gives this method the entry that was generated; this method can change whatever it needs to about the pending entry. We have two of these, because for each accounting line, we're going to have two entries: the "explicit" entry, which is the entry derived directly from the accounting line itself, and the "offset" entry, which offsets the action of the explicit entry, typically by reducing or augmenting the cash of the account. These methods also get many parameters: the accounting document, a "GeneralLedgerPendingEntrySequenceHelper" which basically generates sequence numbers for each entry in a document (so, yes, it's a glorified, encapsulated counter), the accounting line that the pending ledger entry is being generated for, and the already generated pending ledger entry to customize.

When writing we're writing our own rules for our own accounting document, these are the methods that we're going to want to override. Since it works just like the rest of the KFS rules system, it's very easy.

But, wait...that's not all! Because accounting documents come in so many sizes, shapes, and forms in KFS, there's a whole lot of methods in AccountingDocumentRuleBase that aren't covered by the new accounting document rules. These methods actually do validation on how the document balances, if the general ledger pending entries for the document balance out, if a minimum number of source and target accounting lines have been met. Most of these methods are protected, so for highly advanced accounting documents, the logic can be manipulated.

Finally, AccountingDocumentRuleBase is loaded with lots of interesting helper methods to assist in general ledger pending entry creation. For instance, there are methods that determine if a certain balance type code represents a liability. Anyone interested in GLPE creation for documents - I know that includes most of us! - should take a look at the bottom third of the source code of the class.

To sum up: for most accounting document rules, we're going to inherit from AccountingDocumentRuleBase and override the custom rule methods we need - in many cases, processCustomAddAccountingLineBusinessRules. However, the rules framework is powerful and a extensions of AccountingDocumentRuleBase do have a lot of power over how accounting documents are balanced.

Accounting Document Authorization

Rules are important. They prevent bad data from getting into the system, which in the case of accounting documents is particularly important, as bad data could make serious mistakes in the ledger of an account. Sometimes, however, we don't want to wait for a rule to prevent something from happening, since a rule only occurs after something has been entered. We need guards to prevent certain actions from happening in the first place, and DocumentAuthorizers are those guards.

Once again, a full examination of document authorizers are a bit beyond our scope, since they are a fundamental part of the Kuali Rice document hierarchy. However, accounting documents do add a couple twists to document authorizers; definitely we want to take a look at those.

org.kuali.kfs.document.authorization.AccountingDocumentAuthorizer extends TransactionalDcoumentAuthorizer, and it requires four new methods: getAccountingLineEditableFields(), a variant version of getEditMode(), and two versions of getEditableAccounts. (getEditableAccounts(TransactionalDocument, ChartUser) isn't actually called anymore and may be removed when you read this; we'll concentrate on the version that takes in a list of accounting lines.)

getEditMode(Document document, UniversalUser user, List sourceAccountingLines, List targetAccountingLines) is interesting, though we as implementors will rarely find need to reimplement it. It was created because if a fiscal officer, approving a document, enters an invalid account number for the accounting line that they must approve, the fiscal officer would lose the ability to edit the document because of how the normal version of getEditMode() works. Therefore, this version sends in baseline source and target accounting lines - which we'll cover in a minute - which are guaranteed to be valid, and therefore, this method can prevent that lockout situation from occurring.

getAccountingLineEditableFields() returns an empty HashMap() in the base implementation and the only document which overrides it is This is because the DV goes through extensive workflow routing, and therefore is up for approval from a variety of users. Typically during routing, only officers associated with the accounts on the document are allowed to edit accounting lines; however, in the case of the DV, where several officers need to approve the document, different aspects of the accounting lines need to be editable that otherwise aren't. For instance, officers from the travel office should not be allowed to change the account or description of an accounting line, but they should be able to change the amount of an accounting line and the object code for an accounting line. This method allows that.

getAccountingLineEditableFields() and getEditableAccounts() return Maps. Maps of what?


Again, this is a legacy idiosyncrasy. The very first version of the JSTL did not include JSTL functions, and when the first financial processing documents were being created that posed a problem, because there wasn't a very good way to determine if a specific value existed in a list of values. The discussed methods basically return Maps simply because there was no fn:contains() JSTL function that could operate on a Collection. However, in reality, the Maps returned by these methods are Sets - key sets - with values attached. A JSP page can test a value for emptiness by using EL Map semantics, like so:

As such, it matter much what value we associate with the key, because it's the key that matters. It does matter a little, though. We can't put "null" as the value, for instance, as the test above will then fail; same goes if the Map had a String "" as the value. In the case of the DV doc authorizer's getAccountingLineEditableFields(), the values returned are the String "TRUE", though they could have just as well been any non-empty String, or even a Boolean.TRUE. However, whatever is put out in that map as a value, it's being stored in memory for the length of the page render, so putting, say, a POJO as the value is typically not a great idea either.

getEditableAccounts() takes in as parameters a list of the accounting lines on an accounting document and the current user, and it returns a Map - again, a key set with values attached - with the String versions of the accounts that can be edited by the given user. If an account comes in as null, it can still be edited by the current user, as that current user would need to fix the account in all probability; otherwise, the method uses Account.hasResponsibilityOnAccount() to test whether the given user can edit the accounting line with the given account. Naturally, we can override this in our own accounting documents to give whomever the ability to edit certain lines or (much more commonly in the existing financial documents) revoke the ability to edit certain accounting lines from certain editors.

Therefore, the accounting document framework extends document authorizers to allow more guards over what accounting lines can be edited, just as we would expect.

Putting an Accounting Document on the Web

Obviously, our accounting document needs to go on the web, and once again, it works just like the entire document processing framework in the rest of Kuali. We've got actions and forms, and the accounting document framework adds some actions (such as adding accounting lines, deleting them, and so forth) and changes the form in interesting ways; our accounting documents will simply extend the form (org.kuali.kfs.web.struts.form.KualiAccountingDocumentFormBase) and the action class (org.kuali.kfs.web.struts.action.KualiAccountingDocumentActionBase) as necessary. Let's look, then, at how the accounting framework extends the document framework in the web layer.


The form for accounting documents holds some very obvious data, and it also does some fairly non-obvious things. Let's take a tour of how KualiAccountingDocumentFormBase extends the form base.

  • Since the view layer needs to be able to get information created by the document authorizer for our accounting documents - what accounts are editable for the current user and so on - so those Maps are actually generated by the action and set in the form before the form is passed to the view layer.
  • The form also includes a "newSourceAccountingLine" and "newTargetAccountingLine", since new source and target accounting lines have not been added yet to the document (naturally, just as with any form in KFS, the accounting document is already encapsulated in the form), and there are getters and setters associated with these properties.
  • There are methods to "populate" accounting lines. These methods set the primary keys for child objects within accounting lines. For instance, let's say that we've entered an accounting line with a sub-object code. Sub-object code BO's have an extensive primary key - not only the sub-object code, but also the parent object code, the chart of the parent object code, and the fiscal year that the sub-object code is valid for. All of these can be derived from the document or the accounting line, and the populate methods fill out all of that information on the sub-object BO before it is saved, so that the O/RM layer can correctly generate the relationship between the accounting line and the BO in the database.
  • If source or target accounting lines were uploaded for the document, which was discussed in the section on accounting line parsers, the form needs to hold on to the uploaded forms.
  • The form contains accounting line decorators. These are very simple objects that tell the user interface whether or not the given accounting line is revertible or not. This based on whether there is a previous version of the accounting line - a "baseline accounting line" - which is different from the current version of the accounting line that the user can edit. For instance, when we first add an accounting line to a document, it's not revertible - there's no previous version. But then if we change the account on that added accounting line, we've got a previous version, and we can revert to it. Therefore, the decorator for that accounting line has its "revertible" property set to true. Every accounting line has one.

Baseline accounting lines deserve a closer look.

Baseline Accounting Lines

Baseline accounting lines are stored for each accounting line in an accounting document. What are they? When we add an accounting line to a document, the accounting line is validated and then put into the document. Then we can edit that accounting line. The baseline accounting line is simply the values for the last validated version of a given accounting line, all held as hidden fields within the document.

Baseline accounting lines are used in a variety of ways within the framework. We saw above how a variant version of getEditMode() uses the baseline accounting lines to prevent users from locking themselves out by entering invalid data. They also allow a quick reversion to the previous version of an accounting line: if we're working on an accounting line, mess it up, and want to undo, we have a revert button which simply reverts the accounting line back to the values in the baseline version.

Naturally, the accounting document holds the current version of the accounting lines, which is to say: the accounting lines as the user has entered them. These baseline accounting lines, therefore, can't live in the document, and therefore exist within the form.


KualiAccountingDocumentActionBase extends the KualiTransactionalDocumentActionBase to allow users to actually act on the accounting lines in an accounting document. Most of the methods look pretty familiar by now - we've met some of them through the events that were added for the rule framework, and some of them through the accounting line parsers and baseline accounting lines. For most of the new action methods we've got, there's a version for source lines and target lines, both of which end up calling a central method which does all the work. For instance, we see that there's a revertSourceLine and revertTargetLine, both of which do some processing and then call revertAccountingLine. We'll concentrate on the "accounting line" methods.

  • insertAccountingLine is called when we add a new accounting line to the document. It validates the accounting line, and then creates corresponding baseline accounting lines and accounting line decorators (though the line is obviously not revertible at this point).
  • deleteAccountingLine is called when an old accounting line is removed from a document. It validates the removal and if that works, it removes the baseline accounting line and the decorator.
  • revertAccountingLine is called when the user reverts an updated accounting line to the baseline version of the line. No validation occurs at this point, though if the baseline accounting line needed sales tax associated with it, then those fields will be shown.
  • uploadAccountingLines is called when a new file of accounting lines are uploaded into the document. It parsers the file using an accounting line parser, and then actually defers to insertAccountingLine to put the accounting lines into the file
  • performBalanceInquiryForAccountingLine is called when a user checks the balance on an accounting line. This allows users to conveniently check the balances on the accounts given in the accounting line.
  • hideDetails and showDetails hide or show the accounting lines on the form.

There are two other areas of responsibility for the action: figuring out if the account entered on an accounting line needs to be overridden and figuring out if the accounting line is subject to sales tax.

Accounting Line Overrides

There are two interesting situations where accounting lines entered by users cause "warnings," but the user can continue with the accounting line. The most common example is for expired accounts. While closed accounts cannot be used in an accounting document, an account that has past its expiration date but which is not closed can be used. However, since this represents a condition that the user should at least be warned about, KFS doesn't automatically add the accounting line. Instead, it places a check box in the account box of the accounting line. If the user wants to override the expired account, they click the checkbox; otherwise, the user must change the account in the accounting line (the user must do one of the two, or keep on getting the error about the override being required forever). The other condition that allows the user an override option is for budget object codes.

Every accounting line on a document has a number of hidden variables related to if the accounting line needs to have an account expiration check box associated with it. Here's an example from a new target accounting line:

When a new accounting line is added, the account and the object code on the lines are checked to see if they qualify for overrides. If so, the appropriate hidden variables are set to "Yes" and the override logic in turns on the appropriate checkbox.

Sales Tax

Some accounts are also subject to sales tax. When a user enters a line subject to sales tax, the accounting line will return with extra fields to fill in for sales tax information.

Two parameters control whether an accounting line is subject to sales tax: KFS-FP / Document / SALES_TAX_APPLICABLE_DOCUMENT_TYPES which is a list of document types that sales tax will be applied to, and KFS-FP / Document / SALES_TAX_APPLICABLE_ACCOUNTS_AND_OBJECT_CODES, which lists the account numbers and object codes of lines that should have sales tax applied to them.

For instance, let's say that we're creating a Disbursement of Income and Expense document using the account BL-9612706 and object code 9015, and that these combinations are listed in the parameters above. When we try to add this accounting line, the line will pop back, requiring sales tax information. Those fields are the chart and account of the account to take the sales tax from, the gross taxable amount, the taxable sales amount, and the sales date. Given this information, when general ledger pending entries are generated, they'll calculate the appropriate sales tax and generate extra entries for that.

Most of the logic for determining what lines need sales tax and how to show those lines exists in KualiAccountingDocumentActionBase, but because the major logic - which accounting lines should have sales tax associated with them - is set in parameters, very few accounting documents are going to have to override any of those methods.

Accounting Line tags

We end our tour of the accounting document view layer by looking at the tags used to display accounting documents. There are two tags in particular are needed on every accounting document page: gl:generalLedgerPendingEntries and fin:accountingLines.

gl:generalLedgerPendingEntries is simple enough. It simply displays the tab of pending ledger entries that it can seen in the document and displays that in a consistent way. It has no attributes; it simply looks at the document from the form and finds the pending ledger entries from the document.

fin:accountingLines is more complex; but then again, it had to be to display all of the various types of accounting lines in documents in KFS. It has a lot of attributes, which give tons of flexibility. It also pawns its work off to a couple other tags, which actually draw the table cells that make up the accounting line. Therefore, let's take a look at the attributes and very cursory discovery of the tags that make up fin:accountingLines.

fin:accountingLines only has one required attribute: editableAccounts, which the form generates from the document authorizer associated with the document in the data dictionary.

There are 22 non-required document attributes:

  • extraSourceRowFields and extraTargetRowFields: these add extra fields to either the source accounting lines or the target accounting lines. Those fields always need to fit in the second line, so there's a definite limit to how many fields we can with this. These attributes are used all over the place; we can see an example of their use on the General Error Correction document, for one.
  • optionalFields and isOptionalFieldsInNewRow: these add extra fields to an accounting line as well...though in this case, many, many, many fields are added to each accounting line. We can see the use of these fields on the Labor Journal Voucher.
  • sourceAccountingLinesOnly: we'll set this to true if we only have one set of accounting lines in our document. Cash Receipt uses this, for instance.
  • editingMode and editableFields: this sets the editing mode and editable fields, both generated by the document authorizer. As such, these attributes are used in several documents.
  • includeObjectTypeCode: this allows the user to edit the object type of an accounting line. This is really rare - most of the time the object type for the document is derived from the object code. However, the general ledger Journal Voucher uses this option.
  • debitCreditAmount: Some documents with one set of accounting lines have two fields for the amount: the debit amount and the credit amount. Naturally, for each line, only one will be filled in. We'll set this to true if we want this behavior. Again, the general ledger Journal Voucher uses this attribute.
  • currentBaseAmount, displayMonthlyAmounts, and accountingLineAttributes: all used by the Budget Adjustment and year end Budget Adjustment, both of which extended the accounting line business object to have monthly totals. displayMonthlyAmounts set to true to get the accounting line tags to display the extra totals.
  • extraHiddenFields: Used by documents such as Budget Adjustment which extend accounting line. accountingLineAttributes is a map with data dictionary values, which the tag can use to figure out how to display the extra fields on the accounting line business object. Finally, currentBaseAmount is set to true if there will be two amount columns. It kind of works like the debitCreditAmount, except the two amount columns are "current" and "base."
  • forcedReadOnlyFields: This attribute takes in a list of field names, and those fields will then be forced to be displayed as read only, no matter what. We can see an example of this being used by the Indirect Cost Adjustment document to set the object codes for all target and source accounting lines.
  • inherit and useCurrencyFormattedTotal: These attributes are used exclusively by two Labor documents, Benefits Expense Transfer and Salary Expense Transfer.
  • accountPrefix, hideTotalLine, hideFields, and accountingAddLineIndex: All of these attributes are used exclusively by Pur/AP documents, such as Credit Memo.
  • suppressBaseline and groupsOverride: Neither of these are used in any current accounting document.

Given all of this flexibility, it makes sense that fin:accountingLines divides its actual display logic among several smaller tags. Source and target line groups are each displayed as fin:accountingLineGroup tags. Each group has an import accounting tags line, rendered with the fin:accountingLineImportRow tag. Each accounting line in a group is rendered with a fin:accountingLineRow tag. This, in turn, renders all of its cells with fin:accountingLineDataCell tags. The buttons are rendered with fin:accountingLineActionDataCell tags. And naturally, there are tags to render override fields and sales tax fields. Most accounting documents will not need to change these tags at all...but as we've seen, each accounting document has its own unique needs and sometimes the rendering code needs to be made more extensible accordingly. There also exists fin:voucherAccountingLines which is really a special call of fin:accountingLines, which allows for source only lines with specific fields editable by the user. This tag is used by Journal Voucher, for instance.

Finally, there are also tags to show a list of items (like the one used in the Internal Billing document), a list of checks (like the one used in the Cash Receipt document), and ones to show currency and coin totals (like the one used in the Cash Management document). When we create new financial documents, it's always a good idea to see if we can use any of the tags that already exist in other accounting documents.

Workflow for Accounting Documents

We finish our tour of accounting documents by looking at the workflow needs for accounting documents. Since accounting documents tend to use the functionality that workflow already provided, most of what we accounting document implementors will focus on will be document types.

As surely covered elsewhere, document types control the routing for all workflow documents. We can also inherit from them; indeed, most accounting documents will inherit from the FinancialDocumentBase workflow document. Inheriting from this gives us two important pieces of functionality:

  • A basic route for accounting workflow documents. While we might want to extend this - as Disbursement Voucher did, extensively - many accounting documents have similar workflow routes. They go through ad hoc routing first, and then they need to go through Account Review and Org Review. Account Review forces the document to be approved by the fiscal officer for the account. Org Review forces the document to be approved by the Fiscal Approver for the organization. (Naturally, if both the account fiscal officer and the organization fiscal approver are the same person, the document need be approved only once).
  • FinancialDocumentBase also gives us a couple of handy extra search attributes. One allows us to find documents that use a given account number. The other gives us a range of total amounts, so that we can search for all Disbursement Vouchers, for instance, that totaled to anywhere between $100 and $500.

Again, most accounting documents will inherit from either FinancialDocumentBase or a child of FinancialDocumentBase, and they'll likely also have similar workflow configurations, though, again, we can customize the workflow configuration extensively on a document by document basis.

One note, however, is warranted to mention here. Let's say that for an accounting document, we need to create an entirely new rule template. Examining everything that entails is far beyond the scope of this document, but it means the creation of a rule attribute, typically done through XML. Let's take a look at a rule attribute commonly used in financial document routing - the KualiSubFundGroupAttribute which is included in the KualiSubFundGroupTemplate.

The important bit in the routingConfig for the rule is the xpathexpression tag. This tag finds what value in the document should match against a rule. For this rule attribute, for instance, we want to match the sub-fund group code of the account of either the source accounting lines or target accounting lines with the rule's value. Now, if we actually looked at a workflow document, we'd see that it uses the exact class name of the class under the <sourceAccountingLines> and <targetAccountingLines> tags. The "kfs_sourceAccountingLineClass" and "kfs_targetAccountingLineClass", however, make sure that no matter what the document type is - and, importantly, no matter which class that document uses for its source and target accounting lines - the proper class name is inserted into the xpathexpression before it is evaluated. So, for instance, on a Distribution of Income and Expense document, the xpathexpression will refer to "//", while on a Budget Adjustment Document, the xpathexpression will refer to "//". The point is that if we're creating a new rule attribute for a new rule template to be used for routing our document type, we need to use "kfs_sourceAccountingLineClass" and "kfs_targetAccountingLineClass" to make sure that other documents using this rule still work, even if they extend the AccountingLine class.


As we've seen, to build all of the various documents with accounting lines in KFS, a large amount of highly reusable and customizable functionality was created, and as we create new accounting documents, we can easily build on top of the model and functionality KFS already has. This makes a lot of sense: KFS was really built to support accounting documents. We've seen how we can build documents, create rules and document authorizers, put together the proper data dictionary, customize the controller and view layers, and finally create the right workflow route for accounting documents. The ability to put this all together with a minimum of big changes means that the accounting document framework provides KFS with a lot of power for each of our institutions.

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