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.purap.document.validation.impl;
017    
018    import java.util.HashMap;
019    import java.util.List;
020    import java.util.Map;
021    
022    import org.apache.commons.lang.StringUtils;
023    import org.kuali.kfs.module.purap.PurapConstants;
024    import org.kuali.kfs.module.purap.PurapKeyConstants;
025    import org.kuali.kfs.module.purap.PurapPropertyConstants;
026    import org.kuali.kfs.module.purap.PurapConstants.PREQDocumentsStrings;
027    import org.kuali.kfs.module.purap.businessobject.LineItemReceivingItem;
028    import org.kuali.kfs.module.purap.businessobject.PurapEnterableItem;
029    import org.kuali.kfs.module.purap.businessobject.ReceivingItem;
030    import org.kuali.kfs.module.purap.document.LineItemReceivingDocument;
031    import org.kuali.kfs.module.purap.document.ReceivingDocument;
032    import org.kuali.kfs.module.purap.document.service.ReceivingService;
033    import org.kuali.kfs.module.purap.document.validation.AddReceivingItemRule;
034    import org.kuali.kfs.module.purap.document.validation.ContinuePurapRule;
035    import org.kuali.kfs.sys.KFSKeyConstants;
036    import org.kuali.kfs.sys.KFSPropertyConstants;
037    import org.kuali.kfs.sys.businessobject.UnitOfMeasure;
038    import org.kuali.kfs.sys.context.SpringContext;
039    import org.kuali.rice.kns.document.Document;
040    import org.kuali.rice.kns.document.TransactionalDocument;
041    import org.kuali.rice.kns.rules.DocumentRuleBase;
042    import org.kuali.rice.kns.service.BusinessObjectService;
043    import org.kuali.rice.kns.service.DataDictionaryService;
044    import org.kuali.rice.kns.service.DictionaryValidationService;
045    import org.kuali.rice.kns.util.GlobalVariables;
046    import org.kuali.rice.kns.util.ObjectUtils;
047    
048    public class LineItemReceivingDocumentRule extends DocumentRuleBase implements ContinuePurapRule, AddReceivingItemRule{
049    
050        @Override
051        protected boolean processCustomRouteDocumentBusinessRules(Document document) {        
052            boolean valid = true;
053            LineItemReceivingDocument lineItemReceivingDocument = (LineItemReceivingDocument)document;
054            
055            GlobalVariables.getMessageMap().clearErrorPath();
056            GlobalVariables.getMessageMap().addToErrorPath(KFSPropertyConstants.DOCUMENT);
057            
058            valid &= super.processCustomRouteDocumentBusinessRules(document);
059            valid &= canCreateLineItemReceivingDocument(lineItemReceivingDocument);
060            valid &= isAtLeastOneItemEntered(lineItemReceivingDocument);
061            valid &= validateItemUnitOfMeasure(lineItemReceivingDocument);
062            
063            //  makes sure all of the lines adhere to the rule that quantityDamaged and 
064            // quantityReturned cannot (each) equal more than the quantityReceived
065            valid &= validateAllReceivingLinesHaveSaneQuantities(lineItemReceivingDocument);
066            
067            return valid;
068        }
069        /**
070         * TODO: move this up
071         * This method...
072         * @param receivingDocument
073         * @return
074         */
075        protected boolean isAtLeastOneItemEntered(ReceivingDocument receivingDocument){
076            for (ReceivingItem item : (List<ReceivingItem>) receivingDocument.getItems()) {
077                if (((PurapEnterableItem)item).isConsideredEntered()) {
078                    //if any item is entered return true
079                    return true;
080                }
081            }
082            //if no items are entered return false
083            GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERROR_PROPERTY, PurapKeyConstants.ERROR_RECEIVING_LINEITEM_REQUIRED);
084            return false;
085            
086        }    
087        
088        public boolean processContinuePurapBusinessRules(TransactionalDocument document) {
089            
090            boolean valid = true;
091            LineItemReceivingDocument lineItemReceivingDocument = (LineItemReceivingDocument)document;
092            
093            GlobalVariables.getMessageMap().clearErrorPath();
094            GlobalVariables.getMessageMap().addToErrorPath(KFSPropertyConstants.DOCUMENT);
095            
096            valid &= hasRequiredFieldsForContinue(lineItemReceivingDocument);
097            //only do this if valid
098            if(valid){
099                valid &= canCreateLineItemReceivingDocument(lineItemReceivingDocument);
100            }
101            
102            return valid;
103        }
104        
105        /**
106         * Make sure the required fields on the init screen are filled in.
107         * 
108         * @param lineItemReceivingDocument
109         * @return
110         */
111        protected boolean hasRequiredFieldsForContinue(LineItemReceivingDocument lineItemReceivingDocument){
112            
113            boolean valid = true;
114            
115            if (ObjectUtils.isNull(lineItemReceivingDocument.getPurchaseOrderIdentifier())) {
116                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, KFSKeyConstants.ERROR_REQUIRED, PREQDocumentsStrings.PURCHASE_ORDER_ID);
117                valid &= false;
118            }
119    
120            if (ObjectUtils.isNull(lineItemReceivingDocument.getShipmentReceivedDate())) {
121                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.SHIPMENT_RECEIVED_DATE, KFSKeyConstants.ERROR_REQUIRED, PurapConstants.LineItemReceivingDocumentStrings.VENDOR_DATE);
122                valid &= false;
123            }
124    
125            return valid;
126        }
127        
128        /**
129         * Determines if it is valid to create a receiving line document.  Only one
130         * receiving line document can be active at any time per purchase order document.
131         * 
132         * @param lineItemReceivingDocument
133         * @return
134         */
135        protected boolean canCreateLineItemReceivingDocument(LineItemReceivingDocument lineItemReceivingDocument){
136            
137            boolean valid = true;
138            
139            if( SpringContext.getBean(ReceivingService.class).canCreateLineItemReceivingDocument(lineItemReceivingDocument.getPurchaseOrderIdentifier(), lineItemReceivingDocument.getDocumentNumber()) == false){
140                valid &= false;
141                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_IDENTIFIER, PurapKeyConstants.ERROR_RECEIVING_LINE_DOCUMENT_ACTIVE_FOR_PO, lineItemReceivingDocument.getDocumentNumber(), lineItemReceivingDocument.getPurchaseOrderIdentifier().toString());
142            }
143             
144            return valid;
145        }
146    
147        /**
148         * Validates that if the item type is quantity based, the unit of measure is required.
149         */
150        protected boolean validateItemUnitOfMeasure(ReceivingDocument receivingDocument) {
151            boolean valid = true;
152            for (ReceivingItem item : (List<ReceivingItem>) receivingDocument.getItems()) {
153                // Validations for quantity based item type
154                if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
155                    String uomCode = item.getItemUnitOfMeasureCode();
156                    if (StringUtils.isEmpty(uomCode)) {
157                        valid = false;
158                        String attributeLabel = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).getAttributeDefinition(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE).getLabel();
159                        GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + item.getItemUnitOfMeasureCode());
160                    }
161                    else {
162                        // Find out whether the unit of measure code has existed in the database
163                        Map<String, String> fieldValues = new HashMap<String, String>();
164                        fieldValues.put(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, item.getItemUnitOfMeasureCode());
165                        if (SpringContext.getBean(BusinessObjectService.class).countMatching(UnitOfMeasure.class, fieldValues) != 1) {
166                            // This is the case where the unit of measure code on the item does not exist in the database.
167                            valid = false;
168                            GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, PurapKeyConstants.PUR_ITEM_UNIT_OF_MEASURE_CODE_INVALID, item.getItemUnitOfMeasureCode());
169                        }
170                    }
171                }
172            }
173            return valid;
174        }
175    
176        /**
177         * @see org.kuali.kfs.module.purap.document.validation.AddReceivingItemRule#processAddReceivingItemRules(org.kuali.kfs.module.purap.document.ReceivingDocument, org.kuali.kfs.module.purap.businessobject.ReceivingItem)
178         */
179        public boolean processAddReceivingItemRules(ReceivingDocument document, LineItemReceivingItem item,String errorPathPrefix) {
180            boolean valid = true;
181            
182            valid &= SpringContext.getBean(DictionaryValidationService.class).isBusinessObjectValid(item,errorPathPrefix);
183            
184            //  test that the amount entered in the QuantityReturned and/or QuantityDamaged fields dont 
185            // either equal more than the QuantityReceived.  In other words, you can only return or mark as 
186            // damaged those that are received.  It doesnt make sense to receive 2 but return 3.  
187            valid &= validateQuantityReturnedNotMoreThanReceived(document, item, errorPathPrefix, new Integer(0));
188            valid &= validateQuantityDamagedNotMoreThanReceived(document, item, errorPathPrefix, new Integer(0));
189            
190            return valid;
191        }
192    
193        protected boolean validateQuantityReturnedNotMoreThanReceived(ReceivingDocument document, LineItemReceivingItem item, String errorPathPrefix, Integer lineNumber) {
194            if (item.getItemReturnedTotalQuantity() != null && item.getItemReceivedTotalQuantity() != null) {
195                if (item.getItemReturnedTotalQuantity().isGreaterThan(item.getItemReceivedTotalQuantity())) {
196                    GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERROR_PROPERTY, PurapKeyConstants.ERROR_RECEIVING_LINE_QTYRETURNED_GT_QTYRECEIVED, (lineNumber.intValue() == 0 ? "Add Line" : lineNumber.toString()));
197                    return false;
198                }
199            }
200            return true;
201        }
202    
203        protected boolean validateQuantityDamagedNotMoreThanReceived(ReceivingDocument document, LineItemReceivingItem item, String errorPathPrefix, Integer lineNumber) {
204            if (item.getItemDamagedTotalQuantity() != null && item.getItemReceivedTotalQuantity() != null) {
205                if (item.getItemDamagedTotalQuantity().isGreaterThan(item.getItemReceivedTotalQuantity())) {
206                    GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERROR_PROPERTY, PurapKeyConstants.ERROR_RECEIVING_LINE_QTYDAMAGED_GT_QTYRECEIVED, (lineNumber.intValue() == 0 ? "Add Line" : lineNumber.toString()));
207                    return false;
208                }
209            }
210            return true;
211        }
212        
213        protected boolean validateAllReceivingLinesHaveSaneQuantities(ReceivingDocument document) {
214            GlobalVariables.getMessageMap().clearErrorPath();
215            boolean valid = true;
216            for (int i = 0; i < document.getItems().size(); i++) {
217                LineItemReceivingItem item = (LineItemReceivingItem) document.getItems().get(i);
218                
219                valid &= validateQuantityReturnedNotMoreThanReceived(document, item, "", new Integer(i + 1));
220                valid &= validateQuantityDamagedNotMoreThanReceived(document, item, "", new Integer(i + 1));
221            }
222            return valid;
223        }
224        
225    }