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