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.PurapKeyConstants;
024    import org.kuali.kfs.module.purap.PurapPropertyConstants;
025    import org.kuali.kfs.module.purap.PurapConstants.ItemFields;
026    import org.kuali.kfs.module.purap.PurapConstants.ItemTypeCodes;
027    import org.kuali.kfs.module.purap.businessobject.PurApItem;
028    import org.kuali.kfs.module.purap.businessobject.PurchasingItemBase;
029    import org.kuali.kfs.sys.KFSKeyConstants;
030    import org.kuali.kfs.sys.KFSPropertyConstants;
031    import org.kuali.kfs.sys.businessobject.UnitOfMeasure;
032    import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
033    import org.kuali.kfs.vnd.businessobject.CommodityCode;
034    import org.kuali.rice.kns.service.BusinessObjectService;
035    import org.kuali.rice.kns.service.DataDictionaryService;
036    import org.kuali.rice.kns.util.GlobalVariables;
037    import org.kuali.rice.kns.util.ObjectUtils;
038    
039    public class PurchasingAddItemValidation extends PurchasingAccountsPayableAddItemValidation {
040    
041        private BusinessObjectService businessObjectService;
042        private DataDictionaryService dataDictionaryService;
043        
044        public boolean validate(AttributedDocumentEvent event) {
045            boolean valid=true;        
046            GlobalVariables.getMessageMap().addToErrorPath(PurapPropertyConstants.NEW_PURCHASING_ITEM_LINE);
047            //refresh itemType
048            PurApItem refreshedItem = getItemForValidation();
049            refreshedItem.refreshReferenceObject("itemType");
050            super.setItemForValidation(refreshedItem);
051            
052            valid &= super.validate(event);
053            valid &= validateItemUnitPrice(getItemForValidation());
054            valid &= validateUnitOfMeasure(getItemForValidation());
055            if (getItemForValidation().getItemType().isLineItemIndicator()) {
056                valid &= validateItemDescription(getItemForValidation());
057                valid &= validateItemQuantity(getItemForValidation());
058                valid &= validateCommodityCodes(getItemForValidation(), commodityCodeIsRequired());
059            }
060            GlobalVariables.getMessageMap().removeFromErrorPath(PurapPropertyConstants.NEW_PURCHASING_ITEM_LINE);
061    
062            return valid;
063        }
064    
065        /**
066         * Validates whether the commodity code existed on the item, and if existed, whether the
067         * commodity code on the item existed in the database, and if so, whether the commodity 
068         * code is active. Display error if any of these 3 conditions are not met.
069         * 
070         * @param item  The PurApItem containing the commodity code to be validated.
071         * @return boolean false if the validation fails and true otherwise.
072         */
073        protected boolean validateCommodityCodes(PurApItem item, boolean commodityCodeRequired) {
074            boolean valid = true;
075            String identifierString = item.getItemIdentifierString();
076            PurchasingItemBase purItem = (PurchasingItemBase) item;
077            
078            //This validation is only needed if the commodityCodeRequired system parameter is true
079            if (commodityCodeRequired && StringUtils.isBlank(purItem.getPurchasingCommodityCode()) ) {
080                //This is the case where the commodity code is required but the item does not currently contain the commodity code.
081                valid = false;
082                String attributeLabel = dataDictionaryService.
083                                        getDataDictionary().getBusinessObjectEntry(CommodityCode.class.getName()).
084                                        getAttributeDefinition(PurapPropertyConstants.ITEM_COMMODITY_CODE).getLabel();
085                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + identifierString);
086            }
087            else if (StringUtils.isNotBlank(purItem.getPurchasingCommodityCode())) {
088                //Find out whether the commodity code has existed in the database
089                Map<String,String> fieldValues = new HashMap<String, String>();
090                fieldValues.put(PurapPropertyConstants.ITEM_COMMODITY_CODE, purItem.getPurchasingCommodityCode());
091                if (businessObjectService.countMatching(CommodityCode.class, fieldValues) != 1) {
092                    //This is the case where the commodity code on the item does not exist in the database.
093                    valid = false;
094                    GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, PurapKeyConstants.PUR_COMMODITY_CODE_INVALID,  " in " + identifierString);
095                }
096                else {
097                    valid &= validateThatCommodityCodeIsActive(item);
098                }
099            }
100            
101            return valid;
102        }
103    
104        /**
105         * Validates the unit price for all applicable item types. It validates that the unit price field was
106         * entered on the item, and that the price is in the right range for the item type.
107         * 
108         * @param purDocument the purchasing document to be validated
109         * @return boolean false if there is any validation that fails.
110         */
111        public boolean validateItemUnitPrice(PurApItem item) {
112            boolean valid = true;
113            if (item.getItemType().isLineItemIndicator()) {
114                if (ObjectUtils.isNull(item.getItemUnitPrice())) {
115                    valid = false;
116                    String attributeLabel = dataDictionaryService.
117                                            getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
118                                            getAttributeDefinition(PurapPropertyConstants.ITEM_UNIT_PRICE).getLabel();
119                    GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
120                }
121            }    
122    
123            if (ObjectUtils.isNotNull(item.getItemUnitPrice())) {
124                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)))) {
125                    // If the item type is not full order discount or trade in items, don't allow negative unit price.
126                    GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, PurapKeyConstants.ERROR_ITEM_AMOUNT_BELOW_ZERO, ItemFields.UNIT_COST, item.getItemIdentifierString());
127                    valid = false;
128                }
129                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)))) {
130                    // If the item type is full order discount or trade in items, its unit price must be negative.
131                    GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, PurapKeyConstants.ERROR_ITEM_AMOUNT_NOT_BELOW_ZERO, ItemFields.UNIT_COST, item.getItemIdentifierString());
132                    valid = false;
133                }
134            }
135    
136            return valid;
137        }
138    
139        /**
140         * Validates that if the item type is quantity based, the unit of measure is required.
141         * 
142         * @param item the item to be validated
143         * @return boolean false if the item type is quantity based and the unit of measure is empty.
144         */
145        public boolean validateUnitOfMeasure(PurApItem item) {
146            boolean valid = true;
147            PurchasingItemBase purItem = (PurchasingItemBase) item;
148            // Validations for quantity based item type
149            if (purItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
150                String uomCode = purItem.getItemUnitOfMeasureCode();
151                if (StringUtils.isEmpty(uomCode)) {
152                    valid = false;
153                    String attributeLabel = dataDictionaryService.
154                                            getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
155                                            getAttributeDefinition(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE).
156                                            getLabel();
157                    GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
158                }
159                else {
160                    //Find out whether the unit of measure code has existed in the database
161                    Map<String,String> fieldValues = new HashMap<String, String>();
162                    fieldValues.put(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, purItem.getItemUnitOfMeasureCode());
163                    if (businessObjectService.countMatching(UnitOfMeasure.class, fieldValues) != 1) {
164                        //This is the case where the unit of measure code on the item does not exist in the database.
165                        valid = false;
166                        GlobalVariables.getMessageMap().putError(KFSPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, PurapKeyConstants.PUR_ITEM_UNIT_OF_MEASURE_CODE_INVALID,  " in " + item.getItemIdentifierString());
167                    }
168                }
169            }
170    
171            return valid;
172        }
173    
174        /**
175         * Checks that a description was entered for the item.
176         * 
177         * @param item
178         * @return
179         */
180        public boolean validateItemDescription(PurApItem item) {
181            boolean valid = true;      
182            if (StringUtils.isEmpty(item.getItemDescription())) {
183                valid = false;
184                String attributeLabel = dataDictionaryService.
185                                        getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
186                                        getAttributeDefinition(PurapPropertyConstants.ITEM_DESCRIPTION).getLabel();
187                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_DESCRIPTION, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
188            }
189            return valid;
190        }
191    
192        /**
193         * Validates that if the item type is quantity based, the item quantity is required and if the item type is amount based, the
194         * quantity is not allowed.
195         * 
196         * @param item the item to be validated
197         * @return boolean false if there's any validation that fails.
198         */
199        public boolean validateItemQuantity(PurApItem item) {
200            boolean valid = true;
201            PurchasingItemBase purItem = (PurchasingItemBase) item;
202            if (purItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && (ObjectUtils.isNull(purItem.getItemQuantity()))) {
203                valid = false;
204                String attributeLabel = dataDictionaryService.
205                                        getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
206                                        getAttributeDefinition(PurapPropertyConstants.ITEM_QUANTITY).getLabel();            
207                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.QUANTITY, KFSKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
208            }
209            else if (purItem.getItemType().isAmountBasedGeneralLedgerIndicator() && ObjectUtils.isNotNull(purItem.getItemQuantity())) {
210                valid = false;
211                String attributeLabel = dataDictionaryService.
212                                        getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
213                                        getAttributeDefinition(PurapPropertyConstants.ITEM_QUANTITY).getLabel(); 
214                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.QUANTITY, PurapKeyConstants.ERROR_ITEM_QUANTITY_NOT_ALLOWED, attributeLabel + " in " + item.getItemIdentifierString());
215            }
216    
217            return valid;
218        }
219    
220        /**
221         * Predicate to do a parameter lookup and tell us whether a commodity code is required.
222         * Override in child classes. 
223         * 
224         * @return      True if a commodity code is required.
225         */
226        protected boolean commodityCodeIsRequired() {
227            return false;
228        }
229    
230        protected boolean validateThatCommodityCodeIsActive(PurApItem item) {
231            if (!((PurchasingItemBase)item).getCommodityCode().isActive()) {
232                //This is the case where the commodity code on the item is not active.
233                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, PurapKeyConstants.PUR_COMMODITY_CODE_INACTIVE, " in " + item.getItemIdentifierString());
234                return false;
235            }
236            return true;
237        }
238    
239        public BusinessObjectService getBusinessObjectService() {
240            return businessObjectService;
241        }
242    
243        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
244            this.businessObjectService = businessObjectService;
245        }
246    
247        public DataDictionaryService getDataDictionaryService() {
248            return dataDictionaryService;
249        }
250    
251        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
252            this.dataDictionaryService = dataDictionaryService;
253        }
254    
255    }