001/*
002 * Copyright 2009 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.select.document.validation.impl;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.log4j.Logger;
020import org.kuali.ole.docstore.common.client.DocstoreClientLocator;
021import org.kuali.ole.module.purap.PurapConstants;
022import org.kuali.ole.module.purap.PurapConstants.ItemFields;
023import org.kuali.ole.module.purap.PurapConstants.ItemTypeCodes;
024import org.kuali.ole.module.purap.PurapKeyConstants;
025import org.kuali.ole.module.purap.PurapPropertyConstants;
026import org.kuali.ole.module.purap.businessobject.PurApItem;
027import org.kuali.ole.module.purap.businessobject.PurchasingItemBase;
028import org.kuali.ole.module.purap.document.validation.impl.PurchasingAccountsPayableAddItemValidation;
029import org.kuali.ole.select.bo.OLEDonor;
030import org.kuali.ole.select.businessobject.OlePurchaseOrderItem;
031import org.kuali.ole.select.businessobject.OleRequisitionItem;
032import org.kuali.ole.sys.OLEConstants;
033import org.kuali.ole.sys.OLEKeyConstants;
034import org.kuali.ole.sys.OLEPropertyConstants;
035import org.kuali.ole.sys.businessobject.UnitOfMeasure;
036import org.kuali.ole.sys.context.SpringContext;
037import org.kuali.ole.sys.document.validation.event.AttributedDocumentEvent;
038import org.kuali.ole.vnd.businessobject.CommodityCode;
039import org.kuali.rice.core.api.util.type.KualiDecimal;
040import org.kuali.rice.core.api.util.type.KualiInteger;
041import org.kuali.rice.kns.service.DataDictionaryService;
042import org.kuali.rice.krad.service.BusinessObjectService;
043import org.kuali.rice.krad.service.KRADServiceLocator;
044import org.kuali.rice.krad.util.GlobalVariables;
045import org.kuali.rice.krad.util.KRADConstants;
046import org.kuali.rice.krad.util.ObjectUtils;
047
048import java.math.BigDecimal;
049import java.util.HashMap;
050import java.util.Map;
051
052public class OlePurchasingAddItemValidation extends PurchasingAccountsPayableAddItemValidation {
053
054    private BusinessObjectService businessObjectService;
055    private DataDictionaryService dataDictionaryService;
056    private DocstoreClientLocator docstoreClientLocator;
057    private static final Logger LOG = Logger.getLogger(OlePurchasingAddItemValidation.class);
058
059
060    public DocstoreClientLocator getDocstoreClientLocator() {
061        if (docstoreClientLocator == null) {
062            docstoreClientLocator = SpringContext.getBean(DocstoreClientLocator.class);
063        }
064        return docstoreClientLocator;
065    }
066
067    public boolean validate(AttributedDocumentEvent event) {
068        boolean valid = true;
069        GlobalVariables.getMessageMap().addToErrorPath(PurapPropertyConstants.NEW_PURCHASING_ITEM_LINE);
070        //refresh itemType
071        PurApItem refreshedItem = getItemForValidation();
072        refreshedItem.refreshReferenceObject("itemType");
073        super.setItemForValidation(refreshedItem);
074
075        valid &= super.validate(event);
076        //valid &= validateItemUnitPrice(getItemForValidation());
077        //valid &= validateUnitOfMeasure(getItemForValidation());
078        if (getItemForValidation().getItemType().isLineItemIndicator()) {
079            valid &= validateItemDescription(getItemForValidation());
080            valid &= validateItemLocation(getItemForValidation());
081            valid &= validateCopiesAndPartsForEInstance(getItemForValidation());
082            valid &= validateCommodityCodes(getItemForValidation(), commodityCodeIsRequired());
083        }
084        GlobalVariables.getMessageMap().removeFromErrorPath(PurapPropertyConstants.NEW_PURCHASING_ITEM_LINE);
085
086        return valid;
087    }
088
089    /**
090     * Validates whether the commodity code existed on the item, and if existed, whether the
091     * commodity code on the item existed in the database, and if so, whether the commodity
092     * code is active. Display error if any of these 3 conditions are not met.
093     *
094     * @param item The PurApItem containing the commodity code to be validated.
095     * @return boolean false if the validation fails and true otherwise.
096     */
097    protected boolean validateCommodityCodes(PurApItem item, boolean commodityCodeRequired) {
098        boolean valid = true;
099        String identifierString = item.getItemIdentifierString();
100        PurchasingItemBase purItem = (PurchasingItemBase) item;
101
102        //This validation is only needed if the commodityCodeRequired system parameter is true
103        if (commodityCodeRequired && StringUtils.isBlank(purItem.getPurchasingCommodityCode())) {
104            //This is the case where the commodity code is required but the item does not currently contain the commodity code.
105            valid = false;
106            String attributeLabel = dataDictionaryService.
107                    getDataDictionary().getBusinessObjectEntry(CommodityCode.class.getName()).
108                    getAttributeDefinition(PurapPropertyConstants.ITEM_COMMODITY_CODE).getLabel();
109            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, OLEKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + identifierString);
110        } else if (StringUtils.isNotBlank(purItem.getPurchasingCommodityCode())) {
111            //Find out whether the commodity code has existed in the database
112            Map<String, String> fieldValues = new HashMap<String, String>();
113            fieldValues.put(PurapPropertyConstants.ITEM_COMMODITY_CODE, purItem.getPurchasingCommodityCode());
114            if (businessObjectService.countMatching(CommodityCode.class, fieldValues) != 1) {
115                //This is the case where the commodity code on the item does not exist in the database.
116                valid = false;
117                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, PurapKeyConstants.PUR_COMMODITY_CODE_INVALID, " in " + identifierString);
118            } else {
119                valid &= validateThatCommodityCodeIsActive(item);
120            }
121        }
122
123        return valid;
124    }
125
126    /**
127     * Validates the unit price for all applicable item types. It validates that the unit price field was
128     * entered on the item, and that the price is in the right range for the item type.
129     *
130     * @param purDocument the purchasing document to be validated
131     * @return boolean false if there is any validation that fails.
132     */
133    public boolean validateItemUnitPrice(PurApItem item) {
134        boolean valid = true;
135        if (item.getItemType().isLineItemIndicator()) {
136            if (ObjectUtils.isNull(item.getItemUnitPrice())) {
137                valid = false;
138                String attributeLabel = dataDictionaryService.
139                        getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
140                        getAttributeDefinition(PurapPropertyConstants.ITEM_UNIT_PRICE).getLabel();
141                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, OLEKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
142            }
143        }
144
145        if (ObjectUtils.isNotNull(item.getItemUnitPrice())) {
146            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)))) {
147                // If the item type is not full order discount or trade in items, don't allow negative unit price.
148                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, PurapKeyConstants.ERROR_ITEM_AMOUNT_BELOW_ZERO, ItemFields.UNIT_COST, item.getItemIdentifierString());
149                valid = false;
150            } 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)))) {
151                // If the item type is full order discount or trade in items, its unit price must be negative.
152                GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_UNIT_PRICE, PurapKeyConstants.ERROR_ITEM_AMOUNT_NOT_BELOW_ZERO, ItemFields.UNIT_COST, item.getItemIdentifierString());
153                valid = false;
154            }
155        }
156
157        return valid;
158    }
159
160    /**
161     * Validates that if the item type is quantity based, the unit of measure is required.
162     *
163     * @param item the item to be validated
164     * @return boolean false if the item type is quantity based and the unit of measure is empty.
165     */
166    public boolean validateUnitOfMeasure(PurApItem item) {
167        boolean valid = true;
168        PurchasingItemBase purItem = (PurchasingItemBase) item;
169        // Validations for quantity based item type
170        if (purItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
171            String uomCode = purItem.getItemUnitOfMeasureCode();
172            if (StringUtils.isEmpty(uomCode)) {
173                valid = false;
174                String attributeLabel = dataDictionaryService.
175                        getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
176                        getAttributeDefinition(OLEPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE).
177                        getLabel();
178                GlobalVariables.getMessageMap().putError(OLEPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, OLEKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
179            } else {
180                //Find out whether the unit of measure code has existed in the database
181                Map<String, String> fieldValues = new HashMap<String, String>();
182                fieldValues.put(OLEPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, purItem.getItemUnitOfMeasureCode());
183                if (businessObjectService.countMatching(UnitOfMeasure.class, fieldValues) != 1) {
184                    //This is the case where the unit of measure code on the item does not exist in the database.
185                    valid = false;
186                    GlobalVariables.getMessageMap().putError(OLEPropertyConstants.ITEM_UNIT_OF_MEASURE_CODE, PurapKeyConstants.PUR_ITEM_UNIT_OF_MEASURE_CODE_INVALID, " in " + item.getItemIdentifierString());
187                }
188            }
189        }
190
191        return valid;
192    }
193
194    /**
195     * Checks that a description was entered for the item.
196     *
197     * @param item
198     * @return
199     */
200    public boolean validateItemDescription(PurApItem item) {
201        boolean valid = true;
202        if (StringUtils.isEmpty(item.getItemDescription())) {
203            valid = false;
204            String attributeLabel = dataDictionaryService.
205                    getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
206                    getAttributeDefinition(PurapPropertyConstants.ITEM_DESCRIPTION).getLabel();
207            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_DESCRIPTION, OLEKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
208        }
209        return valid;
210    }
211
212
213    /**
214     * Checks that a location was entered for the item for single copy.
215     *
216     * @param item
217     * @return
218     */
219    public boolean validateItemLocation(PurApItem item) {
220        boolean valid = true;
221        if (item instanceof OleRequisitionItem) {
222            OleRequisitionItem oleRequisitionItem = (OleRequisitionItem) item;
223            valid = isValidLocation(oleRequisitionItem.getItemQuantity(), oleRequisitionItem.getItemNoOfParts(), oleRequisitionItem.getItemLocation());
224        } else if (item instanceof OlePurchaseOrderItem) {
225            OlePurchaseOrderItem olePurchaseOrderItem = (OlePurchaseOrderItem) item;
226            valid = isValidLocation(olePurchaseOrderItem.getItemQuantity(), olePurchaseOrderItem.getItemNoOfParts(), olePurchaseOrderItem.getItemLocation());
227        }
228        return valid;
229    }
230
231    private boolean isValidLocation(KualiDecimal noOfCopiesOrdered, KualiInteger noOfPartsOrdered, String itemLocation) {
232        boolean valid = true;
233        if (noOfCopiesOrdered != null && noOfPartsOrdered != null && (noOfCopiesOrdered.equals(new KualiDecimal(1))
234                && noOfPartsOrdered.equals(new KualiInteger(1)))) {
235            if (itemLocation == null || itemLocation.isEmpty()) {
236                GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERROR_PROPERTY,
237                        OLEConstants.ITEM_LOCATION_REQUIRED, new String[]{});
238                valid = false;
239            }
240        }
241        return valid;
242    }
243
244    public boolean validateCopiesAndPartsForEInstance(PurApItem item) {
245        boolean valid = true;
246        if (item instanceof OleRequisitionItem) {
247            OleRequisitionItem oleRequisitionItem = (OleRequisitionItem) item;
248            valid = isValidCopiesAndPartsForEInstance(oleRequisitionItem.getItemQuantity(), oleRequisitionItem.getItemNoOfParts(), oleRequisitionItem.getLinkToOrderOption());
249        } else if (item instanceof OlePurchaseOrderItem) {
250            OlePurchaseOrderItem olePurchaseOrderItem = (OlePurchaseOrderItem) item;
251            valid = isValidCopiesAndPartsForEInstance(olePurchaseOrderItem.getItemQuantity(), olePurchaseOrderItem.getItemNoOfParts(), olePurchaseOrderItem.getLinkToOrderOption());
252        }
253        return valid;
254    }
255
256    private boolean isValidCopiesAndPartsForEInstance(KualiDecimal noOfCopiesOrdered, KualiInteger noOfPartsOrdered, String linkToOrderOption) {
257        boolean valid = true;
258        if (StringUtils.isNotBlank(linkToOrderOption) && (linkToOrderOption.equals(OLEConstants.NB_ELECTRONIC) || linkToOrderOption.equals(OLEConstants.EB_ELECTRONIC)) && noOfCopiesOrdered != null && noOfPartsOrdered != null && (noOfCopiesOrdered.isGreaterThan(new KualiDecimal(1))
259                || noOfPartsOrdered.isGreaterThan(new KualiInteger(1)))) {
260            GlobalVariables.getMessageMap().putError(PurapConstants.ITEM_TAB_ERROR_PROPERTY,
261                    OLEConstants.ITEM_COPIESANDPARTS_SHOULDNOT_BE_GREATERTHAN_ONE_EINSTANCE, new String[]{});
262            valid = false;
263        }
264        return valid;
265    }
266
267    /**
268     * Validates that if the item type is quantity based, the item quantity is required and if the item type is amount based, the
269     * quantity is not allowed.
270     *
271     * @param item the item to be validated
272     * @return boolean false if there's any validation that fails.
273     */
274    public boolean validateItemQuantity(PurApItem item) {
275        boolean valid = true;
276        PurchasingItemBase purItem = (PurchasingItemBase) item;
277        if (purItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && (ObjectUtils.isNull(purItem.getItemQuantity()))) {
278            valid = false;
279            String attributeLabel = dataDictionaryService.
280                    getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
281                    getAttributeDefinition(PurapPropertyConstants.ITEM_QUANTITY).getLabel();
282            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.QUANTITY, OLEKeyConstants.ERROR_REQUIRED, attributeLabel + " in " + item.getItemIdentifierString());
283        } else if (purItem.getItemType().isAmountBasedGeneralLedgerIndicator() && ObjectUtils.isNotNull(purItem.getItemQuantity())) {
284            valid = false;
285            String attributeLabel = dataDictionaryService.
286                    getDataDictionary().getBusinessObjectEntry(item.getClass().getName()).
287                    getAttributeDefinition(PurapPropertyConstants.ITEM_QUANTITY).getLabel();
288            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.QUANTITY, PurapKeyConstants.ERROR_ITEM_QUANTITY_NOT_ALLOWED, attributeLabel + " in " + item.getItemIdentifierString());
289        }
290
291        return valid;
292    }
293
294    /**
295     * Predicate to do a parameter lookup and tell us whether a commodity code is required.
296     * Override in child classes.
297     *
298     * @return True if a commodity code is required.
299     */
300    protected boolean commodityCodeIsRequired() {
301        return false;
302    }
303
304    protected boolean validateThatCommodityCodeIsActive(PurApItem item) {
305        if (!((PurchasingItemBase) item).getCommodityCode().isActive()) {
306            //This is the case where the commodity code on the item is not active.
307            GlobalVariables.getMessageMap().putError(PurapPropertyConstants.ITEM_COMMODITY_CODE, PurapKeyConstants.PUR_COMMODITY_CODE_INACTIVE, " in " + item.getItemIdentifierString());
308            return false;
309        }
310        return true;
311    }
312
313    public BusinessObjectService getBusinessObjectService() {
314        return businessObjectService;
315    }
316
317    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
318        this.businessObjectService = businessObjectService;
319    }
320
321    public DataDictionaryService getDataDictionaryService() {
322        return dataDictionaryService;
323    }
324
325    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
326        this.dataDictionaryService = dataDictionaryService;
327    }
328
329}