001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.ar.document.validation.impl;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.kfs.module.ar.ArKeyConstants;
020    import org.kuali.kfs.module.ar.ArPropertyConstants;
021    import org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied;
022    import org.kuali.kfs.module.ar.businessobject.NonInvoiced;
023    import org.kuali.kfs.module.ar.document.PaymentApplicationDocument;
024    import org.kuali.kfs.sys.document.validation.impl.GeneralLedgerPostingDocumentRuleBase;
025    import org.kuali.rice.kew.exception.WorkflowException;
026    import org.kuali.rice.kns.document.Document;
027    import org.kuali.rice.kns.rule.event.ApproveDocumentEvent;
028    import org.kuali.rice.kns.util.GlobalVariables;
029    import org.kuali.rice.kns.util.KNSConstants;
030    import org.kuali.rice.kns.util.KualiDecimal;
031    import org.kuali.rice.kns.util.MessageMap;
032    
033    public class PaymentApplicationDocumentRule extends GeneralLedgerPostingDocumentRuleBase {
034        
035        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PaymentApplicationDocumentRule.class);
036    
037        protected boolean processCustomSaveDocumentBusinessRules(Document document) {
038            boolean isValid = super.processCustomSaveDocumentBusinessRules(document);
039            
040            PaymentApplicationDocument paymentApplicationDocument = (PaymentApplicationDocument)document;
041            
042            // Validate the applied payments
043            int appliedAmountIndex = 0;
044            for(InvoicePaidApplied invoicePaidApplied : paymentApplicationDocument.getInvoicePaidApplieds()) {
045                String fieldName = ArPropertyConstants.PaymentApplicationDocumentFields.AMOUNT_TO_BE_APPLIED_LINE_N;
046                fieldName = StringUtils.replace(fieldName, "{0}", new Integer(appliedAmountIndex).toString());
047                if(!PaymentApplicationDocumentRuleUtil.validateInvoicePaidApplied(invoicePaidApplied, fieldName, paymentApplicationDocument.getTotalFromControl())) {
048                    isValid = false;
049                    LOG.info("One of the invoice paid applieds for the payment application document is not valid.");
050                }
051                appliedAmountIndex++;
052            }
053            
054            // Validate the nonInvoiced payments
055            for(NonInvoiced nonInvoiced : paymentApplicationDocument.getNonInvoiceds()) {
056                try {
057                    if(!PaymentApplicationDocumentRuleUtil.validateNonInvoiced(nonInvoiced, paymentApplicationDocument, paymentApplicationDocument.getTotalFromControl())) {
058                        isValid = false;
059                        LOG.info("One of the non-invoiced lines on the payment application document is not valid.");
060                    }
061                } catch(WorkflowException workflowException) {
062                    isValid = false;
063                    LOG.error("Workflow exception encountered when trying to validate non invoiced line of payment application document.", workflowException);
064                }
065            }
066            
067            // Validate non applied holdings
068            try {
069                if(!PaymentApplicationDocumentRuleUtil.validateNonAppliedHolding(paymentApplicationDocument, paymentApplicationDocument.getTotalFromControl())) {
070                    isValid = false;
071                    LOG.info("The unapplied line on the payment application document is not valid.");
072                }
073            } catch(WorkflowException workflowException) {
074                isValid = false;
075                LOG.error("Workflow exception encountered when trying to validate nonAppliedHolding attribute of payment application document.", workflowException);
076            }
077            
078            // Validate that the cumulative amount applied doesn't exceed the amount owed.
079            try {
080                if(!PaymentApplicationDocumentRuleUtil.validateCumulativeSumOfInvoicePaidAppliedDoesntExceedCashControlTotal(paymentApplicationDocument)) {
081                    isValid = false;
082                    LOG.info("The total amount applied exceeds the total amount owed per the cash control document total amount.");
083                }
084            } catch(WorkflowException workflowException) {
085                isValid = false;
086                LOG.error("Workflow exception encountered when trying to get validate the total applied amount for payment application document.", workflowException);
087            }
088            
089            // Validate that the cumulative amount applied doesn't exceed the amount owed.
090            try {
091                if(!PaymentApplicationDocumentRuleUtil.validateCumulativeSumOfInvoicePaidAppliedsIsGreaterThanOrEqualToZero(paymentApplicationDocument)) {
092                    isValid = false;
093                    LOG.info("The total amount applied is less than zero.");
094                }
095            } catch(WorkflowException workflowException) {
096                isValid = false;
097                LOG.error("Workflow exception encountered when trying to get validate the total applied amount for payment application document.", workflowException);
098            }
099            
100            // Validate that the unapplied total doesn't exceed the cash control total
101            try {
102                if(!PaymentApplicationDocumentRuleUtil.validateUnappliedAmountDoesntExceedCashControlTotal(paymentApplicationDocument)) {
103                    isValid = false;
104                    LOG.info("The total unapplied amount exceeds the total amount owed per the cash control document total amount.");
105                }
106            } catch(WorkflowException workflowException) {
107                isValid = false;
108                LOG.error("Workflow exception encountered when trying to get validate the total applied amount for payment application document.", workflowException);
109            }
110            
111            // Validate that the unapplied total isn't less than zero
112            try {
113                if(!PaymentApplicationDocumentRuleUtil.validateUnappliedAmountIsGreaterThanOrEqualToZero(paymentApplicationDocument)) {
114                    isValid = false;
115                    LOG.info("The total unapplied amount is less than zero.");
116                }
117            } catch(WorkflowException workflowException) {
118                isValid = false;
119                LOG.error("Workflow exception encountered when trying to get validate the total applied amount for payment application document.", workflowException);
120            }
121            
122            // Validate that the non-invoiced total doesn't exceed the cash control total
123            try {
124                if(!PaymentApplicationDocumentRuleUtil.validateNonInvoicedAmountDoesntExceedCashControlTotal(paymentApplicationDocument)) {
125                    isValid = false;
126                    LOG.info("The total non-invoiced amount exceeds the total amount owed per the cash control document total amount.");
127                }
128            } catch(WorkflowException workflowException) {
129                isValid = false;
130                LOG.error("Workflow exception encountered when trying to get validate the total applied amount for payment application document.", workflowException);
131            }
132            
133            // Validate that the non-invoiced total isn't less than zero
134            try {
135                if(!PaymentApplicationDocumentRuleUtil.validateNonInvoicedAmountIsGreaterThanOrEqualToZero(paymentApplicationDocument)) {
136                    isValid = false;
137                    LOG.info("The total unapplied amount is less than zero.");
138                }
139            } catch(WorkflowException workflowException) {
140                isValid = false;
141                LOG.error("Workflow exception encountered when trying to get validate the total applied amount for payment application document.", workflowException);
142            }
143            
144            return isValid;
145        }
146        
147        /**
148         * @see org.kuali.rice.kns.rules.DocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.Document)
149         */
150        protected boolean processCustomRouteDocumentBusinessRules(Document document) {
151            
152            //  if the super failed, don't even bother running these rules
153            boolean isValid = super.processCustomRouteDocumentBusinessRules(document);
154            if (!isValid) return false;
155            
156            MessageMap errorMap = GlobalVariables.getMessageMap();
157            PaymentApplicationDocument paymentApplicationDocument = (PaymentApplicationDocument) document;
158    
159            // this rules is only applicable to CashControl generated Application document
160            // dont let PayApp docs started from CashControl docs through if not all funds are applied
161            if (paymentApplicationDocument.hasCashControlDetail()) {
162                if (!KualiDecimal.ZERO.equals(paymentApplicationDocument.getUnallocatedBalance())) {
163                    isValid &= false;
164                    errorMap.putError(
165                        KNSConstants.GLOBAL_ERRORS,
166                        ArKeyConstants.PaymentApplicationDocumentErrors.FULL_AMOUNT_NOT_APPLIED);
167                    LOG.info("The payment application document was not fully applied.");
168                }
169            }
170    
171        
172            return isValid;
173        }
174        
175        /**
176         * @param doc
177         * @return true if the organization document number on the document header is not blank.
178         */
179        protected boolean containsCashControlDocument(PaymentApplicationDocument doc) {
180            return (StringUtils.isNotBlank(doc.getDocumentHeader().getOrganizationDocumentNumber()));
181        }
182    
183        /**
184         * @see org.kuali.rice.kns.rules.DocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.rule.event.ApproveDocumentEvent)
185         */
186        protected boolean processCustomApproveDocumentBusinessRules(ApproveDocumentEvent approveEvent) {
187            boolean isValid = super.processCustomApproveDocumentBusinessRules(approveEvent);
188            //PaymentApplicationDocument aDocument = (PaymentApplicationDocument)approveEvent.getDocument();
189            return isValid;
190        }
191    
192    }