001/*
002 * Copyright 2008 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 */
016package org.kuali.ole.sys.document.validation.impl;
017
018import org.kuali.ole.sys.OLEConstants;
019import org.kuali.ole.sys.OLEKeyConstants;
020import org.kuali.ole.sys.context.SpringContext;
021import org.kuali.ole.sys.document.AccountingDocument;
022import org.kuali.ole.sys.document.validation.GenericValidation;
023import org.kuali.ole.sys.document.validation.event.AttributedDocumentEvent;
024import org.kuali.ole.sys.document.validation.event.AttributedSaveDocumentEvent;
025import org.kuali.rice.core.api.util.type.KualiDecimal;
026import org.kuali.rice.core.web.format.CurrencyFormatter;
027import org.kuali.rice.kew.api.exception.WorkflowException;
028import org.kuali.rice.krad.service.DocumentService;
029import org.kuali.rice.krad.util.GlobalVariables;
030
031/**
032 * A validation, used on accounting document approval, that accounting line totals are unchanged
033 */
034public class AccountingLineGroupTotalsUnchangedValidation extends GenericValidation {
035    private AccountingDocument accountingDocumentForValidation;
036    
037    /**
038     * Checks that the source and total amounts on the current version of the accounting document
039     * are equal to the persisted source and total totals.
040     * <strong>Expects a document to be sent in as the first parameter</strong>
041     * @see org.kuali.ole.sys.document.validation.GenericValidation#validate(java.lang.Object[])
042     */
043    public boolean validate(AttributedDocumentEvent event) {
044        AccountingDocument persistedDocument = null;
045        
046        if (event instanceof AttributedSaveDocumentEvent && !accountingDocumentForValidation.getDocumentHeader().getWorkflowDocument().isEnroute()) {
047            return true; // only check save document events if the document is enroute
048        }
049
050        persistedDocument = retrievePersistedDocument(accountingDocumentForValidation);
051
052        boolean isUnchanged = true;
053        if (persistedDocument == null) {
054            handleNonExistentDocumentWhenApproving(accountingDocumentForValidation);
055        }
056        else {
057            // retrieve the persisted totals
058            KualiDecimal persistedSourceLineTotal = persistedDocument.getSourceTotal();
059            KualiDecimal persistedTargetLineTotal = persistedDocument.getTargetTotal();
060
061            // retrieve the updated totals
062            KualiDecimal currentSourceLineTotal = accountingDocumentForValidation.getSourceTotal();
063            KualiDecimal currentTargetLineTotal = accountingDocumentForValidation.getTargetTotal();
064
065            // make sure that totals have remained unchanged, if not, recognize that, and
066            // generate appropriate error messages
067            if (currentSourceLineTotal.compareTo(persistedSourceLineTotal) != 0) {
068                isUnchanged = false;
069
070                // build out error message
071                buildTotalChangeErrorMessage(OLEConstants.SOURCE_ACCOUNTING_LINE_ERRORS, persistedSourceLineTotal, currentSourceLineTotal);
072            }
073
074            if (currentTargetLineTotal.compareTo(persistedTargetLineTotal) != 0) {
075                isUnchanged = false;
076
077                // build out error message
078                buildTotalChangeErrorMessage(OLEConstants.TARGET_ACCOUNTING_LINE_ERRORS, persistedTargetLineTotal, currentTargetLineTotal);
079            }
080        }
081
082        return isUnchanged;
083    }
084
085    /**
086     * attempt to retrieve the document from the DB for comparison
087     * 
088     * @param accountingDocument
089     * @return AccountingDocument
090     */
091    protected AccountingDocument retrievePersistedDocument(AccountingDocument accountingDocument) {
092        AccountingDocument persistedDocument = null;
093
094        try {
095            persistedDocument = (AccountingDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(accountingDocument.getDocumentNumber());
096        }
097        catch (WorkflowException we) {
098            handleNonExistentDocumentWhenApproving(accountingDocument);
099        }
100
101        return persistedDocument;
102    }
103    
104    /**
105     * This method builds out the error message for when totals have changed.
106     * 
107     * @param propertyName
108     * @param persistedSourceLineTotal
109     * @param currentSourceLineTotal
110     */
111    protected void buildTotalChangeErrorMessage(String propertyName, KualiDecimal persistedSourceLineTotal, KualiDecimal currentSourceLineTotal) {
112        String persistedTotal = (String) new CurrencyFormatter().format(persistedSourceLineTotal);
113        String currentTotal = (String) new CurrencyFormatter().format(currentSourceLineTotal);
114        GlobalVariables.getMessageMap().putError(propertyName, OLEKeyConstants.ERROR_DOCUMENT_SINGLE_ACCOUNTING_LINE_SECTION_TOTAL_CHANGED, new String[] { persistedTotal, currentTotal });
115    }
116
117    /**
118     * Handles the case when a non existent document is attempted to be retrieve and that if it's in an initiated state, it's ok.
119     * 
120     * @param accountingDocument
121     */
122    protected final void handleNonExistentDocumentWhenApproving(AccountingDocument accountingDocument) {
123        // check to make sure this isn't an initiated document being blanket approved
124        if (!accountingDocument.getDocumentHeader().getWorkflowDocument().isInitiated()) {
125            throw new IllegalStateException("Document " + accountingDocument.getDocumentNumber() + " is not a valid document that currently exists in the system.");
126        }
127    }
128
129    /**
130     * Gets the accountingDocumentForValidation attribute. 
131     * @return Returns the accountingDocumentForValidation.
132     */
133    public AccountingDocument getAccountingDocumentForValidation() {
134        return accountingDocumentForValidation;
135    }
136
137    /**
138     * Sets the accountingDocumentForValidation attribute value.
139     * @param accountingDocumentForValidation The accountingDocumentForValidation to set.
140     */
141    public void setAccountingDocumentForValidation(AccountingDocument accountingDocumentForValidation) {
142        this.accountingDocumentForValidation = accountingDocumentForValidation;
143    }
144}