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.math.BigDecimal;
019    import java.util.HashMap;
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.ItemFields;
027    import org.kuali.kfs.module.purap.PurapConstants.ItemTypeCodes;
028    import org.kuali.kfs.module.purap.businessobject.PurApItem;
029    import org.kuali.kfs.module.purap.businessobject.PurchasingItemBase;
030    import org.kuali.kfs.sys.KFSKeyConstants;
031    import org.kuali.kfs.sys.KFSPropertyConstants;
032    import org.kuali.kfs.sys.businessobject.UnitOfMeasure;
033    import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
034    import org.kuali.kfs.vnd.businessobject.CommodityCode;
035    import org.kuali.rice.kns.service.BusinessObjectService;
036    import org.kuali.rice.kns.service.DataDictionaryService;
037    import org.kuali.rice.kns.util.GlobalVariables;
038    import org.kuali.rice.kns.util.ObjectUtils;
039    
040    public class PurchasingImportItemValidation extends PurchasingAccountsPayableImportItemValidation {
041    
042        private BusinessObjectService businessObjectService;
043        private DataDictionaryService dataDictionaryService;
044        
045        public boolean validate(AttributedDocumentEvent event) {
046            boolean valid = true;        
047            valid &= super.validate(event);
048            GlobalVariables.getMessageMap().addToErrorPath(PurapConstants.ITEM_TAB_ERROR_PROPERTY);
049            
050            if (getItemForValidation().getItemType().isLineItemIndicator()) {
051                valid &= validateItemDescription(getItemForValidation());
052            }
053            valid &= validateItemUnitPrice(getItemForValidation());
054            valid &= validateUnitOfMeasureCodeExists(getItemForValidation());        
055            valid &= validateCommodityCodes(getItemForValidation(), commodityCodeIsRequired());
056            
057            GlobalVariables.getMessageMap().removeFromErrorPath(PurapConstants.ITEM_TAB_ERROR_PROPERTY);
058            return valid;
059        }
060    
061        /**
062         * Validates whether the commodity code existed on the item, and if existed, whether the
063         * commodity code on the item existed in the database, and if so, whether the commodity 
064         * code is active. Display error if any of these 3 conditions are not met.
065         * 
066         * @param item  The PurApItem containing the commodity code to be validated.
067         * @return boolean false if the validation fails and true otherwise.
068         */
069        protected boolean validateCommodityCodes(PurApItem item, boolean commodityCodeRequired) {
070            boolean valid = true;
071            String identifierString = item.getItemIdentifierString();
072            PurchasingItemBase purItem = (PurchasingItemBase) item;
073            
074            //This validation is only needed if the commodityCodeRequired system parameter is true
075            if (commodityCodeRequired && StringUtils.isBlank(purItem.getPurchasingCommodityCode()) ) {
076                //This is the case where the commodity code is required but the item does not currently contain the commodity code.
077                valid = false;
078                String attributeLabel = dataDictionaryService.
079                                        getDataDictionary().getBusinessObjectEntry(CommodityCode.class.getName()).
080                                        getAttributeDefinition(PurapPropertyConstants.ITEM_COMMODITY_CODE).getLabel();
081                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + identifierString);
082            }
083            else if (StringUtils.isNotBlank(purItem.getPurchasingCommodityCode())) {
084                //Find out whether the commodity code has existed in the database
085                Map<String,String> fieldValues = new HashMap<String, String>();
086                fieldValues.put(PurapPropertyConstants.ITEM_COMMODITY_CODE, purItem.getPurchasingCommodityCode());
087                if (businessObjectService.countMatching(CommodityCode.class, fieldValues) != 1) {
088                    //This is the case where the commodity code on the item does not exist in the database.
089                    valid = false;
090                    GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, PurapKeyConstants.PUR_COMMODITY_CODE_INVALID,  " in " + identifierString);
091                }
092                else {
093                    valid &= validateThatCommodityCodeIsActive(item);
094                }
095            }
096            
097            return valid;
098        }
099    
100        /**
101         * Checks that a description was entered for the item.
102         * 
103         * @param item
104         * @return
105         */
106        public boolean validateItemDescription(PurApItem item) {
107            boolean valid = true;      
108            if (StringUtils.isEmpty(item.getItemDescription())) {
109                valid = false;
110                String attributeLabel = dataDictionaryService.
111                                        getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
112                                        getAttributeDefinition(PurapPropertyConstants.ITEM_DESCRIPTION).getLabel();
113                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_DESCRIPTION, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
114            }
115            return valid;
116        }
117    
118        /**
119         * Validates the unit price for all applicable item types. It validates that the unit price field was
120         * entered on the item, and that the price is in the right range for the item type.
121         * 
122         * @param purDocument the purchasing document to be validated
123         * @return boolean false if there is any validation that fails.
124         */
125        public boolean validateItemUnitPrice(PurApItem item) {
126            boolean valid = true;
127            if (item.getItemType().isLineItemIndicator()) {
128                if (ObjectUtils.isNull(item.getItemUnitPrice())) {
129                    valid = false;
130                    String attributeLabel = dataDictionaryService.
131                                            getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
132                                            getAttributeDefinition(PurapPropertyConstants.ITEM_UNIT_PRICE).getLabel();
133                    GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
134                }
135            }    
136    
137            if (ObjectUtils.isNotNull(item.getItemUnitPrice())) {
138                if ((BigDecimal.ZERO.compareTo(item.getItemUnitPrice()) > 0) && ((!item.getItemTypeCode().equals(ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE)) && (!item.getItemTypeCode().equals(ItemTypeCodes.ITEM_TYPE_TRADE_IN_CODE)))) {
139                    // If the item type is not full order discount or trade in items, don't allow negative unit price.
140                    GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, PurapKeyConstants.ERROR_ITEM_AMOUNT_BELOW_ZERO, ItemFields.UNIT_COST, item.getItemIdentifierString());
141                    valid = false;
142                }
143                else if ((BigDecimal.ZERO.compareTo(item.getItemUnitPrice()) < 0) && ((item.getItemTypeCode().equals(ItemTypeCodes.ITEM_TYPE_ORDER_DISCOUNT_CODE)) || (item.getItemTypeCode().equals(ItemTypeCodes.ITEM_TYPE_TRADE_IN_CODE)))) {
144                    // If the item type is full order discount or trade in items, its unit price must be negative.
145                    GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, PurapKeyConstants.ERROR_ITEM_AMOUNT_NOT_BELOW_ZERO, ItemFields.UNIT_COST, item.getItemIdentifierString());
146                    valid = false;
147                }
148            }
149    
150            return valid;
151        }
152    
153        /**
154         * Validates that if the item type is quantity based, that the unit of measure code is valid.
155         * Looks for the UOM Code in the table. If it is not there, the code is invalid. 
156         * This checking is needed only for imported items, since items added from new line could only 
157         * choose an existing UOM from the drop-down list.
158         * 
159         * @param item the item to be validated
160         * @return boolean false if the item type is quantity based and the unit of measure code is invalid.
161         */
162        protected boolean validateUnitOfMeasureCodeExists(PurApItem item) {
163            boolean valid = true;
164            
165            if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {            
166                String uomCode = item.getItemUnitOfMeasureCode();
167                Map<String,String> fieldValues = new HashMap<String,String>();
168                fieldValues.put(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, uomCode);
169                if (businessObjectService.countMatching(UnitOfMeasure.class, fieldValues) != 1) {
170                    String[] errorParams = { uomCode, "" + item.getItemLineNumber() };
171                    GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERRORS, PurapKeyConstants.ERROR_ITEMPARSER_INVALID_UOM_CODE, errorParams);
172                    valid = false;
173                }  
174            }
175    
176            return valid;
177        }
178    
179        /**
180         * Predicate to do a parameter lookup and tell us whether a commodity code is required.
181         * Override in child classes. 
182         * 
183         * @return      True if a commodity code is required.
184         */
185        protected boolean commodityCodeIsRequired() {
186            return false;
187        }
188    
189        protected boolean validateThatCommodityCodeIsActive(PurApItem item) {
190            if (!((PurchasingItemBase)item).getCommodityCode().isActive()) {
191                //This is the case where the commodity code on the item is not active.
192                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, PurapKeyConstants.PUR_COMMODITY_CODE_INACTIVE, " in " + item.getItemIdentifierString());
193                return false;
194            }
195            return true;
196        }
197    
198        public BusinessObjectService getBusinessObjectService() {
199            return businessObjectService;
200        }
201    
202        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
203            this.businessObjectService = businessObjectService;
204        }
205    
206        public DataDictionaryService getDataDictionaryService() {
207            return dataDictionaryService;
208        }
209    
210        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
211            this.dataDictionaryService = dataDictionaryService;
212        }
213    
214    }