0001: /*
0002: * Copyright 2005-2007 The Kuali Foundation.
0003: *
0004: * Licensed under the Educational Community License, Version 1.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.opensource.org/licenses/ecl1.php
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.kuali.module.financial.rules;
0017:
0018: import static org.kuali.kfs.KFSConstants.GL_CREDIT_CODE;
0019: import static org.kuali.kfs.KFSConstants.GL_DEBIT_CODE;
0020:
0021: import java.util.List;
0022:
0023: import org.apache.commons.lang.StringUtils;
0024: import org.kuali.core.bo.PersistableBusinessObject;
0025: import org.kuali.core.bo.user.AuthenticationUserId;
0026: import org.kuali.core.bo.user.PersonTaxId;
0027: import org.kuali.core.bo.user.UniversalUser;
0028: import org.kuali.core.document.Document;
0029: import org.kuali.core.exceptions.UserNotFoundException;
0030: import org.kuali.core.rule.event.ApproveDocumentEvent;
0031: import org.kuali.core.service.BusinessObjectService;
0032: import org.kuali.core.service.DictionaryValidationService;
0033: import org.kuali.core.service.DocumentAuthorizationService;
0034: import org.kuali.core.service.UniversalUserService;
0035: import org.kuali.core.util.ErrorMap;
0036: import org.kuali.core.util.GeneralLedgerPendingEntrySequenceHelper;
0037: import org.kuali.core.util.GlobalVariables;
0038: import org.kuali.core.util.KualiDecimal;
0039: import org.kuali.core.util.ObjectUtils;
0040: import org.kuali.core.workflow.service.KualiWorkflowDocument;
0041: import org.kuali.kfs.KFSConstants;
0042: import org.kuali.kfs.KFSKeyConstants;
0043: import org.kuali.kfs.KFSPropertyConstants;
0044: import org.kuali.kfs.bo.AccountingLine;
0045: import org.kuali.kfs.bo.GeneralLedgerPendingEntry;
0046: import org.kuali.kfs.bo.SourceAccountingLine;
0047: import org.kuali.kfs.context.SpringContext;
0048: import org.kuali.kfs.document.AccountingDocument;
0049: import org.kuali.kfs.rule.GenerateGeneralLedgerDocumentPendingEntriesRule;
0050: import org.kuali.kfs.rules.AccountingDocumentRuleBase;
0051: import org.kuali.kfs.service.OptionsService;
0052: import org.kuali.kfs.service.ParameterEvaluator;
0053: import org.kuali.module.chart.bo.ChartUser;
0054: import org.kuali.module.chart.bo.ObjectCode;
0055: import org.kuali.module.financial.bo.DisbursementVoucherNonEmployeeExpense;
0056: import org.kuali.module.financial.bo.DisbursementVoucherPayeeDetail;
0057: import org.kuali.module.financial.bo.NonResidentAlienTaxPercent;
0058: import org.kuali.module.financial.bo.Payee;
0059: import org.kuali.module.financial.bo.TravelCompanyCode;
0060: import org.kuali.module.financial.bo.WireCharge;
0061: import org.kuali.module.financial.document.DisbursementVoucherDocument;
0062: import org.kuali.module.financial.document.authorization.DisbursementVoucherDocumentAuthorizer;
0063: import org.kuali.module.financial.service.DisbursementVoucherTaxService;
0064: import org.kuali.module.financial.service.DisbursementVoucherTravelService;
0065: import org.kuali.module.financial.service.UniversityDateService;
0066:
0067: /**
0068: * Business rule(s) applicable to Disbursement Voucher documents.
0069: */
0070: public class DisbursementVoucherDocumentRule extends
0071: AccountingDocumentRuleBase
0072: implements
0073: DisbursementVoucherRuleConstants,
0074: GenerateGeneralLedgerDocumentPendingEntriesRule<AccountingDocument> {
0075: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
0076: .getLogger(DisbursementVoucherDocumentRule.class);
0077: private static final String DV_PAYMENT_REASON_PROPERTY_PATH = KFSPropertyConstants.DV_PAYEE_DETAIL
0078: + "." + KFSPropertyConstants.DISB_VCHR_PAYMENT_REASON_CODE;
0079: private static final String DV_PAYEE_ID_NUMBER_PROPERTY_PATH = KFSPropertyConstants.DV_PAYEE_DETAIL
0080: + "." + KFSPropertyConstants.DISB_VCHR_PAYEE_ID_NUMBER;
0081: private static String taxGroupName;
0082: private static String travelGroupName;
0083: private static String wireTransferGroupName;
0084: private static String frnGroupName;
0085: private static String adminGroupName;
0086:
0087: /**
0088: * Constructs a DisbursementVoucherDocumentRule instance.
0089: */
0090: public DisbursementVoucherDocumentRule() {
0091: setMaxDictionaryValidationDepth(0);
0092: }
0093:
0094: /**
0095: * Returns true disbursement voucher can be saved successfully (i.e. a non-employee travel company and prepaid expenses company is provided)
0096: *
0097: * @param document submitted disbursement voucher document
0098: * @return true if disbursement voucher can be saved successfully
0099: *
0100: * @see org.kuali.core.rules.DocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.core.document.Document)
0101: */
0102: @Override
0103: protected boolean processCustomSaveDocumentBusinessRules(
0104: Document document) {
0105: boolean valid = super
0106: .processCustomSaveDocumentBusinessRules(document);
0107:
0108: DisbursementVoucherDocument disbursementVoucherDocument = (DisbursementVoucherDocument) document;
0109:
0110: // check non employee travel company exists
0111: int i = 0;
0112: List<DisbursementVoucherNonEmployeeExpense> expenses = disbursementVoucherDocument
0113: .getDvNonEmployeeTravel().getDvNonEmployeeExpenses();
0114: for (DisbursementVoucherNonEmployeeExpense expense : expenses) {
0115: TravelCompanyCode travelCompanyCode = retrieveCompany(
0116: expense.getDisbVchrExpenseCode(), expense
0117: .getDisbVchrExpenseCompanyName());
0118:
0119: if (ObjectUtils.isNull(travelCompanyCode)) {
0120: GlobalVariables
0121: .getErrorMap()
0122: .putErrorWithoutFullErrorPath(
0123: KFSPropertyConstants.DOCUMENT
0124: + "."
0125: + KFSPropertyConstants.DV_NON_EMPLOYEE_TRAVEL
0126: + "."
0127: + KFSPropertyConstants.DV_NON_EMPLOYEE_EXPENSES
0128: + "["
0129: + i
0130: + "]"
0131: + "."
0132: + KFSPropertyConstants.DISB_VCHR_EXPENSE_COMPANY_NAME,
0133: KFSKeyConstants.ERROR_EXISTENCE,
0134: "Company ");
0135: }
0136:
0137: i++;
0138: }
0139:
0140: // check prepaid expenses company exists
0141: i = 0;
0142: List<DisbursementVoucherNonEmployeeExpense> prePaidExpenses = disbursementVoucherDocument
0143: .getDvNonEmployeeTravel()
0144: .getDvPrePaidEmployeeExpenses();
0145: for (DisbursementVoucherNonEmployeeExpense prePaidExpense : prePaidExpenses) {
0146: TravelCompanyCode travelCompanyCode = retrieveCompany(
0147: prePaidExpense.getDisbVchrExpenseCode(),
0148: prePaidExpense.getDisbVchrExpenseCompanyName());
0149:
0150: if (ObjectUtils.isNull(travelCompanyCode)) {
0151: GlobalVariables
0152: .getErrorMap()
0153: .putErrorWithoutFullErrorPath(
0154: KFSPropertyConstants.DOCUMENT
0155: + "."
0156: + KFSPropertyConstants.DV_NON_EMPLOYEE_TRAVEL
0157: + "."
0158: + KFSPropertyConstants.DV_PRE_PAID_EMPLOYEE_EXPENSES
0159: + "["
0160: + i
0161: + "]"
0162: + "."
0163: + KFSPropertyConstants.DISB_VCHR_EXPENSE_COMPANY_NAME,
0164: KFSKeyConstants.ERROR_EXISTENCE,
0165: "Company ");
0166: }
0167:
0168: i++;
0169: }
0170:
0171: return valid;
0172: }
0173:
0174: /**
0175: * Overrides to call super. If super fails, then we invoke some DV specific rules about FO routing to double check if the
0176: * individual has special conditions that they can alter accounting lines by.
0177: *
0178: * @param financialdocument submitted disbursement voucher document
0179: * @param accountingLine accounting line in disbursement voucher
0180: * @param action accounting line action
0181: * @return true if accounting line is accessible
0182: *
0183: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#checkAccountingLineAccountAccessibility(org.kuali.core.document.FinancialDocument,
0184: * org.kuali.core.bo.AccountingLine, org.kuali.module.financial.rules.FinancialDocumentRuleBase.AccountingLineAction)
0185: */
0186: @Override
0187: protected boolean checkAccountingLineAccountAccessibility(
0188: AccountingDocument financialDocument,
0189: AccountingLine accountingLine, AccountingLineAction action) {
0190: // first check parent's isAccessible method for basic FO authz checking
0191: boolean isAccessible = accountIsAccessible(financialDocument,
0192: accountingLine);
0193:
0194: // get the authorizer class to check for special conditions routing and if the user is part of a particular workgroup
0195: // but only if the document is enroute
0196: if (!isAccessible
0197: && financialDocument.getDocumentHeader()
0198: .getWorkflowDocument().stateIsEnroute()) {
0199: DisbursementVoucherDocumentAuthorizer dvAuthorizer = (DisbursementVoucherDocumentAuthorizer) SpringContext
0200: .getBean(DocumentAuthorizationService.class)
0201: .getDocumentAuthorizer(financialDocument);
0202: // if approval is requested and it is special conditions routing and the user is in a special conditions routing
0203: // workgroup then
0204: // the line is accessible
0205: if (financialDocument.getDocumentHeader()
0206: .getWorkflowDocument().isApprovalRequested()
0207: && dvAuthorizer.isSpecialRouting(financialDocument,
0208: GlobalVariables.getUserSession()
0209: .getUniversalUser())
0210: && (isUserInTaxGroup() || isUserInTravelGroup()
0211: || isUserInFRNGroup()
0212: || isUserInWireGroup() || isUserInDvAdminGroup())) {
0213: isAccessible = true;
0214: }
0215: }
0216:
0217: // report (and log) errors
0218: if (!isAccessible) {
0219: String[] errorParams = new String[] {
0220: accountingLine.getAccountNumber(),
0221: GlobalVariables.getUserSession().getUniversalUser()
0222: .getPersonUserIdentifier() };
0223: GlobalVariables.getErrorMap().putError(
0224: KFSPropertyConstants.ACCOUNT_NUMBER,
0225: action.accessibilityErrorKey, errorParams);
0226: }
0227:
0228: return isAccessible;
0229: }
0230:
0231: /**
0232: * Returns true if processCustomAddAccountingLineBusinessRules(financialDocument, updatedAccountingLine) returns true.
0233: *
0234: * @param financialDocument submitted disbursement voucher document
0235: * @param originalAccountingLine original accounting line
0236: * @param updatedAccountingLine updated accounting line
0237: * @return same value as processCustomAddAccountingLineBusinessRules(financialDocument, updatedAccountingLine)
0238: *
0239: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#processCustomUpdateAccountingLineBusinessRules(org.kuali.core.document.FinancialDocument,
0240: * org.kuali.core.bo.AccountingLine, org.kuali.core.bo.AccountingLine)
0241: */
0242: @Override
0243: protected boolean processCustomUpdateAccountingLineBusinessRules(
0244: AccountingDocument financialDocument,
0245: AccountingLine originalAccountingLine,
0246: AccountingLine updatedAccountingLine) {
0247: return processCustomAddAccountingLineBusinessRules(
0248: financialDocument, updatedAccountingLine);
0249: }
0250:
0251: /**
0252: * Override to check if we are in special handling where the check amount and accounting line total can decrease, else amounts
0253: * should not have changed.
0254: *
0255: * @param approveEvent event fired when approving document
0256: * @return true check total did not decrease
0257: *
0258: * @see org.kuali.core.rule.DocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.core.rule.event.ApproveDocumentEvent)
0259: */
0260: @Override
0261: protected boolean processCustomApproveDocumentBusinessRules(
0262: ApproveDocumentEvent approveEvent) {
0263: DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) approveEvent
0264: .getDocument();
0265:
0266: // amounts can only decrease
0267: DisbursementVoucherDocumentAuthorizer dvAuthorizer = (DisbursementVoucherDocumentAuthorizer) SpringContext
0268: .getBean(DocumentAuthorizationService.class)
0269: .getDocumentAuthorizer(dvDocument);
0270: if (dvAuthorizer.isSpecialRouting(dvDocument, GlobalVariables
0271: .getUserSession().getUniversalUser())
0272: && (isUserInTaxGroup() || isUserInTravelGroup()
0273: || isUserInFRNGroup() || isUserInWireGroup())) {
0274: boolean approveOK = true;
0275:
0276: // users in foreign or wire workgroup can increase or decrease amounts because of currency conversion
0277: if (!isUserInFRNGroup() && !isUserInWireGroup()) {
0278: DisbursementVoucherDocument persistedDocument = (DisbursementVoucherDocument) retrievePersistedDocument(dvDocument);
0279: if (persistedDocument == null) {
0280: handleNonExistentDocumentWhenApproving(dvDocument);
0281: return approveOK;
0282: } else {
0283: // check total cannot decrease
0284: if (persistedDocument
0285: .getDisbVchrCheckTotalAmount()
0286: .isLessThan(
0287: dvDocument
0288: .getDisbVchrCheckTotalAmount())) {
0289: GlobalVariables
0290: .getErrorMap()
0291: .putError(
0292: KFSPropertyConstants.DOCUMENT
0293: + "."
0294: + KFSPropertyConstants.DISB_VCHR_CHECK_TOTAL_AMOUNT,
0295: KFSKeyConstants.ERROR_DV_CHECK_TOTAL_CHANGE);
0296: approveOK = false;
0297: }
0298: }
0299: }
0300:
0301: return approveOK;
0302: } else {
0303: // amounts must not have been changed
0304: return super
0305: .processCustomApproveDocumentBusinessRules(approveEvent);
0306: }
0307: }
0308:
0309: /**
0310: * Return true if accounting line can be added successfully (i.e. payment reason and payee must be selected before
0311: * accounting line can be entered)
0312: *
0313: * @param financialDocument submitted financial document
0314: * @param accountingLine accounting line
0315: * @return true if accounting line can be added successfully (i.e. payment reason and payee must be selected before
0316: * accounting line can be entered)
0317: *
0318: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#processCustomAddAccountingLineBusinessRules(org.kuali.core.document.FinancialDocument,
0319: * org.kuali.core.bo.AccountingLine)
0320: */
0321: @Override
0322: public boolean processCustomAddAccountingLineBusinessRules(
0323: AccountingDocument financialDocument,
0324: AccountingLine accountingLine) {
0325: boolean allow = true;
0326:
0327: LOG.debug("validating accounting line # "
0328: + accountingLine.getSequenceNumber());
0329:
0330: // don't validate generated tax lines
0331: if (((DisbursementVoucherDocument) financialDocument)
0332: .getDvNonResidentAlienTax() != null) {
0333: List taxLineNumbers = SpringContext
0334: .getBean(DisbursementVoucherTaxService.class)
0335: .getNRATaxLineNumbers(
0336: ((DisbursementVoucherDocument) financialDocument)
0337: .getDvNonResidentAlienTax()
0338: .getFinancialDocumentAccountingLineText());
0339: if (taxLineNumbers.contains(accountingLine
0340: .getSequenceNumber())) {
0341: return true;
0342: }
0343: }
0344:
0345: DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) financialDocument;
0346: ErrorMap errors = GlobalVariables.getErrorMap();
0347:
0348: /* payment reason must be selected before an accounting line can be entered */
0349: if (StringUtils.isBlank(dvDocument.getDvPayeeDetail()
0350: .getDisbVchrPaymentReasonCode())) {
0351: if (!errors
0352: .containsMessageKey(KFSKeyConstants.ERROR_DV_ADD_LINE_MISSING_PAYMENT_REASON)) {
0353: errors
0354: .putErrorWithoutFullErrorPath(
0355: KFSPropertyConstants.DOCUMENT
0356: + "."
0357: + DV_PAYMENT_REASON_PROPERTY_PATH,
0358: KFSKeyConstants.ERROR_DV_ADD_LINE_MISSING_PAYMENT_REASON);
0359: }
0360: allow = false;
0361: }
0362:
0363: /* payee must be selected before an accounting line can be entered */
0364: if (StringUtils.isBlank(dvDocument.getDvPayeeDetail()
0365: .getDisbVchrPayeeIdNumber())) {
0366: if (!errors
0367: .containsMessageKey(KFSKeyConstants.ERROR_DV_ADD_LINE_MISSING_PAYEE)) {
0368: errors
0369: .putErrorWithoutFullErrorPath(
0370: KFSPropertyConstants.DOCUMENT
0371: + "."
0372: + DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
0373: KFSKeyConstants.ERROR_DV_ADD_LINE_MISSING_PAYEE);
0374: }
0375: allow = false;
0376: }
0377:
0378: if (allow) {
0379: LOG.debug("beginning object code validation ");
0380: allow = validateObjectCode(financialDocument,
0381: accountingLine);
0382:
0383: LOG.debug("beginning account number validation ");
0384: allow = allow
0385: & validateAccountNumber(financialDocument,
0386: accountingLine);
0387: }
0388:
0389: LOG.debug("end validating accounting line, has errors: "
0390: + allow);
0391:
0392: return allow;
0393: }
0394:
0395: /**
0396: * Final business rule edits on routing of disbursement voucher document.
0397: *
0398: * @param document submitted disbursement voucher document
0399: * @return true is disbursement voucher document can be routed with out any problems
0400: *
0401: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.core.document.FinancialDocument)
0402: */
0403: @Override
0404: protected boolean processCustomRouteDocumentBusinessRules(
0405: Document document) {
0406: DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) document;
0407: DisbursementVoucherPayeeDetail payeeDetail = dvDocument
0408: .getDvPayeeDetail();
0409:
0410: GlobalVariables.getErrorMap().addToErrorPath(
0411: KFSPropertyConstants.DOCUMENT);
0412:
0413: LOG.debug("processing route rules for document "
0414: + document.getDocumentNumber());
0415:
0416: validateDocumentFields(dvDocument);
0417:
0418: LOG.debug("validating payment reason");
0419: validatePaymentReason(dvDocument);
0420:
0421: LOG.debug("validating payee initiator id");
0422: validatePayeeInitiatorID(dvDocument);
0423:
0424: if (payeeDetail.isPayee()) {
0425: LOG.debug("validating payee information");
0426: validatePayeeInformation(dvDocument);
0427: }
0428:
0429: if (payeeDetail.isEmployee()) {
0430: LOG.debug("validating employee information");
0431: validateEmployeeInformation(dvDocument);
0432: }
0433:
0434: /* specific validation depending on payment method */
0435: if (PAYMENT_METHOD_WIRE.equals(dvDocument
0436: .getDisbVchrPaymentMethodCode())) {
0437: LOG.debug("validating wire transfer");
0438: validateWireTransfer(dvDocument);
0439: } else if (PAYMENT_METHOD_DRAFT.equals(dvDocument
0440: .getDisbVchrPaymentMethodCode())) {
0441: LOG.debug("validating foreign draft");
0442: validateForeignDraft(dvDocument);
0443: }
0444:
0445: /* if nra payment and user is in tax group, check nra tab */
0446: if (dvDocument.getDvPayeeDetail().isDisbVchrAlienPaymentCode()
0447: && isUserInTaxGroup()) {
0448: LOG.debug("validating non resident alien tax");
0449: validateNonResidentAlienInformation(dvDocument);
0450: }
0451:
0452: // non-employee travel
0453:
0454: // retrieve nonemployee travel payment reasons
0455: if (isTravelNonEmplPaymentReason(dvDocument)) {
0456: LOG.debug("validating non employee travel");
0457: validateNonEmployeeTravel(dvDocument);
0458: }
0459:
0460: // pre-paid travel
0461:
0462: // retrieve prepaid travel payment reasons
0463: if (isTravelPrepaidPaymentReason(dvDocument)) {
0464: LOG.debug("validating pre paid travel");
0465: validatePrePaidTravel(dvDocument);
0466: }
0467:
0468: LOG.debug("validating document amounts");
0469: validateDocumentAmounts(dvDocument);
0470:
0471: LOG.debug("validating accounting line counts");
0472: validateAccountingLineCounts(dvDocument);
0473:
0474: LOG.debug("validating documentaton location");
0475: validateDocumentationLocation(dvDocument);
0476:
0477: GlobalVariables.getErrorMap().removeFromErrorPath(
0478: KFSPropertyConstants.DOCUMENT);
0479:
0480: LOG
0481: .debug("finished route validation for document, has errors: "
0482: + !GlobalVariables.getErrorMap().isEmpty());
0483:
0484: return GlobalVariables.getErrorMap().isEmpty();
0485: }
0486:
0487: /**
0488: * Returns whether the document's payment reason is for travel by a non-employee
0489: *
0490: * @param disbursementVoucherDocument submitted disbursement voucher document
0491: * @return true if payment reason is travel by a non-employee
0492: *
0493: */
0494: public boolean isTravelNonEmplPaymentReason(
0495: DisbursementVoucherDocument disbursementVoucherDocument) {
0496: ParameterEvaluator travelNonEmplPaymentReasonEvaluator = getParameterService()
0497: .getParameterEvaluator(
0498: DisbursementVoucherDocument.class,
0499: DisbursementVoucherRuleConstants.NONEMPLOYEE_TRAVEL_PAY_REASONS_PARM_NM,
0500: disbursementVoucherDocument.getDvPayeeDetail()
0501: .getDisbVchrPaymentReasonCode());
0502: return travelNonEmplPaymentReasonEvaluator.evaluationSucceeds();
0503: }
0504:
0505: /**
0506: * Returns whether the document's payment reason is for prepaid travel
0507: *
0508: * @param disbursementVoucherDocument
0509: * @return true if payment reason is for pre-paid travel reason
0510: */
0511: public boolean isTravelPrepaidPaymentReason(
0512: DisbursementVoucherDocument disbursementVoucherDocument) {
0513: ParameterEvaluator travelNonEmplPaymentReasonEvaluator = getParameterService()
0514: .getParameterEvaluator(
0515: DisbursementVoucherDocument.class,
0516: DisbursementVoucherRuleConstants.PREPAID_TRAVEL_PAY_REASONS_PARM_NM,
0517: disbursementVoucherDocument.getDvPayeeDetail()
0518: .getDisbVchrPaymentReasonCode());
0519: return travelNonEmplPaymentReasonEvaluator.evaluationSucceeds();
0520: }
0521:
0522: /**
0523: * Override to change the doc type based on payment method. This is needed to pick up different offset definitions.
0524: *
0525: * @param financialDocument submitted accounting document
0526: * @param accountingLine accounting line in submitted accounting document
0527: * @param explicitEntry explicit GLPE
0528: *
0529: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.core.document.FinancialDocument,
0530: * org.kuali.core.bo.AccountingLine, org.kuali.module.gl.bo.GeneralLedgerPendingEntry)
0531: */
0532: protected void customizeExplicitGeneralLedgerPendingEntry(
0533: AccountingDocument financialDocument,
0534: AccountingLine accountingLine,
0535: GeneralLedgerPendingEntry explicitEntry) {
0536: DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) financialDocument;
0537:
0538: /* change document type based on payment method to pick up different offsets */
0539: if (PAYMENT_METHOD_CHECK.equals(dvDocument
0540: .getDisbVchrPaymentMethodCode())) {
0541: LOG.debug("changing doc type on pending entry "
0542: + explicitEntry
0543: .getTransactionLedgerEntrySequenceNumber()
0544: + " to " + DOCUMENT_TYPE_CHECKACH);
0545: explicitEntry
0546: .setFinancialDocumentTypeCode(DOCUMENT_TYPE_CHECKACH);
0547: } else {
0548: LOG.debug("changing doc type on pending entry "
0549: + explicitEntry
0550: .getTransactionLedgerEntrySequenceNumber()
0551: + " to " + DOCUMENT_TYPE_CHECKACH);
0552: explicitEntry
0553: .setFinancialDocumentTypeCode(DOCUMENT_TYPE_WTFD);
0554: }
0555: }
0556:
0557: /**
0558: * Return true if GLPE's are generated successfully (i.e. there are either 0 GLPE's or 1 GLPE in dibursement voucher document)
0559: *
0560: * @param financialDocument submitted financial document
0561: * @param sequenceHelper helper class to keep track of GLPE sequence
0562: * @return true if GLPE's are generated successfully
0563: *
0564: * @see org.kuali.core.rule.GenerateGeneralLedgerDocumentPendingEntriesRule#processGenerateDocumentGeneralLedgerPendingEntries(org.kuali.core.document.FinancialDocument,org.kuali.core.util.GeneralLedgerPendingEntrySequenceHelper)
0565: */
0566: public boolean processGenerateDocumentGeneralLedgerPendingEntries(
0567: AccountingDocument financialDocument,
0568: GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
0569: DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) financialDocument;
0570: if (dvDocument.getGeneralLedgerPendingEntries() == null
0571: || dvDocument.getGeneralLedgerPendingEntries().size() < 2) {
0572: LOG.warn("No gl entries for accounting lines.");
0573: return true;
0574: // throw new RuntimeException("No gl entries for accounting lines.");
0575: }
0576:
0577: /*
0578: * only generate additional charge entries for payment method wire charge, and if the fee has not been waived
0579: */
0580: if (PAYMENT_METHOD_WIRE.equals(dvDocument
0581: .getDisbVchrPaymentMethodCode())
0582: && !dvDocument
0583: .getDvWireTransfer()
0584: .isDisbursementVoucherWireTransferFeeWaiverIndicator()) {
0585: LOG.debug("generating wire charge gl pending entries.");
0586:
0587: // retrieve wire charge
0588: WireCharge wireCharge = retrieveWireCharge();
0589:
0590: // generate debits
0591: GeneralLedgerPendingEntry chargeEntry = processWireChargeDebitEntries(
0592: dvDocument, sequenceHelper, wireCharge);
0593:
0594: // generate credits
0595: processWireChargeCreditEntries(dvDocument, sequenceHelper,
0596: wireCharge, chargeEntry);
0597: }
0598:
0599: return true;
0600: }
0601:
0602: /**
0603: * Builds an explicit and offset for the wire charge debit. The account associated with the first accounting is used for the
0604: * debit. The explicit and offset entries for the first accounting line and copied and customized for the wire charge.
0605: *
0606: * @param dvDocument submitted disbursement voucher document
0607: * @param sequenceHelper helper class to keep track of GLPE sequence
0608: * @param wireCharge wireCharge object from current fiscal year
0609: * @return GeneralLedgerPendingEntry generated wire charge debit
0610: */
0611: private GeneralLedgerPendingEntry processWireChargeDebitEntries(
0612: DisbursementVoucherDocument dvDocument,
0613: GeneralLedgerPendingEntrySequenceHelper sequenceHelper,
0614: WireCharge wireCharge) {
0615:
0616: // increment the sequence counter
0617: sequenceHelper.increment();
0618:
0619: // grab the explicit entry for the first accounting line and adjust for wire charge entry
0620: GeneralLedgerPendingEntry explicitEntry = (GeneralLedgerPendingEntry) ObjectUtils
0621: .deepCopy(dvDocument.getGeneralLedgerPendingEntry(0));
0622: explicitEntry
0623: .setTransactionLedgerEntrySequenceNumber(new Integer(
0624: sequenceHelper.getSequenceCounter()));
0625: explicitEntry.setFinancialObjectCode(wireCharge
0626: .getExpenseFinancialObjectCode());
0627: explicitEntry
0628: .setFinancialSubObjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE
0629: .getBlankFinancialSubObjectCode());
0630: explicitEntry.setFinancialObjectTypeCode(SpringContext.getBean(
0631: OptionsService.class).getCurrentYearOptions()
0632: .getFinObjTypeExpenditureexpCd());
0633: explicitEntry.setTransactionDebitCreditCode(GL_DEBIT_CODE);
0634:
0635: if (KFSConstants.COUNTRY_CODE_UNITED_STATES.equals(dvDocument
0636: .getDvWireTransfer().getDisbVchrBankCountryCode())) {
0637: explicitEntry.setTransactionLedgerEntryAmount(wireCharge
0638: .getDomesticChargeAmt());
0639: } else {
0640: explicitEntry.setTransactionLedgerEntryAmount(wireCharge
0641: .getForeignChargeAmt());
0642: }
0643:
0644: explicitEntry
0645: .setTransactionLedgerEntryDescription("Automatic debit for wire transfer fee");
0646:
0647: dvDocument.getGeneralLedgerPendingEntries().add(explicitEntry);
0648:
0649: // create offset
0650: sequenceHelper.increment();
0651:
0652: // handle the offset entry
0653: GeneralLedgerPendingEntry offsetEntry = (GeneralLedgerPendingEntry) ObjectUtils
0654: .deepCopy(explicitEntry);
0655: populateOffsetGeneralLedgerPendingEntry(dvDocument
0656: .getPostingYear(), explicitEntry, sequenceHelper,
0657: offsetEntry);
0658:
0659: dvDocument.getGeneralLedgerPendingEntries().add(offsetEntry);
0660:
0661: return explicitEntry;
0662: }
0663:
0664: /**
0665: * Builds an explicit and offset for the wire charge credit. The account and income object code found in the wire charge table
0666: * is used for the entry.
0667: *
0668: * @param dvDocument submitted disbursement voucher document
0669: * @param sequenceHelper helper class to keep track of GLPE sequence
0670: * @param chargeEntry GLPE charge
0671: * @param wireCharge wireCharge object from current fiscal year
0672: *
0673: */
0674: private void processWireChargeCreditEntries(
0675: DisbursementVoucherDocument dvDocument,
0676: GeneralLedgerPendingEntrySequenceHelper sequenceHelper,
0677: WireCharge wireCharge, GeneralLedgerPendingEntry chargeEntry) {
0678:
0679: // increment the sequence counter
0680: sequenceHelper.increment();
0681:
0682: // copy the charge entry and adjust for credit
0683: GeneralLedgerPendingEntry explicitEntry = (GeneralLedgerPendingEntry) ObjectUtils
0684: .deepCopy(chargeEntry);
0685: explicitEntry
0686: .setTransactionLedgerEntrySequenceNumber(new Integer(
0687: sequenceHelper.getSequenceCounter()));
0688: explicitEntry.setChartOfAccountsCode(wireCharge
0689: .getChartOfAccountsCode());
0690: explicitEntry.setAccountNumber(wireCharge.getAccountNumber());
0691: explicitEntry.setFinancialObjectCode(wireCharge
0692: .getIncomeFinancialObjectCode());
0693:
0694: // retrieve object type
0695: ObjectCode objectCode = new ObjectCode();
0696: objectCode.setUniversityFiscalYear(explicitEntry
0697: .getUniversityFiscalYear());
0698: objectCode.setChartOfAccountsCode(wireCharge
0699: .getChartOfAccountsCode());
0700: objectCode.setFinancialObjectCode(wireCharge
0701: .getIncomeFinancialObjectCode());
0702: objectCode = (ObjectCode) SpringContext.getBean(
0703: BusinessObjectService.class).retrieve(objectCode);
0704:
0705: explicitEntry.setFinancialObjectTypeCode(objectCode
0706: .getFinancialObjectTypeCode());
0707: explicitEntry.setTransactionDebitCreditCode(GL_CREDIT_CODE);
0708:
0709: explicitEntry
0710: .setFinancialSubObjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE
0711: .getBlankFinancialSubObjectCode());
0712: explicitEntry
0713: .setSubAccountNumber(GENERAL_LEDGER_PENDING_ENTRY_CODE
0714: .getBlankSubAccountNumber());
0715: explicitEntry.setProjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE
0716: .getBlankProjectCode());
0717:
0718: explicitEntry
0719: .setTransactionLedgerEntryDescription("Automatic credit for wire transfer fee");
0720:
0721: dvDocument.getGeneralLedgerPendingEntries().add(explicitEntry);
0722:
0723: // create offset
0724: sequenceHelper.increment();
0725:
0726: // handle the offset entry
0727: GeneralLedgerPendingEntry offsetEntry = (GeneralLedgerPendingEntry) ObjectUtils
0728: .deepCopy(explicitEntry);
0729: populateOffsetGeneralLedgerPendingEntry(dvDocument
0730: .getPostingYear(), explicitEntry, sequenceHelper,
0731: offsetEntry);
0732:
0733: dvDocument.getGeneralLedgerPendingEntries().add(offsetEntry);
0734: }
0735:
0736: /**
0737: * Validates conditional required fields. Note fields that are always required are validated by the dictionary framework.
0738: *
0739: * @param document submitted disbursement voucher document
0740: */
0741: private void validateDocumentFields(
0742: DisbursementVoucherDocument document) {
0743: ErrorMap errors = GlobalVariables.getErrorMap();
0744:
0745: // validate document required fields, and payee fields, and formatting
0746: SpringContext.getBean(DictionaryValidationService.class)
0747: .validateDocument(document);
0748: errors.addToErrorPath(KFSPropertyConstants.DV_PAYEE_DETAIL);
0749: SpringContext.getBean(DictionaryValidationService.class)
0750: .validateBusinessObject(document.getDvPayeeDetail());
0751: errors
0752: .removeFromErrorPath(KFSPropertyConstants.DV_PAYEE_DETAIL);
0753: if (!errors.isEmpty()) {
0754: return;
0755: }
0756:
0757: /* remit name & address required if special handling is indicated */
0758: if (document.isDisbVchrSpecialHandlingCode()) {
0759: if (StringUtils.isBlank(document.getDvPayeeDetail()
0760: .getDisbVchrRemitPersonName())
0761: || StringUtils.isBlank(document.getDvPayeeDetail()
0762: .getDisbVchrPayeeLine1Addr())) {
0763: errors.putErrorWithoutFullErrorPath(
0764: KFSConstants.GENERAL_SPECHAND_TAB_ERRORS,
0765: KFSKeyConstants.ERROR_DV_SPECIAL_HANDLING);
0766: }
0767: }
0768:
0769: /* if no documentation is selected, must be a note explaining why */
0770: if (NO_DOCUMENTATION_LOCATION.equals(document
0771: .getDisbursementVoucherDocumentationLocationCode())
0772: && hasNoNotes(document)) {
0773: errors
0774: .putError(
0775: KFSPropertyConstants.DISBURSEMENT_VOUCHER_DOCUMENTATION_LOCATION_CODE,
0776: KFSKeyConstants.ERROR_DV_NO_DOCUMENTATION_NOTE_MISSING);
0777: }
0778:
0779: /* if special handling indicated, must be a note exlaining why */
0780: if (document.isDisbVchrSpecialHandlingCode()
0781: && hasNoNotes(document)) {
0782: errors
0783: .putErrorWithoutFullErrorPath(
0784: KFSConstants.GENERAL_PAYMENT_TAB_ERRORS,
0785: KFSKeyConstants.ERROR_DV_SPECIAL_HANDLING_NOTE_MISSING);
0786: }
0787:
0788: /* if exception attached indicated, must be a note exlaining why */
0789: if (document.isExceptionIndicator() && hasNoNotes(document)) {
0790: errors
0791: .putErrorWithoutFullErrorPath(
0792: KFSConstants.GENERAL_PAYMENT_TAB_ERRORS,
0793: KFSKeyConstants.ERROR_DV_EXCEPTION_ATTACHED_NOTE_MISSING);
0794: }
0795:
0796: /* city, state & zip must be given for us */
0797: if (KFSConstants.COUNTRY_CODE_UNITED_STATES.equals(document
0798: .getDvPayeeDetail().getDisbVchrPayeeCountryCode())) {
0799: if (StringUtils.isBlank(document.getDvPayeeDetail()
0800: .getDisbVchrPayeeCityName())) {
0801: errors
0802: .putError(
0803: KFSPropertyConstants.DV_PAYEE_DETAIL
0804: + "."
0805: + KFSPropertyConstants.DISB_VCHR_PAYEE_CITY_NAME,
0806: KFSKeyConstants.ERROR_DV_PAYEE_CITY_NAME);
0807: }
0808: if (StringUtils.isBlank(document.getDvPayeeDetail()
0809: .getDisbVchrPayeeStateCode())) {
0810: errors
0811: .putError(
0812: KFSPropertyConstants.DV_PAYEE_DETAIL
0813: + "."
0814: + KFSPropertyConstants.DISB_VCHR_PAYEE_STATE_CODE,
0815: KFSKeyConstants.ERROR_DV_PAYEE_STATE_CODE);
0816: }
0817: if (StringUtils.isBlank(document.getDvPayeeDetail()
0818: .getDisbVchrPayeeZipCode())) {
0819: errors
0820: .putError(
0821: KFSPropertyConstants.DV_PAYEE_DETAIL
0822: + "."
0823: + KFSPropertyConstants.DISB_VCHR_PAYEE_ZIP_CODE,
0824: KFSKeyConstants.ERROR_DV_PAYEE_ZIP_CODE);
0825: }
0826: }
0827:
0828: /* country required except for employee payees */
0829: if (!document.getDvPayeeDetail().isEmployee()
0830: && StringUtils.isBlank(document.getDvPayeeDetail()
0831: .getDisbVchrPayeeCountryCode())) {
0832: errors
0833: .putError(
0834: KFSPropertyConstants.DV_PAYEE_DETAIL
0835: + "."
0836: + KFSPropertyConstants.DISB_VCHR_PAYEE_COUNTRY_CODE,
0837: KFSKeyConstants.ERROR_REQUIRED,
0838: "Payee Country ");
0839: }
0840: }
0841:
0842: /**
0843: * Return true if disbursement voucher does not have any notes
0844: *
0845: * @param document submitted disbursement voucher document
0846: * @return whether the given document has no notes
0847: */
0848: private static boolean hasNoNotes(
0849: DisbursementVoucherDocument document) {
0850: // TODO: this is not optimal it shouldn't get it directly from DocHeader revisit later
0851: return (document.getDocumentHeader().getBoNotes() == null || document
0852: .getDocumentHeader().getBoNotes().size() == 0);
0853: }
0854:
0855: /**
0856: * Validates wire transfer tab information
0857: *
0858: * @param document submitted disbursement voucher document
0859: */
0860: private void validateWireTransfer(
0861: DisbursementVoucherDocument document) {
0862: ErrorMap errors = GlobalVariables.getErrorMap();
0863:
0864: errors.addToErrorPath(KFSPropertyConstants.DV_WIRE_TRANSFER);
0865: SpringContext.getBean(DictionaryValidationService.class)
0866: .validateBusinessObject(document.getDvWireTransfer());
0867:
0868: if (KFSConstants.COUNTRY_CODE_UNITED_STATES.equals(document
0869: .getDvWireTransfer().getDisbVchrBankCountryCode())
0870: && StringUtils.isBlank(document.getDvWireTransfer()
0871: .getDisbVchrBankRoutingNumber())) {
0872: errors.putError(
0873: KFSPropertyConstants.DISB_VCHR_BANK_ROUTING_NUMBER,
0874: KFSKeyConstants.ERROR_DV_BANK_ROUTING_NUMBER);
0875: }
0876:
0877: if (KFSConstants.COUNTRY_CODE_UNITED_STATES.equals(document
0878: .getDvWireTransfer().getDisbVchrBankCountryCode())
0879: && StringUtils.isBlank(document.getDvWireTransfer()
0880: .getDisbVchrBankStateCode())) {
0881: errors.putError(
0882: KFSPropertyConstants.DISB_VCHR_BANK_STATE_CODE,
0883: KFSKeyConstants.ERROR_REQUIRED, "Bank State");
0884: }
0885:
0886: /* cannot have attachment checked for wire transfer */
0887: if (document.isDisbVchrAttachmentCode()) {
0888: errors
0889: .putErrorWithoutFullErrorPath(
0890: KFSPropertyConstants.DOCUMENT
0891: + "."
0892: + KFSPropertyConstants.DISB_VCHR_ATTACHMENT_CODE,
0893: KFSKeyConstants.ERROR_DV_WIRE_ATTACHMENT);
0894: }
0895:
0896: errors
0897: .removeFromErrorPath(KFSPropertyConstants.DV_WIRE_TRANSFER);
0898: }
0899:
0900: /**
0901: * Validates foreign draft tab information
0902: *
0903: * @param document submitted disbursement voucher document
0904: */
0905: private void validateForeignDraft(
0906: DisbursementVoucherDocument document) {
0907: ErrorMap errors = GlobalVariables.getErrorMap();
0908: errors.addToErrorPath(KFSPropertyConstants.DV_WIRE_TRANSFER);
0909:
0910: /* currency type code required */
0911: if (StringUtils.isBlank(document.getDvWireTransfer()
0912: .getDisbursementVoucherForeignCurrencyTypeCode())) {
0913: GlobalVariables
0914: .getErrorMap()
0915: .putError(
0916: KFSPropertyConstants.DISB_VCHR_FD_CURRENCY_TYPE_CODE,
0917: KFSKeyConstants.ERROR_DV_CURRENCY_TYPE_CODE);
0918: }
0919:
0920: /* currency type name required */
0921: if (StringUtils.isBlank(document.getDvWireTransfer()
0922: .getDisbursementVoucherForeignCurrencyTypeName())) {
0923: GlobalVariables
0924: .getErrorMap()
0925: .putError(
0926: KFSPropertyConstants.DISB_VCHR_FD_CURRENCY_TYPE_NAME,
0927: KFSKeyConstants.ERROR_DV_CURRENCY_TYPE_NAME);
0928: }
0929:
0930: errors
0931: .removeFromErrorPath(KFSPropertyConstants.DV_WIRE_TRANSFER);
0932: }
0933:
0934: /**
0935: * Validates fields for an alien payment.
0936: *
0937: * @param document submitted disbursement voucher document
0938: */
0939: public void validateNonResidentAlienInformation(
0940: DisbursementVoucherDocument document) {
0941: ErrorMap errors = GlobalVariables.getErrorMap();
0942:
0943: errors
0944: .addToErrorPath(KFSPropertyConstants.DV_NON_RESIDENT_ALIEN_TAX);
0945:
0946: /* income class code required */
0947: if (StringUtils.isBlank(document.getDvNonResidentAlienTax()
0948: .getIncomeClassCode())) {
0949: errors.putError(KFSPropertyConstants.INCOME_CLASS_CODE,
0950: KFSKeyConstants.ERROR_REQUIRED,
0951: "Income class code ");
0952: } else {
0953: /* for foreign source or treaty exempt, non reportable, tax percents must be 0 and gross indicator can not be checked */
0954: if (document.getDvNonResidentAlienTax()
0955: .isForeignSourceIncomeCode()
0956: || document.getDvNonResidentAlienTax()
0957: .isIncomeTaxTreatyExemptCode()
0958: || NRA_TAX_INCOME_CLASS_NON_REPORTABLE
0959: .equals(document.getDvNonResidentAlienTax()
0960: .getIncomeClassCode())) {
0961:
0962: if ((document.getDvNonResidentAlienTax()
0963: .getFederalIncomeTaxPercent() != null && !(new KualiDecimal(
0964: 0).equals(document.getDvNonResidentAlienTax()
0965: .getFederalIncomeTaxPercent())))) {
0966: errors
0967: .putError(
0968: KFSPropertyConstants.FEDERAL_INCOME_TAX_PERCENT,
0969: KFSKeyConstants.ERROR_DV_FEDERAL_TAX_NOT_ZERO);
0970: }
0971:
0972: if ((document.getDvNonResidentAlienTax()
0973: .getStateIncomeTaxPercent() != null && !(new KualiDecimal(
0974: 0).equals(document.getDvNonResidentAlienTax()
0975: .getStateIncomeTaxPercent())))) {
0976: errors
0977: .putError(
0978: KFSPropertyConstants.STATE_INCOME_TAX_PERCENT,
0979: KFSKeyConstants.ERROR_DV_STATE_TAX_NOT_ZERO);
0980: }
0981:
0982: if (document.getDvNonResidentAlienTax()
0983: .isIncomeTaxGrossUpCode()) {
0984: errors
0985: .putError(
0986: KFSPropertyConstants.INCOME_TAX_GROSS_UP_CODE,
0987: KFSKeyConstants.ERROR_DV_GROSS_UP_INDICATOR);
0988: }
0989:
0990: if (NRA_TAX_INCOME_CLASS_NON_REPORTABLE.equals(document
0991: .getDvNonResidentAlienTax()
0992: .getIncomeClassCode())
0993: && StringUtils.isNotBlank(document
0994: .getDvNonResidentAlienTax()
0995: .getPostalCountryCode())) {
0996: errors
0997: .putError(
0998: KFSPropertyConstants.POSTAL_COUNTRY_CODE,
0999: KFSKeyConstants.ERROR_DV_POSTAL_COUNTRY_CODE);
1000: }
1001: } else {
1002: if (document.getDvNonResidentAlienTax()
1003: .getFederalIncomeTaxPercent() == null) {
1004: errors
1005: .putError(
1006: KFSPropertyConstants.FEDERAL_INCOME_TAX_PERCENT,
1007: KFSKeyConstants.ERROR_REQUIRED,
1008: "Federal tax percent ");
1009: } else {
1010: // check tax percent is in nra tax pct table for income class code
1011: NonResidentAlienTaxPercent taxPercent = new NonResidentAlienTaxPercent();
1012: taxPercent.setIncomeClassCode(document
1013: .getDvNonResidentAlienTax()
1014: .getIncomeClassCode());
1015: taxPercent
1016: .setIncomeTaxTypeCode(FEDERAL_TAX_TYPE_CODE);
1017: taxPercent.setIncomeTaxPercent(document
1018: .getDvNonResidentAlienTax()
1019: .getFederalIncomeTaxPercent());
1020:
1021: PersistableBusinessObject retrievedPercent = SpringContext
1022: .getBean(BusinessObjectService.class)
1023: .retrieve(taxPercent);
1024: if (retrievedPercent == null) {
1025: errors
1026: .putError(
1027: KFSPropertyConstants.FEDERAL_INCOME_TAX_PERCENT,
1028: KFSKeyConstants.ERROR_DV_INVALID_FED_TAX_PERCENT,
1029: new String[] {
1030: document
1031: .getDvNonResidentAlienTax()
1032: .getFederalIncomeTaxPercent()
1033: .toString(),
1034: document
1035: .getDvNonResidentAlienTax()
1036: .getIncomeClassCode() });
1037: }
1038: }
1039:
1040: if (document.getDvNonResidentAlienTax()
1041: .getStateIncomeTaxPercent() == null) {
1042: errors
1043: .putError(
1044: KFSPropertyConstants.STATE_INCOME_TAX_PERCENT,
1045: KFSKeyConstants.ERROR_REQUIRED,
1046: "State tax percent ");
1047: } else {
1048: NonResidentAlienTaxPercent taxPercent = new NonResidentAlienTaxPercent();
1049: taxPercent.setIncomeClassCode(document
1050: .getDvNonResidentAlienTax()
1051: .getIncomeClassCode());
1052: taxPercent
1053: .setIncomeTaxTypeCode(STATE_TAX_TYPE_CODE);
1054: taxPercent.setIncomeTaxPercent(document
1055: .getDvNonResidentAlienTax()
1056: .getStateIncomeTaxPercent());
1057:
1058: PersistableBusinessObject retrievedPercent = SpringContext
1059: .getBean(BusinessObjectService.class)
1060: .retrieve(taxPercent);
1061: if (retrievedPercent == null) {
1062: errors
1063: .putError(
1064: KFSPropertyConstants.STATE_INCOME_TAX_PERCENT,
1065: KFSKeyConstants.ERROR_DV_INVALID_STATE_TAX_PERCENT,
1066: new String[] {
1067: document
1068: .getDvNonResidentAlienTax()
1069: .getStateIncomeTaxPercent()
1070: .toString(),
1071: document
1072: .getDvNonResidentAlienTax()
1073: .getIncomeClassCode() });
1074: }
1075: }
1076: }
1077: }
1078:
1079: /* country code required, unless income type is nonreportable */
1080: if (StringUtils.isBlank(document.getDvNonResidentAlienTax()
1081: .getPostalCountryCode())
1082: && !NRA_TAX_INCOME_CLASS_NON_REPORTABLE.equals(document
1083: .getDvNonResidentAlienTax()
1084: .getIncomeClassCode())) {
1085: errors.putError(KFSPropertyConstants.POSTAL_COUNTRY_CODE,
1086: KFSKeyConstants.ERROR_REQUIRED, "Country code ");
1087: }
1088:
1089: errors
1090: .removeFromErrorPath(KFSPropertyConstants.DV_NON_RESIDENT_ALIEN_TAX);
1091: }
1092:
1093: /**
1094: * Validates non employee travel information.
1095: *
1096: * @param document submitted disbursement voucher document
1097: */
1098: private void validateNonEmployeeTravel(
1099: DisbursementVoucherDocument document) {
1100: ErrorMap errors = GlobalVariables.getErrorMap();
1101:
1102: errors
1103: .addToErrorPath(KFSPropertyConstants.DV_NON_EMPLOYEE_TRAVEL);
1104: SpringContext.getBean(DictionaryValidationService.class)
1105: .validateBusinessObjectsRecursively(
1106: document.getDvNonEmployeeTravel(), 1);
1107:
1108: /* travel from and to state required if country is us */
1109: if (KFSConstants.COUNTRY_CODE_UNITED_STATES.equals(document
1110: .getDvNonEmployeeTravel().getDvTravelFromCountryCode())
1111: && StringUtils.isBlank(document
1112: .getDvNonEmployeeTravel()
1113: .getDisbVchrTravelFromStateCode())) {
1114: errors
1115: .putError(
1116: KFSPropertyConstants.DISB_VCHR_TRAVEL_FROM_STATE_CODE,
1117: KFSKeyConstants.ERROR_DV_TRAVEL_FROM_STATE);
1118: }
1119: if (KFSConstants.COUNTRY_CODE_UNITED_STATES.equals(document
1120: .getDvNonEmployeeTravel()
1121: .getDisbVchrTravelToCountryCode())
1122: && StringUtils.isBlank(document
1123: .getDvNonEmployeeTravel()
1124: .getDisbVchrTravelToStateCode())) {
1125: errors
1126: .putError(
1127: KFSPropertyConstants.DISB_VCHR_TRAVEL_TO_STATE_CODE,
1128: KFSKeyConstants.ERROR_DV_TRAVEL_TO_STATE);
1129: }
1130:
1131: if (!errors.isEmpty()) {
1132: errors
1133: .removeFromErrorPath(KFSPropertyConstants.DV_NON_EMPLOYEE_TRAVEL);
1134: return;
1135: }
1136:
1137: /* must fill in all required per diem fields if any field is filled in */
1138: boolean perDiemSectionComplete = validatePerDiemSection(
1139: document, errors);
1140:
1141: /* must fill in all required personal vehicle fields if any field is filled in */
1142: boolean personalVehicleSectionComplete = validatePersonalVehicleSection(
1143: document, errors);
1144:
1145: /* must have per diem change message if actual amount is different from calculated amount */
1146: if (perDiemSectionComplete) { // Only validate if per diem section is filled in
1147: if (document.getDvNonEmployeeTravel()
1148: .getDisbVchrPerdiemCalculatedAmt().compareTo(
1149: document.getDvNonEmployeeTravel()
1150: .getDisbVchrPerdiemActualAmount()) != 0
1151: && StringUtils.isBlank(document
1152: .getDvNonEmployeeTravel()
1153: .getDvPerdiemChangeReasonText())) {
1154: errors
1155: .putError(
1156: KFSPropertyConstants.DV_PERDIEM_CHANGE_REASON_TEXT,
1157: KFSKeyConstants.ERROR_DV_PERDIEM_CHANGE_REQUIRED);
1158: }
1159: }
1160:
1161: /* make sure per diem fields have not changed since the per diem amount calculation */
1162: if (perDiemSectionComplete) { // Only validate if per diem section is filled in
1163: KualiDecimal calculatedPerDiem = SpringContext.getBean(
1164: DisbursementVoucherTravelService.class)
1165: .calculatePerDiemAmount(
1166: document.getDvNonEmployeeTravel()
1167: .getDvPerdiemStartDttmStamp(),
1168: document.getDvNonEmployeeTravel()
1169: .getDvPerdiemEndDttmStamp(),
1170: document.getDvNonEmployeeTravel()
1171: .getDisbVchrPerdiemRate());
1172: if (calculatedPerDiem.compareTo(document
1173: .getDvNonEmployeeTravel()
1174: .getDisbVchrPerdiemCalculatedAmt()) != 0) {
1175: errors.putErrorWithoutFullErrorPath(
1176: KFSConstants.GENERAL_NONEMPLOYEE_TAB_ERRORS,
1177: KFSKeyConstants.ERROR_DV_PER_DIEM_CALC_CHANGE);
1178: }
1179: }
1180:
1181: /* total on non-employee travel must equal Check Total */
1182: /* if tax has been take out, need to add back in the tax amount for the check */
1183: KualiDecimal paidAmount = document
1184: .getDisbVchrCheckTotalAmount();
1185: paidAmount = paidAmount.add(SpringContext.getBean(
1186: DisbursementVoucherTaxService.class)
1187: .getNonResidentAlienTaxAmount(document));
1188: // Ignore this rule if the DV has been coded for NRA tax, because amounts will not balance after tax coding.
1189: boolean nraTaxCoded = !"".equalsIgnoreCase(document
1190: .getDvNonResidentAlienTax().getIncomeClassCode())
1191: && !"N".equalsIgnoreCase(document
1192: .getDvNonResidentAlienTax()
1193: .getIncomeClassCode());
1194: if (!nraTaxCoded
1195: && paidAmount.compareTo(document
1196: .getDvNonEmployeeTravel()
1197: .getTotalTravelAmount()) != 0) {
1198: errors.putErrorWithoutFullErrorPath(
1199: KFSConstants.DV_CHECK_TRAVEL_TOTAL_ERROR,
1200: KFSKeyConstants.ERROR_DV_TRAVEL_CHECK_TOTAL);
1201: }
1202:
1203: /* make sure mileage fields have not changed since the mileage amount calculation */
1204: if (personalVehicleSectionComplete) {
1205: KualiDecimal currentCalcAmt = document
1206: .getDvNonEmployeeTravel()
1207: .getDisbVchrMileageCalculatedAmt();
1208: KualiDecimal currentActualAmt = document
1209: .getDvNonEmployeeTravel()
1210: .getDisbVchrPersonalCarAmount();
1211: if (ObjectUtils.isNotNull(currentCalcAmt)
1212: && ObjectUtils.isNotNull(currentActualAmt)) {
1213: KualiDecimal calculatedMileageAmount = SpringContext
1214: .getBean(DisbursementVoucherTravelService.class)
1215: .calculateMileageAmount(
1216: document
1217: .getDvNonEmployeeTravel()
1218: .getDvPersonalCarMileageAmount(),
1219: document.getDvNonEmployeeTravel()
1220: .getDvPerdiemStartDttmStamp());
1221: if (calculatedMileageAmount.compareTo(document
1222: .getDvNonEmployeeTravel()
1223: .getDisbVchrMileageCalculatedAmt()) != 0) {
1224: errors
1225: .putErrorWithoutFullErrorPath(
1226: KFSConstants.GENERAL_NONEMPLOYEE_TAB_ERRORS,
1227: KFSKeyConstants.ERROR_DV_MILEAGE_CALC_CHANGE);
1228: }
1229:
1230: // determine if the rule is flagged off in the parm setting
1231: boolean performTravelMileageLimitInd = getParameterService()
1232: .getIndicatorParameter(
1233: DisbursementVoucherDocument.class,
1234: NONEMPLOYEE_TRAVEL_ACTUAL_MILEAGE_LIMIT_PARM_NM);
1235: if (performTravelMileageLimitInd) {
1236: // if actual amount is greater than calculated amount
1237: if (currentCalcAmt.subtract(currentActualAmt)
1238: .isNegative()) {
1239: errors
1240: .putError(
1241: KFSPropertyConstants.DV_PERSONAL_CAR_AMOUNT,
1242: KFSKeyConstants.ERROR_DV_ACTUAL_MILEAGE_TOO_HIGH);
1243: }
1244: }
1245: }
1246: }
1247:
1248: errors
1249: .removeFromErrorPath(KFSPropertyConstants.DV_NON_EMPLOYEE_TRAVEL);
1250: }
1251:
1252: /**
1253: * This method checks to see if the per diem section of the non employee travel tab contains any values. If this section
1254: * contains any values, the section is validated to ensure that all the required fields for this section are populated.
1255: *
1256: * @param document submitted disbursement voucher document
1257: * @param errors map containing any generated errors
1258: * @return true if per diem section is used by user and that all fields contain values.
1259: */
1260: private boolean validatePerDiemSection(
1261: DisbursementVoucherDocument document, ErrorMap errors) {
1262: boolean perDiemSectionComplete = true;
1263:
1264: // Checks to see if any per diem fields are filled in
1265: boolean perDiemUsed = StringUtils.isNotBlank(document
1266: .getDvNonEmployeeTravel()
1267: .getDisbVchrPerdiemCategoryName())
1268: || ObjectUtils.isNotNull(document
1269: .getDvNonEmployeeTravel()
1270: .getDisbVchrPerdiemRate())
1271: || ObjectUtils.isNotNull(document
1272: .getDvNonEmployeeTravel()
1273: .getDisbVchrPerdiemCalculatedAmt())
1274: || ObjectUtils.isNotNull(document
1275: .getDvNonEmployeeTravel()
1276: .getDisbVchrPerdiemActualAmount());
1277:
1278: // If any per diem fields contain data, validates that all required per diem fields are filled in
1279: if (perDiemUsed) {
1280: if (StringUtils.isBlank(document.getDvNonEmployeeTravel()
1281: .getDisbVchrPerdiemCategoryName())) {
1282: errors
1283: .putError(
1284: KFSPropertyConstants.DISB_VCHR_PERDIEM_CATEGORY_NAME,
1285: KFSKeyConstants.ERROR_DV_PER_DIEM_CATEGORY);
1286: perDiemSectionComplete = false;
1287: }
1288: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1289: .getDisbVchrPerdiemRate())) {
1290: errors.putError(
1291: KFSPropertyConstants.DISB_VCHR_PERDIEM_RATE,
1292: KFSKeyConstants.ERROR_DV_PER_DIEM_RATE);
1293: perDiemSectionComplete = false;
1294: }
1295: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1296: .getDisbVchrPerdiemCalculatedAmt())) {
1297: errors
1298: .putError(
1299: KFSPropertyConstants.DISB_VCHR_PERDIEM_CALCULATED_AMT,
1300: KFSKeyConstants.ERROR_DV_PER_DIEM_CALC_AMT);
1301: perDiemSectionComplete = false;
1302: }
1303: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1304: .getDisbVchrPerdiemActualAmount())) {
1305: errors
1306: .putError(
1307: KFSPropertyConstants.DISB_VCHR_PERDIEM_ACTUAL_AMOUNT,
1308: KFSKeyConstants.ERROR_DV_PER_DIEM_ACTUAL_AMT);
1309: perDiemSectionComplete = false;
1310: }
1311: }
1312: perDiemSectionComplete = perDiemSectionComplete && perDiemUsed;
1313: return perDiemSectionComplete;
1314: }
1315:
1316: /**
1317: * This method checks to see if the per diem section of the non employee travel tab contains any values. If this section
1318: * contains any values, the section is validated to ensure that all the required fields for this section are populated.
1319: *
1320: * @param document submitted disbursement voucher document
1321: * @param errors map containing any generated errors
1322: * @return true if per diem section is used by user and that all fields contain values.
1323: */
1324: private boolean validatePersonalVehicleSection(
1325: DisbursementVoucherDocument document, ErrorMap errors) {
1326: boolean personalVehicleSectionComplete = true;
1327:
1328: // Checks to see if any per diem fields are filled in
1329: boolean personalVehilcleUsed = ObjectUtils
1330: .isNotNull(document.getDvNonEmployeeTravel()
1331: .getDisbVchrAutoFromCityName())
1332: || ObjectUtils.isNotNull(document
1333: .getDvNonEmployeeTravel()
1334: .getDisbVchrAutoFromStateCode())
1335: || ObjectUtils.isNotNull(document
1336: .getDvNonEmployeeTravel()
1337: .getDisbVchrAutoToCityName())
1338: || ObjectUtils.isNotNull(document
1339: .getDvNonEmployeeTravel()
1340: .getDisbVchrAutoToStateCode())
1341: || ObjectUtils.isNotNull(document
1342: .getDvNonEmployeeTravel()
1343: .getDvPersonalCarMileageAmount())
1344: || ObjectUtils.isNotNull(document
1345: .getDvNonEmployeeTravel()
1346: .getDisbVchrMileageCalculatedAmt())
1347: || ObjectUtils.isNotNull(document
1348: .getDvNonEmployeeTravel()
1349: .getDisbVchrPersonalCarAmount());
1350:
1351: // If any per diem fields contain data, validates that all required per diem fields are filled in
1352: if (personalVehilcleUsed) {
1353: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1354: .getDisbVchrAutoFromCityName())) {
1355: errors
1356: .putError(
1357: KFSPropertyConstants.DISB_VCHR_AUTO_FROM_CITY_NAME,
1358: KFSKeyConstants.ERROR_DV_AUTO_FROM_CITY);
1359: personalVehicleSectionComplete = false;
1360: }
1361: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1362: .getDisbVchrAutoToCityName())) {
1363: errors
1364: .putError(
1365: KFSPropertyConstants.DISB_VCHR_AUTO_TO_CITY_NAME,
1366: KFSKeyConstants.ERROR_DV_AUTO_TO_CITY);
1367: personalVehicleSectionComplete = false;
1368: }
1369:
1370: // are state fields required always or only for US travel?
1371: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1372: .getDisbVchrAutoFromStateCode())) {
1373: errors
1374: .putError(
1375: KFSPropertyConstants.DISB_VCHR_AUTO_FROM_STATE_CODE,
1376: KFSKeyConstants.ERROR_DV_AUTO_FROM_STATE);
1377: personalVehicleSectionComplete = false;
1378: }
1379: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1380: .getDisbVchrAutoToStateCode())) {
1381: errors
1382: .putError(
1383: KFSPropertyConstants.DISB_VCHR_AUTO_TO_STATE_CODE,
1384: KFSKeyConstants.ERROR_DV_AUTO_TO_STATE);
1385: personalVehicleSectionComplete = false;
1386: }
1387: // end state field validation
1388:
1389: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1390: .getDvPersonalCarMileageAmount())) {
1391: errors
1392: .putError(
1393: KFSPropertyConstants.DV_PERSONAL_CAR_MILEAGE_AMOUNT,
1394: KFSKeyConstants.ERROR_DV_MILEAGE_AMT);
1395: personalVehicleSectionComplete = false;
1396: }
1397: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1398: .getDisbVchrMileageCalculatedAmt())) {
1399: errors
1400: .putError(
1401: KFSPropertyConstants.DISB_VCHR_MILEAGE_CALCULATED_AMT,
1402: KFSKeyConstants.ERROR_DV_MILEAGE_CALC_AMT);
1403: personalVehicleSectionComplete = false;
1404: }
1405: if (ObjectUtils.isNull(document.getDvNonEmployeeTravel()
1406: .getDisbVchrPersonalCarAmount())) {
1407: errors
1408: .putError(
1409: KFSPropertyConstants.DISB_VCHR_PERSONAL_CAR_AMOUNT,
1410: KFSKeyConstants.ERROR_DV_MILEAGE_ACTUAL_AMT);
1411: personalVehicleSectionComplete = false;
1412: }
1413: }
1414: personalVehicleSectionComplete = personalVehicleSectionComplete
1415: && personalVehilcleUsed;
1416: return personalVehicleSectionComplete;
1417: }
1418:
1419: /**
1420: * Validates pre paid travel information.
1421: *
1422: * @param document submitted disbursement voucher document
1423: */
1424: private void validatePrePaidTravel(
1425: DisbursementVoucherDocument document) {
1426: ErrorMap errors = GlobalVariables.getErrorMap();
1427:
1428: errors
1429: .addToErrorPath(KFSPropertyConstants.DV_PRE_CONFERENCE_DETAIL);
1430: SpringContext.getBean(DictionaryValidationService.class)
1431: .validateBusinessObjectsRecursively(
1432: document.getDvPreConferenceDetail(), 1);
1433: if (!errors.isEmpty()) {
1434: errors
1435: .removeFromErrorPath(KFSPropertyConstants.DV_PRE_CONFERENCE_DETAIL);
1436: return;
1437: }
1438:
1439: /* check conference end date is not before conference start date */
1440: if (document.getDvPreConferenceDetail()
1441: .getDisbVchrConferenceEndDate().compareTo(
1442: document.getDvPreConferenceDetail()
1443: .getDisbVchrConferenceStartDate()) < 0) {
1444: errors.putError(
1445: KFSPropertyConstants.DISB_VCHR_CONFERENCE_END_DATE,
1446: KFSKeyConstants.ERROR_DV_CONF_END_DATE);
1447: }
1448:
1449: /* total on prepaid travel must equal Check Total */
1450: /* if tax has been take out, need to add back in the tax amount for the check */
1451: KualiDecimal paidAmount = document
1452: .getDisbVchrCheckTotalAmount();
1453: paidAmount = paidAmount.add(SpringContext.getBean(
1454: DisbursementVoucherTaxService.class)
1455: .getNonResidentAlienTaxAmount(document));
1456: if (paidAmount.compareTo(document.getDvPreConferenceDetail()
1457: .getDisbVchrConferenceTotalAmt()) != 0) {
1458: errors.putErrorWithoutFullErrorPath(
1459: KFSConstants.GENERAL_PREPAID_TAB_ERRORS,
1460: KFSKeyConstants.ERROR_DV_PREPAID_CHECK_TOTAL);
1461: }
1462:
1463: errors
1464: .removeFromErrorPath(KFSPropertyConstants.DV_PRE_CONFERENCE_DETAIL);
1465: }
1466:
1467: /**
1468: * Validates the selected documentation location field.
1469: *
1470: * @param document submitted disbursement voucher document
1471: */
1472: private void validateDocumentationLocation(
1473: DisbursementVoucherDocument document) {
1474: String documentationLocationCode = document
1475: .getDisbursementVoucherDocumentationLocationCode();
1476:
1477: if (ObjectUtils.isNotNull(document.getDvPayeeDetail()
1478: .getDisbVchrPaymentReasonCode())) {
1479: // payment reason restrictions
1480: getParameterService()
1481: .getParameterEvaluator(
1482: document.getClass(),
1483: DisbursementVoucherRuleConstants.VALID_DOC_LOC_BY_PAYMENT_REASON_PARM,
1484: DisbursementVoucherRuleConstants.INVALID_DOC_LOC_BY_PAYMENT_REASON_PARM,
1485: document.getDvPayeeDetail()
1486: .getDisbVchrPaymentReasonCode(),
1487: documentationLocationCode)
1488: .evaluateAndAddError(
1489: document.getClass(),
1490: KFSPropertyConstants.DISBURSEMENT_VOUCHER_DOCUMENTATION_LOCATION_CODE);
1491: }
1492:
1493: // alien indicator restrictions
1494: if (document.getDvPayeeDetail().isDisbVchrAlienPaymentCode()) {
1495: getParameterService()
1496: .getParameterEvaluator(
1497: DisbursementVoucherDocument.class,
1498: ALIEN_INDICATOR_CHECKED_PARM_NM,
1499: documentationLocationCode)
1500: .evaluateAndAddError(
1501: document.getClass(),
1502: KFSPropertyConstants.DISBURSEMENT_VOUCHER_DOCUMENTATION_LOCATION_CODE);
1503: }
1504:
1505: // initiator campus code restrictions
1506: getParameterService()
1507: .getParameterEvaluator(
1508: DisbursementVoucherDocument.class,
1509: DisbursementVoucherRuleConstants.VALID_DOC_LOC_BY_CAMPUS_PARM,
1510: DisbursementVoucherRuleConstants.INVALID_DOC_LOC_BY_CAMPUS_PARM,
1511: ((ChartUser) getInitiator(document)
1512: .getModuleUser(ChartUser.MODULE_ID))
1513: .getOrganization()
1514: .getOrganizationPhysicalCampusCode(),
1515: documentationLocationCode)
1516: .evaluateAndAddError(
1517: document.getClass(),
1518: KFSPropertyConstants.DISBURSEMENT_VOUCHER_DOCUMENTATION_LOCATION_CODE);
1519: }
1520:
1521: /**
1522: * Validates the payment reason is valid with the other document attributes.
1523: *
1524: * @param document submitted disbursement voucher document
1525: */
1526: public void validatePaymentReason(
1527: DisbursementVoucherDocument document) {
1528: ErrorMap errors = GlobalVariables.getErrorMap();
1529: String paymentReasonCode = document.getDvPayeeDetail()
1530: .getDisbVchrPaymentReasonCode();
1531:
1532: // restrictions on payment reason when alien indicator is checked
1533: if (document.getDvPayeeDetail().isDisbVchrAlienPaymentCode()) {
1534: ParameterEvaluator alienPaymentReasonsEvaluator = getParameterService()
1535: .getParameterEvaluator(
1536: DisbursementVoucherDocument.class,
1537: ALIEN_PAYMENT_REASONS_PARM_NM,
1538: paymentReasonCode);
1539: alienPaymentReasonsEvaluator.evaluateAndAddError(document
1540: .getClass(), DV_PAYMENT_REASON_PROPERTY_PATH);
1541: }
1542:
1543: // check revolving fund restrictions
1544: // retrieve revolving fund payment reasons
1545: ParameterEvaluator revolvingFundPaymentReasonCodeEvaluator = getParameterService()
1546: .getParameterEvaluator(
1547: DisbursementVoucherDocument.class,
1548: REVOLVING_FUND_PAY_REASONS_PARM_NM,
1549: paymentReasonCode);
1550: if (revolvingFundPaymentReasonCodeEvaluator
1551: .evaluationSucceeds()
1552: && !document.getDvPayeeDetail()
1553: .isDvPayeeRevolvingFundCode()
1554: && !document.getDvPayeeDetail().isVendor()) {
1555: errors.putError(DV_PAYMENT_REASON_PROPERTY_PATH,
1556: KFSKeyConstants.ERROR_DV_REVOLVING_PAYMENT_REASON,
1557: paymentReasonCode);
1558: }
1559:
1560: // if payment reason is moving, payee must be an employee or have payee ownership type I (individual)
1561: ParameterEvaluator movingPaymentReasonCodeEvaluator = getParameterService()
1562: .getParameterEvaluator(
1563: DisbursementVoucherDocument.class,
1564: MOVING_PAY_REASONS_PARM_NM, paymentReasonCode);
1565: if (movingPaymentReasonCodeEvaluator.evaluationSucceeds()) {
1566: if (!document.getDvPayeeDetail().isEmployee()) {
1567: boolean invalidMovingPayee = false;
1568: if (document.getDvPayeeDetail().isPayee()) {
1569: Payee payee = retrievePayee(document
1570: .getDvPayeeDetail()
1571: .getDisbVchrPayeeIdNumber());
1572: // payee must be an individual
1573: if (!OWNERSHIP_TYPE_INDIVIDUAL.equals(payee
1574: .getPayeeOwnershipTypCd())) {
1575: invalidMovingPayee = true;
1576: }
1577: } else {
1578: // vendors cannot be paid for moving
1579: invalidMovingPayee = true;
1580: }
1581:
1582: if (invalidMovingPayee) {
1583: errors
1584: .putError(
1585: DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
1586: KFSKeyConstants.ERROR_DV_MOVING_PAYMENT_PAYEE);
1587: }
1588: }
1589: }
1590:
1591: // for research payments over a certain limit the payee must be a vendor
1592: ParameterEvaluator researchPaymentReasonCodeEvaluator = getParameterService()
1593: .getParameterEvaluator(
1594: DisbursementVoucherDocument.class,
1595: RESEARCH_PAY_REASONS_PARM_NM, paymentReasonCode);
1596: if (researchPaymentReasonCodeEvaluator.evaluationSucceeds()) {
1597: // check rule is active
1598: if (getParameterService().parameterExists(
1599: DisbursementVoucherDocument.class,
1600: RESEARCH_CHECK_LIMIT_AMOUNT_PARM_NM)
1601: && StringUtils
1602: .isNotBlank(getParameterService()
1603: .getParameterValue(
1604: DisbursementVoucherDocument.class,
1605: RESEARCH_CHECK_LIMIT_AMOUNT_PARM_NM))) {
1606: String researchPayLimit = getParameterService()
1607: .getParameterValue(
1608: DisbursementVoucherDocument.class,
1609: RESEARCH_CHECK_LIMIT_AMOUNT_PARM_NM);
1610: KualiDecimal payLimit = new KualiDecimal(
1611: researchPayLimit);
1612: if (document.getDisbVchrCheckTotalAmount()
1613: .isGreaterEqual(payLimit)
1614: && !document.getDvPayeeDetail().isVendor()) {
1615: errors
1616: .putError(
1617: DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
1618: KFSKeyConstants.ERROR_DV_RESEARCH_PAYMENT_PAYEE,
1619: payLimit.toString());
1620: }
1621: }
1622: }
1623: }
1624:
1625: /**
1626: * Validates that the payee is not the initiator.
1627: *
1628: * @param document submitted disbursement voucher document
1629: */
1630: public void validatePayeeInitiatorID(
1631: DisbursementVoucherDocument document) {
1632: DisbursementVoucherPayeeDetail payeeDetail = document
1633: .getDvPayeeDetail();
1634:
1635: String uuid = "";
1636: if (payeeDetail.isPayee()
1637: && StringUtils.isNotBlank(payeeDetail
1638: .getDisbVchrPayeeIdNumber())) {
1639: Payee dvPayee = retrievePayee(payeeDetail
1640: .getDisbVchrPayeeIdNumber());
1641: // if the payee tax type is SSN, then check the tax number
1642: if (dvPayee != null
1643: && TAX_TYPE_SSN.equals(dvPayee
1644: .getTaxpayerTypeCode())) {
1645: // check ssn against employee table
1646: UniversalUser user = new UniversalUser();
1647: user.setPersonTaxIdentifier(dvPayee.getTaxIdNumber());
1648: user = (UniversalUser) SpringContext.getBean(
1649: BusinessObjectService.class).retrieve(user);
1650: if (user != null) {
1651: uuid = user.getPersonUniversalIdentifier();
1652: }
1653: }
1654: } else if (payeeDetail.isEmployee()) {
1655: // payee id number is uuid
1656: uuid = payeeDetail.getDisbVchrPayeeIdNumber();
1657: }
1658:
1659: // if a uuid was found, check it against the initiator uuid
1660: if (StringUtils.isNotBlank(uuid)) {
1661: UniversalUser initUser = getInitiator(document);
1662: if (uuid.equals(initUser.getPersonUniversalIdentifier())) {
1663: GlobalVariables.getErrorMap().putError(
1664: DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
1665: KFSKeyConstants.ERROR_PAYEE_INITIATOR);
1666: }
1667: }
1668: }
1669:
1670: /**
1671: * Validate attributes of the payee for the document.
1672: *
1673: * @param document submitted disbursement voucher document
1674: */
1675: public void validatePayeeInformation(
1676: DisbursementVoucherDocument document) {
1677: DisbursementVoucherPayeeDetail payeeDetail = document
1678: .getDvPayeeDetail();
1679:
1680: if (StringUtils.isBlank(payeeDetail.getDisbVchrPayeeIdNumber())) {
1681: return;
1682: }
1683:
1684: ErrorMap errors = GlobalVariables.getErrorMap();
1685:
1686: /* Retrieve Payee */
1687: Payee dvPayee = retrievePayee(payeeDetail
1688: .getDisbVchrPayeeIdNumber());
1689: if (dvPayee == null) {
1690: errors.putError(DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
1691: KFSKeyConstants.ERROR_EXISTENCE, "Payee ID ");
1692: return;
1693: }
1694:
1695: /* DV Payee must be active */
1696: if (!dvPayee.isPayeeActiveCode()) {
1697: errors.putError(DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
1698: KFSKeyConstants.ERROR_INACTIVE, "Payee ID ");
1699: return;
1700: }
1701:
1702: /* check payment reason is allowed for payee type */
1703: ParameterEvaluator paymentReasonsByTypeEvaluator = getParameterService()
1704: .getParameterEvaluator(
1705: DisbursementVoucherDocument.class,
1706: DisbursementVoucherRuleConstants.VALID_PAYMENT_REASONS_BY_PAYEE_TYPE_PARM,
1707: DisbursementVoucherRuleConstants.INVALID_PAYMENT_REASONS_BY_PAYEE_TYPE_PARM,
1708: DisbursementVoucherRuleConstants.DV_PAYEE_TYPE_PAYEE,
1709: document.getDvPayeeDetail()
1710: .getDisbVchrPaymentReasonCode());
1711: paymentReasonsByTypeEvaluator.evaluateAndAddError(document
1712: .getClass(), DV_PAYMENT_REASON_PROPERTY_PATH);
1713:
1714: /* for payees with tax type ssn, check employee restrictions */
1715: if (TAX_TYPE_SSN.equals(dvPayee.getTaxpayerTypeCode())) {
1716: if (isActiveEmployeeSSN(dvPayee.getTaxIdNumber())) {
1717: // determine if the rule is flagged off in the parm setting
1718: boolean performPrepaidEmployeeInd = getParameterService()
1719: .getIndicatorParameter(
1720: DisbursementVoucherDocument.class,
1721: PERFORM_PREPAID_EMPL_PARM_NM);
1722:
1723: if (performPrepaidEmployeeInd) {
1724: /* active payee employees cannot be paid for prepaid travel */
1725: ParameterEvaluator travelPrepaidPaymentReasonCodeEvaluator = getParameterService()
1726: .getParameterEvaluator(
1727: DisbursementVoucherDocument.class,
1728: PREPAID_TRAVEL_PAY_REASONS_PARM_NM,
1729: payeeDetail
1730: .getDisbVchrPaymentReasonCode());
1731: if (travelPrepaidPaymentReasonCodeEvaluator
1732: .evaluationSucceeds()) {
1733: errors
1734: .putError(
1735: DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
1736: KFSKeyConstants.ERROR_ACTIVE_EMPLOYEE_PREPAID_TRAVEL);
1737: }
1738:
1739: }
1740: } else if (isEmployeeSSN(dvPayee.getTaxIdNumber())) {
1741: // check parm setting for paid outside payroll check
1742: boolean performPaidOutsidePayrollInd = getParameterService()
1743: .getIndicatorParameter(
1744: DisbursementVoucherDocument.class,
1745: DisbursementVoucherRuleConstants.CHECK_EMPLOYEE_PAID_OUTSIDE_PAYROLL_PARM_NM);
1746:
1747: if (performPaidOutsidePayrollInd) {
1748: /* If payee is type payee and employee, payee record must be flagged as paid outside of payroll */
1749: if (!dvPayee.isPayeeEmployeeCode()) {
1750: errors
1751: .putError(
1752: DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
1753: KFSKeyConstants.ERROR_EMPLOYEE_PAID_OUTSIDE_PAYROLL);
1754: }
1755: }
1756: }
1757: }
1758: }
1759:
1760: /**
1761: * Validate attributes of an employee payee for the document.
1762: *
1763: * @param document submitted disbursement voucher document
1764: */
1765: public void validateEmployeeInformation(
1766: DisbursementVoucherDocument document) {
1767: DisbursementVoucherPayeeDetail payeeDetail = document
1768: .getDvPayeeDetail();
1769:
1770: if (StringUtils.isBlank(payeeDetail.getDisbVchrPayeeIdNumber())) {
1771: return;
1772: }
1773:
1774: ErrorMap errors = GlobalVariables.getErrorMap();
1775:
1776: /* check existence of employee */
1777: UniversalUser employee = retrieveEmployee(payeeDetail
1778: .getDisbVchrPayeeIdNumber());
1779: if (employee == null) {
1780: errors.putError(DV_PAYEE_ID_NUMBER_PROPERTY_PATH,
1781: KFSKeyConstants.ERROR_EXISTENCE, "Payee ID ");
1782: errors
1783: .removeFromErrorPath(KFSPropertyConstants.DV_PAYEE_DETAIL);
1784: return;
1785: }
1786:
1787: /* check payment reason is allowed for employee type */
1788: ParameterEvaluator paymentReasonsForPayeeTypeEvaluator = getParameterService()
1789: .getParameterEvaluator(
1790: DisbursementVoucherDocument.class,
1791: DisbursementVoucherRuleConstants.VALID_PAYMENT_REASONS_BY_PAYEE_TYPE_PARM,
1792: DisbursementVoucherRuleConstants.INVALID_PAYMENT_REASONS_BY_PAYEE_TYPE_PARM,
1793: DisbursementVoucherRuleConstants.DV_PAYEE_TYPE_EMPLOYEE,
1794: document.getDvPayeeDetail()
1795: .getDisbVchrPaymentReasonCode());
1796: paymentReasonsForPayeeTypeEvaluator.evaluateAndAddError(
1797: document.getClass(), DV_PAYMENT_REASON_PROPERTY_PATH);
1798: }
1799:
1800: /**
1801: * Validates that there is at least one source accounting line
1802: *
1803: * @param document submitted disbursement voucher document
1804: */
1805: private void validateAccountingLineCounts(
1806: DisbursementVoucherDocument dvDocument) {
1807: ErrorMap errors = GlobalVariables.getErrorMap();
1808:
1809: if (dvDocument.getSourceAccountingLines().size() < 1) {
1810: errors.putErrorWithoutFullErrorPath(
1811: KFSConstants.ACCOUNTING_LINE_ERRORS,
1812: KFSKeyConstants.ERROR_NO_ACCOUNTING_LINES);
1813: }
1814: }
1815:
1816: /**
1817: * Checks the amounts on the document for reconciliation.
1818: *
1819: * @param document submitted disbursement voucher document
1820: */
1821: public void validateDocumentAmounts(
1822: DisbursementVoucherDocument document) {
1823: ErrorMap errors = GlobalVariables.getErrorMap();
1824:
1825: /* check total cannot be negative or zero */
1826: if (!document.getDisbVchrCheckTotalAmount().isPositive()) {
1827: errors.putError(
1828: KFSPropertyConstants.DISB_VCHR_CHECK_TOTAL_AMOUNT,
1829: KFSKeyConstants.ERROR_NEGATIVE_OR_ZERO_CHECK_TOTAL);
1830: }
1831:
1832: /* total accounting lines cannot be negative */
1833: if (KFSConstants.ZERO.compareTo(document.getSourceTotal()) == 1) {
1834: errors.putErrorWithoutFullErrorPath(
1835: KFSConstants.ACCOUNTING_LINE_ERRORS,
1836: KFSKeyConstants.ERROR_NEGATIVE_ACCOUNTING_TOTAL);
1837: }
1838:
1839: /* total of accounting lines must match check total */
1840: if (document.getDisbVchrCheckTotalAmount().compareTo(
1841: document.getSourceTotal()) != 0) {
1842: errors.putErrorWithoutFullErrorPath(
1843: KFSConstants.ACCOUNTING_LINE_ERRORS,
1844: KFSKeyConstants.ERROR_CHECK_ACCOUNTING_TOTAL);
1845: }
1846: }
1847:
1848: /**
1849: * Checks object codes restrictions, including restrictions in parameters table.
1850: *
1851: * @param FinancialDocument submitted accounting document
1852: * @param accountingLine accounting line in accounting document
1853: * @return true if object code exists, is active, and object level and code exist for a provided payment reason
1854: */
1855: public boolean validateObjectCode(
1856: AccountingDocument financialDocument,
1857: AccountingLine accountingLine) {
1858: DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) financialDocument;
1859: ErrorMap errors = GlobalVariables.getErrorMap();
1860:
1861: String errorKey = KFSPropertyConstants.FINANCIAL_OBJECT_CODE;
1862: boolean objectCodeAllowed = true;
1863:
1864: /* object code exist done in super, check we have a valid object */
1865: if (ObjectUtils.isNull(accountingLine.getObjectCode())) {
1866: return false;
1867: }
1868:
1869: /* make sure object code is active */
1870: if (!accountingLine.getObjectCode()
1871: .isFinancialObjectActiveCode()) {
1872: errors.putError(errorKey, KFSKeyConstants.ERROR_INACTIVE,
1873: "object code");
1874: objectCodeAllowed = false;
1875: }
1876:
1877: String documentPaymentReason = dvDocument.getDvPayeeDetail()
1878: .getDisbVchrPaymentReasonCode();
1879: if (StringUtils.isBlank(documentPaymentReason)) {
1880: return objectCodeAllowed;
1881: }
1882:
1883: /* check object level is in permitted list for payment reason */
1884: objectCodeAllowed = objectCodeAllowed
1885: && getParameterService()
1886: .getParameterEvaluator(
1887: financialDocument.getClass(),
1888: DisbursementVoucherRuleConstants.VALID_OBJ_LEVEL_BY_PAYMENT_REASON_PARM,
1889: DisbursementVoucherRuleConstants.INVALID_OBJ_LEVEL_BY_PAYMENT_REASON_PARM,
1890: documentPaymentReason,
1891: accountingLine.getObjectCode()
1892: .getFinancialObjectLevelCode())
1893: .evaluateAndAddError(
1894: SourceAccountingLine.class,
1895: "objectCode.financialObjectLevelCode",
1896: KFSPropertyConstants.FINANCIAL_OBJECT_CODE);
1897:
1898: /* check object code is in permitted list for payment reason */
1899: objectCodeAllowed = objectCodeAllowed
1900: && getParameterService()
1901: .getParameterEvaluator(
1902: financialDocument.getClass(),
1903: DisbursementVoucherRuleConstants.VALID_OBJ_CODE_BY_PAYMENT_REASON_PARM,
1904: DisbursementVoucherRuleConstants.INVALID_OBJ_CODE_BY_PAYMENT_REASON_PARM,
1905: documentPaymentReason,
1906: accountingLine.getFinancialObjectCode())
1907: .evaluateAndAddError(
1908: SourceAccountingLine.class,
1909: KFSPropertyConstants.FINANCIAL_OBJECT_CODE);
1910:
1911: objectCodeAllowed = objectCodeAllowed
1912: && getParameterService()
1913: .getParameterEvaluator(
1914: financialDocument.getClass(),
1915: DisbursementVoucherRuleConstants.VALID_OBJECT_SUB_TYPES_BY_SUB_FUND_GROUP_PARM,
1916: DisbursementVoucherRuleConstants.INVALID_OBJECT_SUB_TYPES_BY_SUB_FUND_GROUP_PARM,
1917: accountingLine.getAccount()
1918: .getSubFundGroupCode(),
1919: accountingLine
1920: .getObjectCode()
1921: .getFinancialObjectSubTypeCode())
1922: .evaluateAndAddError(
1923: SourceAccountingLine.class,
1924: "objectCode.financialObjectSubTypeCode",
1925: KFSPropertyConstants.FINANCIAL_OBJECT_CODE);
1926: return objectCodeAllowed;
1927: }
1928:
1929: /**
1930: * Checks account number restrictions, including restrictions in parameters table.
1931: *
1932: * @param FinancialDocument submitted financial document
1933: * @param accountingLine accounting line in submitted accounting document
1934: * @return true if account exists, falls withing global function code restrictions, and account's sub fund
1935: * is in permitted list for payment reason
1936: */
1937: public boolean validateAccountNumber(
1938: AccountingDocument financialDocument,
1939: AccountingLine accountingLine) {
1940: DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) financialDocument;
1941: ErrorMap errors = GlobalVariables.getErrorMap();
1942:
1943: String errorKey = KFSPropertyConstants.ACCOUNT_NUMBER;
1944: boolean accountNumberAllowed = true;
1945:
1946: /* account exist and object exist done in super, check we have a valid object */
1947: if (ObjectUtils.isNull(accountingLine.getAccount())
1948: || ObjectUtils.isNull(accountingLine.getObjectCode())) {
1949: return false;
1950: }
1951:
1952: /* global function code restrictions */
1953: if (accountNumberAllowed) {
1954: ParameterEvaluator evaluator = getParameterService()
1955: .getParameterEvaluator(
1956: DisbursementVoucherDocument.class,
1957: FUNCTION_CODE_GLOBAL_RESTRICTION_PARM_NM,
1958: accountingLine.getAccount()
1959: .getFinancialHigherEdFunctionCd());
1960: // accountNumberAllowed is true now
1961: accountNumberAllowed = evaluator.evaluateAndAddError(
1962: SourceAccountingLine.class,
1963: "account.financialHigherEdFunctionCd",
1964: KFSPropertyConstants.ACCOUNT_NUMBER);
1965: }
1966:
1967: String documentPaymentReason = dvDocument.getDvPayeeDetail()
1968: .getDisbVchrPaymentReasonCode();
1969: if (StringUtils.isBlank(documentPaymentReason)) {
1970: return accountNumberAllowed;
1971: }
1972:
1973: /* check sub fund is in permitted list for payment reason */
1974: accountNumberAllowed = accountNumberAllowed
1975: && getParameterService()
1976: .getParameterEvaluator(
1977: financialDocument.getClass(),
1978: DisbursementVoucherRuleConstants.VALID_SUB_FUND_GROUPS_BY_PAYMENT_REASON_PARM,
1979: DisbursementVoucherRuleConstants.INVALID_SUB_FUND_GROUPS_BY_PAYMENT_REASON_PARM,
1980: documentPaymentReason,
1981: accountingLine.getAccount()
1982: .getSubFundGroupCode())
1983: .evaluateAndAddError(
1984: SourceAccountingLine.class,
1985: "account.subFundGroupCode",
1986: KFSPropertyConstants.ACCOUNT_NUMBER);
1987:
1988: return accountNumberAllowed;
1989: }
1990:
1991: /**
1992: * Overrides the parent to return true, because Disbursement Voucher documents only use the SourceAccountingLines data
1993: * structures. The list that holds TargetAccountingLines should be empty. This will be checked when the document is "routed" or
1994: * submitted to post - it's called automatically by the parent's processRouteDocument method.
1995: *
1996: * @param financialDocument submitted accounting document
1997: * @return true
1998: *
1999: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#isTargetAccountingLinesRequiredNumberForRoutingMet(org.kuali.core.document.FinancialDocument)
2000: */
2001: @Override
2002: protected boolean isTargetAccountingLinesRequiredNumberForRoutingMet(
2003: AccountingDocument financialDocument) {
2004: return true;
2005: }
2006:
2007: /**
2008: * Overrides the parent to return true, because Disbursement Voucher documents do not have to balance in order to be submitted
2009: * for routing.
2010: *
2011: * @param financialDocument submitted financial document
2012: * @return true
2013: */
2014: @Override
2015: protected boolean isDocumentBalanceValid(
2016: AccountingDocument financialDocument) {
2017: return true;
2018: }
2019:
2020: /**
2021: * Override to check for tax accounting lines. These lines can have negative amounts which the super will reject.
2022: *
2023: * @param document submitted document
2024: * @param accountingLine accounting line document
2025: * @return true if there is no non-resident alien tax provided parent class call to isAmountValid(document, accountingLine) returns true OR
2026: * if there is a non-resident alien tax provided if accounting line sequence number is included in tax line numbers
2027: * @see org.kuali.core.rule.AccountingLineRule#isAmountValid(org.kuali.core.document.FinancialDocument,
2028: * org.kuali.core.bo.AccountingLine)
2029: */
2030: @Override
2031: public boolean isAmountValid(AccountingDocument document,
2032: AccountingLine accountingLine) {
2033: if (((DisbursementVoucherDocument) document)
2034: .getDvNonResidentAlienTax() != null) {
2035: List taxLineNumbers = SpringContext
2036: .getBean(DisbursementVoucherTaxService.class)
2037: .getNRATaxLineNumbers(
2038: ((DisbursementVoucherDocument) document)
2039: .getDvNonResidentAlienTax()
2040: .getFinancialDocumentAccountingLineText());
2041: if (taxLineNumbers.contains(accountingLine
2042: .getSequenceNumber())) {
2043: return true;
2044: }
2045: }
2046: return super .isAmountValid(document, accountingLine);
2047: }
2048:
2049: /**
2050: * Checks if the current user is a member of the dv tax workgroup.
2051: *
2052: * @return true if user is in group
2053: */
2054: private boolean isUserInTaxGroup() {
2055: if (taxGroupName == null) {
2056: taxGroupName = getParameterService().getParameterValue(
2057: DisbursementVoucherDocument.class,
2058: KFSConstants.FinancialApcParms.DV_TAX_WORKGROUP);
2059: }
2060: return GlobalVariables.getUserSession().getUniversalUser()
2061: .isMember(taxGroupName);
2062: }
2063:
2064: /**
2065: * Checks if the current user is a member of the dv travel workgroup.
2066: *
2067: * @return true if user is in group
2068: */
2069: private boolean isUserInTravelGroup() {
2070: if (travelGroupName == null) {
2071: travelGroupName = getParameterService().getParameterValue(
2072: DisbursementVoucherDocument.class,
2073: KFSConstants.FinancialApcParms.DV_TRAVEL_WORKGROUP);
2074: }
2075: return GlobalVariables.getUserSession().getUniversalUser()
2076: .isMember(travelGroupName);
2077: }
2078:
2079: /**
2080: * Checks if the current user is a member of the dv frn workgroup.
2081: *
2082: * @return true if user is in group
2083: */
2084: private boolean isUserInFRNGroup() {
2085: if (frnGroupName == null) {
2086: frnGroupName = getParameterService()
2087: .getParameterValue(
2088: DisbursementVoucherDocument.class,
2089: KFSConstants.FinancialApcParms.DV_FOREIGNDRAFT_WORKGROUP);
2090: }
2091: return GlobalVariables.getUserSession().getUniversalUser()
2092: .isMember(frnGroupName);
2093: }
2094:
2095: /**
2096: * Checks if the current user is a member of the dv wire workgroup.
2097: *
2098: * @return true if user is in group
2099: */
2100: private boolean isUserInWireGroup() {
2101: if (wireTransferGroupName == null) {
2102: wireTransferGroupName = getParameterService()
2103: .getParameterValue(
2104: DisbursementVoucherDocument.class,
2105: KFSConstants.FinancialApcParms.DV_WIRETRANSFER_WORKGROUP);
2106: }
2107: return GlobalVariables.getUserSession().getUniversalUser()
2108: .isMember(wireTransferGroupName);
2109: }
2110:
2111: /**
2112: * This method checks to see whether the user is in the dv admin group or not.
2113: *
2114: * @return true if user is in group, false otherwise
2115: */
2116: private boolean isUserInDvAdminGroup() {
2117: if (adminGroupName == null) {
2118: adminGroupName = getParameterService().getParameterValue(
2119: DisbursementVoucherDocument.class,
2120: KFSConstants.FinancialApcParms.DV_ADMIN_WORKGROUP);
2121: }
2122: return GlobalVariables.getUserSession().getUniversalUser()
2123: .isMember(adminGroupName);
2124: }
2125:
2126: /**
2127: * Returns the initiator of the document as a KualiUser
2128: *
2129: * @param document submitted document
2130: * @return <code>KualiUser</code>
2131: */
2132: private UniversalUser getInitiator(AccountingDocument document) {
2133: UniversalUser initUser = null;
2134: try {
2135:
2136: initUser = SpringContext
2137: .getBean(UniversalUserService.class)
2138: .getUniversalUser(
2139: new AuthenticationUserId(document
2140: .getDocumentHeader()
2141: .getWorkflowDocument()
2142: .getInitiatorNetworkId()));
2143: } catch (UserNotFoundException e) {
2144: throw new RuntimeException("Document Initiator not found "
2145: + e.getMessage());
2146: }
2147:
2148: return initUser;
2149: }
2150:
2151: /**
2152: * Retrieves the wire transfer information for the current fiscal year.
2153: *
2154: * @return <code>WireCharge</code>
2155: */
2156: private WireCharge retrieveWireCharge() {
2157: WireCharge wireCharge = new WireCharge();
2158: wireCharge.setUniversityFiscalYear(SpringContext.getBean(
2159: UniversityDateService.class).getCurrentFiscalYear());
2160:
2161: wireCharge = (WireCharge) SpringContext.getBean(
2162: BusinessObjectService.class).retrieve(wireCharge);
2163: if (wireCharge == null) {
2164: LOG
2165: .error("Wire charge information not found for current fiscal year.");
2166: throw new RuntimeException(
2167: "Wire charge information not found for current fiscal year.");
2168: }
2169:
2170: return wireCharge;
2171: }
2172:
2173: /**
2174: * Retrieves the Company object from the company name.
2175: *
2176: * @param companyCode company code
2177: * @param companyName company name
2178: * @return <code>Payee</code>
2179: */
2180: private TravelCompanyCode retrieveCompany(String companyCode,
2181: String companyName) {
2182: TravelCompanyCode travelCompanyCode = new TravelCompanyCode();
2183: travelCompanyCode.setCode(companyCode);
2184: travelCompanyCode.setName(companyName);
2185: return (TravelCompanyCode) SpringContext.getBean(
2186: BusinessObjectService.class)
2187: .retrieve(travelCompanyCode);
2188: }
2189:
2190: /**
2191: * Retrieves the Payee object from the payee id number.
2192: *
2193: * @param payeeIdNumber payee ID number
2194: * @return <code>Payee</code>
2195: */
2196: private Payee retrievePayee(String payeeIdNumber) {
2197: Payee payee = new Payee();
2198: payee.setPayeeIdNumber(payeeIdNumber);
2199: return (Payee) SpringContext.getBean(
2200: BusinessObjectService.class).retrieve(payee);
2201: }
2202:
2203: /**
2204: * Retrieves the UniversalUser object from the uuid.
2205: *
2206: * @param uuid universal user identifier
2207: * @return <code>UniversalUser</code>
2208: */
2209: private UniversalUser retrieveEmployee(String uuid) {
2210: UniversalUser employee = new UniversalUser();
2211: employee.setPersonUniversalIdentifier(uuid);
2212: return (UniversalUser) SpringContext.getBean(
2213: BusinessObjectService.class).retrieve(employee);
2214: }
2215:
2216: /**
2217: * Retrieves UniversalUser from SSN
2218: * @param ssnNumber social security number
2219: * @return <code>UniversalUser</code>
2220: */
2221: private UniversalUser getUniversalUser(String ssnNumber) {
2222: PersonTaxId personTaxId = new PersonTaxId(ssnNumber);
2223: UniversalUser user = null;
2224: try {
2225: user = SpringContext.getBean(UniversalUserService.class)
2226: .getUniversalUser(personTaxId);
2227: } catch (UserNotFoundException e) {
2228: }
2229: return user;
2230: }
2231:
2232: /**
2233: * Performs a lookup on universal users for the given ssn number.
2234: *
2235: * @param ssnNumber social security number
2236: * @return true if the ssn number is a valid employee ssn
2237: */
2238: private boolean isEmployeeSSN(String ssnNumber) {
2239: boolean isEmployee = false;
2240:
2241: UniversalUser employee = getUniversalUser(ssnNumber);
2242: if (employee != null) {
2243: isEmployee = true;
2244: }
2245:
2246: return isEmployee;
2247: }
2248:
2249: /**
2250: * Performs a lookup on universal users for the given ssn number.
2251: *
2252: * @param ssnNumber social security number
2253: * @return true if the ssn number is a valid employee ssn and the employee is active
2254: */
2255: private boolean isActiveEmployeeSSN(String ssnNumber) {
2256: boolean isActiveEmployee = false;
2257:
2258: UniversalUser employee = getUniversalUser(ssnNumber);
2259:
2260: if (employee != null
2261: && KFSConstants.EMPLOYEE_ACTIVE_STATUS.equals(employee
2262: .getEmployeeStatusCode())) {
2263: isActiveEmployee = true;
2264: }
2265:
2266: return isActiveEmployee;
2267: }
2268:
2269: /**
2270: * checks the status of the document to see if the coversheet is printable
2271: *
2272: * @param document submitted document
2273: * @return true if document is not cancelled, initiated, disapproved, exception, or saved
2274: */
2275:
2276: public boolean isCoverSheetPrintable(Document document) {
2277: KualiWorkflowDocument workflowDocument = document
2278: .getDocumentHeader().getWorkflowDocument();
2279:
2280: return !(workflowDocument.stateIsCanceled()
2281: || workflowDocument.stateIsInitiated()
2282: || workflowDocument.stateIsDisapproved()
2283: || workflowDocument.stateIsException()
2284: || workflowDocument.stateIsDisapproved() || workflowDocument
2285: .stateIsSaved());
2286:
2287: }
2288:
2289: /**
2290: * Rerturns true if accounting line debit
2291: *
2292: * @param financialDocument submitted accounting document
2293: * @param accountingLine accounting line in accounting document
2294: * @return true if document is debit
2295: * @see IsDebitUtils#isDebitConsideringNothingPositiveOnly(FinancialDocumentRuleBase, FinancialDocument, AccountingLine)
2296: * @see org.kuali.core.rule.AccountingLineRule#isDebit(org.kuali.core.document.FinancialDocument,
2297: * org.kuali.core.bo.AccountingLine)
2298: */
2299: public boolean isDebit(AccountingDocument financialDocument,
2300: AccountingLine accountingLine) {
2301: // disallow error corrections
2302: IsDebitUtils.disallowErrorCorrectionDocumentCheck(this ,
2303: financialDocument);
2304: if (financialDocument instanceof DisbursementVoucherDocument) {
2305: // special case - dv NRA tax accounts can be negative, and are debits if positive
2306: DisbursementVoucherDocument dvDoc = (DisbursementVoucherDocument) financialDocument;
2307:
2308: if (dvDoc.getDvNonResidentAlienTax() != null
2309: && dvDoc.getDvNonResidentAlienTax()
2310: .getFinancialDocumentAccountingLineText() != null
2311: && dvDoc.getDvNonResidentAlienTax()
2312: .getFinancialDocumentAccountingLineText()
2313: .contains(
2314: accountingLine.getSequenceNumber()
2315: .toString())) {
2316: return accountingLine.getAmount().isPositive();
2317: }
2318: }
2319:
2320: return IsDebitUtils.isDebitConsideringNothingPositiveOnly(this,
2321: financialDocument, accountingLine);
2322: }
2323:
2324: }
|