1 Customizing Kuali: A Technical Perspective Naser Alavi (Michigan State University) Warren Liang...
-
Upload
leonard-benson -
Category
Documents
-
view
217 -
download
2
Transcript of 1 Customizing Kuali: A Technical Perspective Naser Alavi (Michigan State University) Warren Liang...
1
Customizing Kuali:A Technical Perspective
Naser Alavi (Michigan State University)
Warren Liang (University of California, Irvine)
3
Customizing Kuali
• Which aspects of Kuali are we customizing in this presentation?– Customizing business rules– Customizing authorization– Customizing system parameters
4
System Parameters/Rules
Purpose:
• Externalizing constants out of code
• They may be maintained by a non-developer via a maintenance document.
• The code must be programmed to access the constant value(s) by its unique key.
5
System Parameters Use Cases
Where they may be used:
• Business rule checks
• Any place where runtime changes to values displayed within the application will be needed without a restart of the application server
6
System Parameters
The System Parameters can be used in the following manners:
• Key-value constants – (key=DEBIT, value=D)
• Key-value group constants– (key=VALID_OBJECT_CODES_FOR_XYZ,
value=5000;5001;3400;2300)
7
System Parameters Use Cases
Where they may not / should not be used:• System configuration values that can’t be
represented as constants/literals in the code• Core constants that will not change no matter
what the installation • Spring XML configuration files - you cannot
have XML files use this mechanism • OJB XML configuration or descriptor files - you
cannot have XML files use this mechanism
8
System Parameters Table
• A single parameter record has a composite key made up of:– Parameter Name – Parameter Security Grouping
• This composite key helps to categorize our constants.– As an enhancement for next phase to further
modularize these parameters a new “Module Code” field will be added to this table
9
Using System Parameters
• Using the System Parameters for Key-Value Constants:
String MANDATORY_TRANSFER = kualiConfigurationService
.getApplicationParameter("TransactionProcessingEDocs", "Chart.SubObjectCode.MandatoryTransfer");
if (financialSubObjectCode.equals(MANDATORY_TRANSFER)) {
...
10
Using System Parameters
• Using the System Parameters for Key-Value Lists of Constants (array of values)
String[] VALID_OBJ_SUB_TYPES_FOR_TOF = SpringServiceLocator.getKualiConfigurationService()
.getApplicationParameters("TransactionProcessing",
"ValidObjectSubTypeCodesForTofAccountingLines");
if(ArrayUtils.contains(VALID_OBJ_SUB_TYPES_FOR_TOF, accountingLine.getFinancialSubObjectCode())) { ...
11
System Parameter Security Class/Table
• The following class represents a security grouping which is identified by its security group name.
• Each security grouping has a workgroup associated with it for authorization purposes.
12
System Parameter Security Class/Table
public class FinancialSystemParameterSecurity extends PersistableBusinessObjectBase {…
// script name is the security groupprivate String financialSystemScriptName;
private String workgroupName; private String financialSystemScriptDescription;
private List financialSystemParameters;…
13
System Parameter Attributes
• The following class represents a KFS parameter with an associated text value.
• This text value can be interpreted as a single value or a semicolon delimited list of values
• The parameter name and the security group name make up the primary key for this business object
• The security group name is the link to the System Parameter Security Table
14
System Parameter Attributes
public class FinancialSystemParameter extends PersistableBusinessObjectBase {
… private String financialSystemScriptName; private String financialSystemParameterName; private String financialSystemParameterText; private String financialSystemParameterDescription; private boolean
financialSystemMultipleValueIndicator;
private FinancialSystemParameterSecurity financialSystemParameterSecurity;…
15
Business Rules
• Maintained by Business Rules Tables
• Allows Functional Users to maintain and create business rules (to some extent).
• Allows institutions to customize out of the box business rules based on their codes and policies.
16
Business Rules
• Uses KualiParameterRule object for evaluating business rules
• This object holds the underlying rule record values and provides a conveniencemethod for the evaluation of the rule
• You can retrieve the values of a rule record as a KualiParameterRule by using the KualiConfigurationService method: KualiParameterRule getApplicationParameterRule(String scriptName, String parameter)
21
Sample Code Using Business Rule inTaxNumberService
/** * Splits the set of tax number formats which are returned
* from the rule service as a semicolon-delimited String * into a String array. * * @return A String array of the tax number format regular
expressions. */ public String[] parseSSNFormats() { if(taxNumberFormats == null) { taxNumberFormats =
SpringServiceLocator.getKualiConfigurationService().getApplicationRule("PurapAdminGroup","PURAP.TAX_NUMBER_FORMATS");
} String taxFormats = taxNumberFormats.getRuleText(); return taxFormats.split(";"); }
22
Sample Code Using Business Rule inTaxNumberService
public boolean isValidTaxNumber( String taxNbr, String taxType ) {
String[] ssnFormats = parseSSNFormats(); String[] feinFormats = parseFEINFormats(); Integer defaultTaxNbrDigits = new Integer (SpringServiceLocator.getKualiConfigurationService().getApplicationParameterValue("PurapAdminGroup","PURAP.DEFAULT_TAX_NUM_DIGITS"));
if (taxNbr.length() != defaultTaxNbrDigits || !isStringAllNumbers(taxNbr)) { return false; }
23
Business Rule Attributes
package org.kuali.core.bo;
public class BusinessRule extends PersistableBusinessObjectBase {
private String ruleGroupName; private String ruleName; private String ruleText; private String ruleDescription; private String ruleOperatorCode; private boolean
financialSystemMultipleValueIndicator; private boolean
financialSystemParameterActiveIndicator;
BusinessRuleSecurity ruleGroup;
24
System Parameters/Rules
Summary:
• Create and populate workgroup that should have maintenance permission
• Create corresponding security group
• Create parameter (value to control runtime system behavior) or rule (definition of legal/illegal values)
25
Customizing Kuali
Outline:
• System Parameters
• Business Rules
• Authorization class
• Pre-Rules
26
Putting It All Together
• Defining a Transactional Document– Payment Request Doc DD XML– Payment Request Rule– Payment Request Authorizer– Payment Request Pre-Rule (using question
framework)– Demo New Payment Request Document
functionality and sample code
27
Plugging Business Rules Class
• “Pluggable” business rules– Specify business rule implementation in
document’s data dictionary file– Registering your business rule class with a
document type.– The fully qualified classname of the business
rule class must be specified with the <businessRuleClass> tag in the document's data dictionary file.
28
Plugging Business Rules Class
Example, KualiPaymentRequestDocument.xml:
<documentClass>org.kuali.module.purap.document.PaymentRequestDocument
</documentClass>
<businessRulesClass>org.kuali.module.purap.rules.PaymentRequestDocumentRule
</businessRulesClass>
29
Plugging Business Rules Class
• “Pluggable” business rules– Normally used to detect errors on a form– Since these are written in Java, it is much more
expressive than just matching– Has access to all Kuali services– The rules framework creates a copy of the
document so that any changes made during rule application will not affect the document when it is persisted.
32
DocumentRuleBase
• DocumentRuleBase implements common checks that should be run after every event, and rule implementations should extend this class
• Instead of overriding the methods defined by the *DocumentRule interfaces, override the processCustom* methods defined in this class (e.g. to define new save rules, override processCustomSaveDocumentBusinessRules instead of processSaveDocument)
33
Associating Rules with Events(Save Example)
package org.kuali.core.rule;
/** * Defines a rule which gets invoked immediately
before a document gets saved. */public interface SaveDocumentRule extends
BusinessRule { /** * @param document * @return false if the rule fails */ public boolean processSaveDocument(Document
document);}
34
Associating Rules with Events and Using Common Services
/** * This class contains all of the business rules that
are common to all documents. */public abstract class DocumentRuleBase implements
SaveDocumentRule, RouteDocumentRule, ApproveDocumentRule, AddNoteRule, AddAdHocRoutePersonRule, AdHocRouteWorkgroupRule {private static UniversalUserService universalUserService;
private static KualiModuleService kualiModuleService;
private static DictionaryValidationService dictionaryValidationService;
private static KualiWorkflowInfo workflowInfoService;
private static KualiConfigurationService kualiConfigurationService;
35
DocumentRuleBase Rule Implementation
public abstract class DocumentRuleBase implements SaveDocumentRule, … {
public boolean processSaveDocument(Document document) { boolean isValid = true; isValid &= isDocumentOverviewValid(document); if (isValid) { isValid &= processCustomSaveDocumentBusinessRules(document); } return isValid; } protected boolean processCustomSaveDocumentBusinessRules (Document document) { return true; }}
36
DocumentRule Sample
public class PaymentRequestDocumentRule extends AccountsPayableDocumentRuleBase {
@Override protected boolean processCustomSaveDocumentBusinessRules (Document document) { PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument) document; return processValidation(paymentRequestDocument); }
37
DocumentRule Sample
public class PaymentRequestDocumentRule extends AccountsPayableDocumentRuleBase {
….@Override public boolean
processValidation(PurchasingAccountsPayableDocument purapDocument) {
boolean valid = super.processValidation(purapDocument);
valid &= processInvoiceValidation((PaymentRequestDocument)purapDocument);
valid &= processPurchaseOrderIDValidation((PaymentRequestDocument)purapDocument);
valid &= processPaymentRequestDateValidation((PaymentRequestDocument)purapDocument);
return valid; }
38
Customizing Kuali
Outline:
• System Parameters
• Business Rules
• Authorization class
• Pre-Rules
39
Plugging Authorizer Class
• Modifying Authorizations for a Document Type– Payment Request Document
• Initiators in Doc DD XML
• Complex Rules in Document Authorizer Class
• Approvals in Workflow
40
Plugging Authorizer Class
Authorization DD File Code Sample:<documentClass>
org.kuali.module.purap.document.PaymentRequestDocument</documentClass><documentAuthorizerClass>
org.kuali.module.purap.document.PaymentRequestDocumentAuthorizer</documentAuthorizerClass> <authorizations> <authorization action="initiate"> <workgroups> <workgroup>kualiUniversalGroup</workgroup> </workgroups> </authorization></authorizations><documentTypeName>KualiPaymentRequestDocument</documentTypeName><documentTypeCode>PREQ</documentTypeCode>
41
Document Authorizer
• The document authorizer framework:– Determines a user can initiate a document– How document fields are rendered– What buttons are displayed
45
Code Modules for PaymentRequest
The functionality demoed was accomplished through codes in:
• PaymentRequestDocumentAuthorizer
• PaymentRequestDocumentRule (already discussed)
• PaymentRequestAction
46
PaymentRequestDocumentAuthorizer
Overrode these methods:• hasInitiateAuthorization
– Simple true/false value
• getEditMode– A map of mode -> value mappings. The JSP
layer uses these mappings to determine how it should render the document
• getDocumentActionFlags– Returns an object with numerous boolean flags,
indicating whether a button may be rendered
47
PaymentRequestDocumentAuthorizer
@Override public boolean hasInitiateAuthorization(Document document,
UniversalUser user) { String authorizedWorkgroup =
SpringServiceLocator.getKualiConfigurationService().getApplicationParameterValue(PurapRuleConstants.PURAP_ADMIN_GROUP, PurapRuleConstants.PURAP_DOCUMENT_PO_ACTIONS);
try { return
SpringServiceLocator.getKualiGroupService().getByGroupName(authorizedWorkgroup).hasMember(user);
} catch (GroupNotFoundException e) { throw new RuntimeException("Workgroup " +
authorizedWorkgroup + " not found",e); } }
48
PaymentRequestDocumentAuthorizer
@Override public Map getEditMode(Document document, UniversalUser
user, List sourceAccountingLines, List targetAccountingLines) {Map editModeMap = super.getEditMode(document, user, sourceAccountingLines, targetAccountingLines);
PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument)document;
if (StringUtils.equals(paymentRequestDocument.getStatusCode(),PurapConstants.PaymentRequestStatuses.INITIATE)){
editModeMap.put(PurapAuthorizationConstants.PaymentRequestEditMode.DISPLAY_INIT_TAB, "TRUE");
} else {
editModeMap.put(PurapAuthorizationConstants.PaymentRequestEditMode.DISPLAY_INIT_TAB, "FALSE");
} return editModeMap; }
49
PaymentRequestDocumentAuthorizer
The JSP uses the values in the edit mode map to determine how to render the document.
Example from PaymentRequest.jsp
<c:if test="${not KualiForm.editingMode['displayInitTab']}" >
<kul:documentOverview editingMode="${KualiForm.editingMode}" includePostingYear="true“ />
</c:if>
<c:if test="${KualiForm.editingMode['displayInitTab']}" >
<purap:paymentRequestInit documentAttributes="${DataDictionary.KualiPaymentRequestDocument.attributes}“ />
</c:if>
50
PaymentRequestDocumentAuthorizer
@Override public DocumentActionFlags getDocumentActionFlags(Document document,
UniversalUser user) { DocumentActionFlags flags = super.getDocumentActionFlags(document, user); KualiWorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument(); PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument)document; if (StringUtils.equals(paymentRequestDocument .getStatusCode(), INITIATE)){ flags.setCanSave(false); flags.setCanClose(false); flags.setCanCancel(true);
} else { flags.setCanSave(true);
} return flags;
}
51
Payment Request Duplication Scenario
• Needed to check for Payment Request duplication before even validating business rules
• In case of duplication give the user a warning and the option of override the warning and continue or to cancel
• We used the question framework for this• Demo this in action in Payment Request • Demo the code
(paymentRequestAction.continuePREQ())
52
PaymentRequestAction
public ActionForward continuePREQ(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { PaymentRequestForm preqForm = (PaymentRequestForm) form;
PaymentRequestDocument paymentRequestDocument = (PaymentRequestDocument) preqForm.getDocument();
Map editMode = preqForm.getEditingMode(); Object question =
request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
PaymentRequestService paymentRequestService =
SpringServiceLocator.getPaymentRequestService(); HashMap<String, String> duplicateMessages =
paymentRequestService.paymentRequestDuplicateMessages(paymentRequestDocument);
53
PaymentRequestAction (Continued)
if (!duplicateMessages.isEmpty()){ if (question == null) { // ask question if not already asked return this.performQuestionWithoutInput(mapping, form,
request, response, PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, duplicateMessages.get(PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION) , KFSConstants.CONFIRMATION_QUESTION, KFSConstants.ROUTE_METHOD, "");
} Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
if
((PurapConstants.PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) {
// if no button clicked just reload the doc in the // INITIATE status and let the user to change the // input values
paymentRequestDocument.setStatusCode(PurapConstants.PaymentRequestStatuses.INITIATE);return mapping.findForward(KFSConstants.MAPPING_BASIC);
} }
54
Customizing Kuali
Outline:
• System Parameters
• Business Rules
• Authorization class
• Pre-Rules
55
Pre-Rules Use Cases
• Pre-Rules allow– Modification of values after form submission
(e.g. auto-fill values, possibly based on other values on the form)
– Switching to another page after clicking the button (e.g. go to a confirmation page)
56
Pre-Rules Configuration
• Pre-rules for documents are configured in the data dictionary by specifying a class name for the implementation
• Example: in UniversalUserMaintenanceDocument.xml<preRulesCheckClass>
org.kuali.core.rules.UniversalUserPreRules
</preRulesCheckClass>
58
PreRulesCheck Interface
public interface PreRulesCheck {
/** * Callback method from Maintenance action that
allows checks to be done and response redirected via the PreRulesCheckEvent
* * @param form * @param request * @param event * @return boolean indicating whether routing
should stop or continue */ public boolean processPreRuleChecks(ActionForm
form, HttpServletRequest request, PreRulesCheckEvent event);
}
59
PreRulesContinuationBase
• This class simplifies requesting clarifying user input prior to applying business rules.
• It mostly shields the classes that extend it from being aware of the web layer, even though the input is collected via a series of one or more request/response cycles.
61
PreRulesContinuationBase
public abstract class PreRulesContinuationBase implements PreRulesCheck {
protected String question; protected String buttonClicked; protected PreRulesCheckEvent event; protected KualiForm form;
public abstract boolean doRules(Document
document);
62
PreRulesContinuationBase
public boolean processPreRuleChecks(ActionForm form, HttpServletRequest request, PreRulesCheckEvent event) {
… try { result = doRules(event.getDocument()); } catch (IsAskingException e) { return false; } if (isAborting) { return false; } return result;}
64
Putting PreRules* in Action
• Pre-rules are invoked automatically by the framework before routing, approving, and blanket approving a document.
• Can also force the invocation of a pre-rule by overriding the Struts action handler of the document.
65
Example 1: Adding a pre-rule check
public class BudgetParametersAction extends BudgetAction { public ActionForward deletePeriodLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // do some processing ActionForward preRulesForward = preRulesCheck(mapping, form, request, response); if (preRulesForward != null) { return preRulesForward; } return mapping.findForward(KFSConstants.MAPPING_BASIC); } …
66
Example 2: UniversalUserPreRules
public class UniversalUserPreRules extends PreRulesContinuationBase {
public boolean doRules(Document document) {
MaintenanceDocument maintenanceDocument = (MaintenanceDocument) document;
UniversalUser newUser = (UniversalUser) maintenanceDocument.getNewMaintainableObject().getBusinessObject();
Field nameField = FieldUtils.getPropertyField( UniversalUser.class, PropertyConstants.PERSON_NAME, false );
ControlDefinition controlDef = KNSServiceLocator.getDataDictionaryService().getAttributeControlDefinition(UniversalUser.class, PropertyConstants.PERSON_NAME );
67
Example 2: UniversalUserPreRules (continued)
if ( controlDef.isHidden() || nameField.isReadOnly() || StringUtils.isBlank( newUser.getPersonName() ) ){
if ( !StringUtils.isBlank( newUser.getPersonFirstName() ) && !StringUtils.isBlank( newUser.getPersonLastName() ) ){
if ( !StringUtils.isBlank( newUser.getPersonMiddleName() ) ) { newUser.setPersonName( newUser.getPersonLastName()+","
+newUser.getPersonFirstName() + " " + newUser.getPersonMiddleName() ); } else { newUser.setPersonName( newUser.getPersonLastName()+","
+newUser.getPersonFirstName() ); } } } boolean success = true; List<KualiModule> modules =
KNSServiceLocator.getKualiModuleService().getInstalledModules(); PreRulesContinuationBase rule = null; for ( KualiModule module : modules ) { rule = (PreRulesContinuationBase) module.getModuleUserPreRules(); if ( rule != null ) { success &= rule.doRules( document ); } }
68
Pre-Rule Scenario Sample 3
• Pre-Rules could be used to derive values based on other values, or
• Setting a default value where needed and the user didn’t enter any value– See next sample code
69
Example 3: Set Unconditional Defaults
public class DelegateChangePreRules implements PreRulesCheck { ...
private void setUnconditionalDefaults(DelegateChangeContainer newDelegateChangeContainer) {
for (DelegateChangeDocument newDelegateChange : newDelegateChangeContainer.getDelegateChanges()) {
// FROM amount defaults to zero if (newDelegateChange.getApprovalFromThisAmount()==null) { newDelegateChange.setApprovalFromThisAmount(new
KualiDecimal(0)); }
// TO amount defaults to zero if (newDelegateChange.getApprovalToThisAmount()==null) { newDelegateChange.setApprovalToThisAmount(new KualiDecimal(0)); } }
}
70
Summary
• System parameters– Provides on-the-fly configuration of certain
components
• Business Rules – Provides business rules based on matching
values
71
Summary
• “Pluggable” business rules– Java-based implementation of rules that are
more complicated than simple matching
• Pre-rules– Allows modification of form values prior to
processing– Allows “redirection” to a different page