1 Customizing Kuali: A Technical Perspective Naser Alavi (Michigan State University) Warren Liang...

73
1 Customizing Kuali: A Technical Perspective Naser Alavi (Michigan State University) Warren Liang (University of California, Irvine)

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)

2

Customizing Kuali

Outline:

• System Parameters

• Business Rules

• Authorization class

• Pre-Rules

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)

17

Business Rule From Administration Tab

18

Business Rule Security Example

19

Business Rule Example

20

Business Rule Examples

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.

30

Payment Request Rule Hierarchy

31

Business Rule Framework

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

42

Create New Payment Request

43

Checking for Duplication

44

Showing Other Action Buttons

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>

57

Pre Rule Checks Architecture

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.

60

PreRulesContinuationBase

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;}

63

PreRulesCheckEvent

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

72

Questions?

[email protected]

73

References

• System parameters/ business rules

https://test.kuali.org/confluence/x/-kw

• Pluggable business rules

https://test.kuali.org/confluence/x/5l

• Pre-rules

https://test.kuali.org/confluence/x/WFE

• Kuali Nervous System (KNS)

https://test.kuali.org/confluence/x/EU8