/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.module.cab.document.validation.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.module.cab.CabKeyConstants;
import org.kuali.kfs.module.cab.CabPropertyConstants;
import org.kuali.kfs.module.cab.businessobject.Pretag;
import org.kuali.kfs.module.cab.businessobject.PretagDetail;
import org.kuali.kfs.module.cab.document.service.PurApInfoService;
import org.kuali.kfs.module.cam.CamsKeyConstants;
import org.kuali.kfs.module.cam.businessobject.Asset;
import org.kuali.kfs.module.purap.businessobject.PurApItem;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.Building;
import org.kuali.kfs.sys.businessobject.Room;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.core.api.util.RiceKeyConstants;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.kns.document.MaintenanceDocument;
import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.bo.PersistableBusinessObject;
import org.kuali.rice.krad.maintenance.MaintenanceDocumentAuthorizer;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.util.GlobalVariables;
/**
* This class represents the business rules for the maintenance of {@link AccountGlobal} business objects
*/
public class PretagRule extends MaintenanceDocumentRuleBase {
protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PretagRule.class);
protected PersistableBusinessObject bo;
protected Pretag newPretag;
public PretagRule() {
super();
}
/**
* This method sets the convenience objects like newPretag and oldPretag, so you have short and easy handles to the new and old
* objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load all
* sub-objects from the DB by their primary keys, if available.
*/
@Override
public void setupConvenienceObjects() {
// setup Pretag convenience objects, make sure all possible detail lines are populated
newPretag = (Pretag) super.getNewBo();
for (PretagDetail dtl : newPretag.getPretagDetails()) {
dtl.refreshNonUpdateableReferences();
}
}
/**
* Does not fail on rules failure
*
* @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
*/
@Override
protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
boolean success = processPretagValidation();
return success & super.processCustomSaveDocumentBusinessRules(document); // always return true on save
}
/**
* @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
*/
@Override
protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
// get the documentAuthorizer for this document
MaintenanceDocumentAuthorizer documentAuthorizer = (MaintenanceDocumentAuthorizer) getDocumentHelperService().getDocumentAuthorizer(document);
WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
boolean success = true;
if (workflowDocument.isInitiated() || workflowDocument.isSaved()){
success &= documentAuthorizer.canCreateOrMaintain((MaintenanceDocument)document, GlobalVariables.getUserSession().getPerson());
if (!success) {
putFieldError(CabPropertyConstants.Pretag.CHART_OF_ACCOUNTS_CODE, CabKeyConstants.CHART_ORG_DISALLOWED_BY_CURRENT_USER);
}
}
return success & super.processCustomRouteDocumentBusinessRules(document);
}
/**
* Validates Pretag and its PretagDetail.
*
* @return boolean false or true
*/
public boolean processPretagValidation() {
boolean success = true;
boolean newDetailLine = false;
setupConvenienceObjects();
success &= checkPurchaseOrderItemExists();
success &= checkAssetRepresentativePrincipalNameExists();
if (newPretag.isActive()) {
success &= checkTotalDetailCount(newPretag, newDetailLine);
success &= isAllCampusBuildingRoomValid(newPretag.getPretagDetails());
}
else {
deactivePretagDetails(newPretag);
}
return success;
}
/**
* validate the asset representative principal name.
*
* @return boolean false or true
*/
protected boolean checkAssetRepresentativePrincipalNameExists() {
boolean valid = true;
if (StringUtils.isNotBlank(newPretag.getPersonUniversal().getPrincipalName())) {
PersonService personService = SpringContext.getBean(org.kuali.rice.kim.api.identity.PersonService.class);
Person person = personService.getPersonByPrincipalName(newPretag.getPersonUniversal().getPrincipalName());
if (person != null) {
newPretag.setPersonUniversal(person);
newPretag.setRepresentativeUniversalIdentifier(person.getPrincipalId());
}
else {
putFieldError(CabPropertyConstants.Pretag.REPRESENTATIVE_ID, CamsKeyConstants.PreTag.ERROR_PRE_TAG_INVALID_REPRESENTATIVE_ID, newPretag.getPersonUniversal().getPrincipalName());
newPretag.setPersonUniversal(null);
newPretag.setRepresentativeUniversalIdentifier(null);
valid &= false;
}
}
return valid;
}
/**
* validate the purchase order item existence in PurAp.
*
* @return boolean false or true
*/
protected boolean checkPurchaseOrderItemExists() {
boolean valid = true;
if (StringUtils.isNotBlank(newPretag.getPurchaseOrderNumber()) && newPretag.getItemLineNumber() != null) {
PurchaseOrderDocument purchaseOrderDoc = getPurApInfoService().getCurrentDocumentForPurchaseOrderIdentifier(Integer.valueOf(newPretag.getPurchaseOrderNumber()));
if (purchaseOrderDoc == null) {
String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(Pretag.class.getName()).getAttributeDefinition(CabPropertyConstants.Pretag.PURCHASE_ORDER_NUMBER).getLabel();
putFieldError(CabPropertyConstants.Pretag.PURCHASE_ORDER_NUMBER, RiceKeyConstants.ERROR_EXISTENCE, label);
valid = false;
}
else if (getItemByLineNumber(purchaseOrderDoc, newPretag.getItemLineNumber()) == null) {
String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(Pretag.class.getName()).getAttributeDefinition(CabPropertyConstants.Pretag.ITEM_LINE_NUMBER).getLabel();
putFieldError(CabPropertyConstants.Pretag.ITEM_LINE_NUMBER, RiceKeyConstants.ERROR_EXISTENCE, label);
valid = false;
}
}
return valid;
}
/**
* Get PurchaseOrderItem by given item line number
*
* @param items
* @param lineNumber
* @return
*/
protected PurApItem getItemByLineNumber(PurchaseOrderDocument purchaseOrderDocument, int lineNumber) {
List items = purchaseOrderDocument.getItems();
for (Iterator iter = items.iterator(); iter.hasNext();) {
PurApItem item = (PurApItem) iter.next();
if (item.getItemLineNumber() != null && item.getItemLineNumber().intValue() == lineNumber) {
return item;
}
}
return null;
}
/**
* This method loops through the list of {@link pretagDetail}s and passes them off to isAllCampusBuildingRoomValid for further
* rule analysis
*
* @param document
* @param details
* @return true if the collection of {@link pretagDetail}s passes the sub-rules
*/
public boolean isAllCampusBuildingRoomValid(List<PretagDetail> details) {
boolean success = true;
// check if there are any pretag details
if (details.size() != 0) {
// check each CampusBuildingRoom
int index = 0;
for (PretagDetail dtl : details) {
String errorPath = MAINTAINABLE_ERROR_PREFIX + "pretagDetails[" + index + "]";
GlobalVariables.getMessageMap().addToErrorPath(errorPath);
success &= isCampusBuildingRoomValid(dtl);
GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
index++;
}
}
return success;
}
/**
* This method calls isCampusTagNumberValid whenever a new {@link PretagDetail} is added to Pretag
*
* @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument,
* java.lang.String, org.kuali.rice.krad.bo.PersistableBusinessObject)
*/
@Override
public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject bo) {
setupConvenienceObjects();
Pretag pretag = (Pretag) document.getNewMaintainableObject().getBusinessObject();
PretagDetail detail = (PretagDetail) bo;
boolean success = true;
boolean newDetailLine = true;
if (detail.isActive()) {
detail.setPurchaseOrderNumber(pretag.getPurchaseOrderNumber());
detail.setItemLineNumber(pretag.getItemLineNumber());
success &= checkDuplicateTagNumber(pretag, detail.getCampusTagNumber());
success &= checkTotalDetailCount(pretag, newDetailLine);
success &= isCampusTagNumberValid(detail);
success &= isCampusBuildingRoomValid(detail);
}
return success;
}
/**
* This method check to see if duplicate tag exists
*
* @return boolean indicating if validation succeeded
*/
protected boolean checkDuplicateTagNumber(Pretag pretag, String tagNumber) {
boolean success = true;
for (PretagDetail dtl : pretag.getPretagDetails()) {
if (dtl.getCampusTagNumber().equals(tagNumber) && dtl.isActive()) {
GlobalVariables.getMessageMap().putError(CabPropertyConstants.Pretag.CAMPUS_TAG_NUMBER, CamsKeyConstants.ERROR_TAG_NUMBER_DUPLICATE, new String[] { tagNumber });
success &= false;
}
}
return success;
}
/**
* This method ensures that total {@link pretagDetail} tag details does not excees in quantity invoiced
*
* @param dtl
* @return true if the detail tag doesn't exist in Asset
*/
public boolean checkTotalDetailCount(Pretag pretag, boolean addLine) {
boolean success = true;
if (pretag.getQuantityInvoiced() != null) {
int totalActiveDetails = getActiveDetailsCount(pretag, addLine);
KualiDecimal totalNumerOfDetails = new KualiDecimal(totalActiveDetails);
if (pretag.getQuantityInvoiced().compareTo(totalNumerOfDetails) < 0) {
GlobalVariables.getMessageMap().putError(CabPropertyConstants.Pretag.CAMPUS_TAG_NUMBER, CamsKeyConstants.PreTag.ERROR_PRE_TAG_DETAIL_EXCESS, new String[] { pretag.getQuantityInvoiced().toString() + "" + " Total number of detail lines " + totalNumerOfDetails.toString() });
success &= false;
}
}
return success;
}
/**
* This method reply that total active detail in {@link pretag}
*
* @param pretag and newDetailLine
* @return total number of active pretagDetails
*/
public int getActiveDetailsCount(Pretag pretag, boolean newDetailLine) {
Collection<PretagDetail> pretagDetails = pretag.getPretagDetails();
if (newDetailLine) {
return countActive(pretagDetails) + 1;
}
else {
return countActive(pretagDetails);
}
}
/**
* This method ensures that each {@link pretagDetail} tag number does not exist in Asset table
*
* @param dtl
* @return true if the detail tag doesn't exist in Asset
*/
public boolean isCampusTagNumberValid(PretagDetail dtl) {
boolean success = true;
if ((dtl.getCampusTagNumber() != null) && (dtl.isActive() && !dtl.getCampusTagNumber().equalsIgnoreCase("N"))) {
Map<String, String> tagMap = new HashMap<String, String>();
tagMap.put(CabPropertyConstants.Pretag.CAMPUS_TAG_NUMBER, dtl.getCampusTagNumber());
int matchDetailCount = getMatchDetailCount(tagMap);
if ((getBoService().countMatching(Asset.class, tagMap) != 0) || (matchDetailCount > 0)) {
GlobalVariables.getMessageMap().putError(CabPropertyConstants.Pretag.CAMPUS_TAG_NUMBER, CamsKeyConstants.PreTag.ERROR_PRE_TAG_NUMBER, new String[] { dtl.getCampusTagNumber() });
success &= false;
}
}
return success;
}
/**
* This method ensures that each {@link pretagDetail} buildingCode and buildingRoomNumber does exist in bulding and room tables
*
* @param dtl
* @return true if the detail buildingCode and buildingRoomNumber does exist in building and room
*/
public boolean isCampusBuildingRoomValid(PretagDetail dtl) {
boolean success = true;
int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount();
getDictionaryValidationService().validateBusinessObject(dtl);
if (StringUtils.isNotBlank(dtl.getCampusCode()) && StringUtils.isNotBlank(dtl.getBuildingCode())) {
Map<String, String> preTagMap = new HashMap<String, String>();
preTagMap.put(KFSPropertyConstants.CAMPUS_CODE, dtl.getCampusCode());
preTagMap.put(KFSPropertyConstants.BUILDING_CODE, dtl.getBuildingCode());
bo = (Building) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(Building.class, preTagMap);
if (bo == null) {
GlobalVariables.getMessageMap().putError(KFSPropertyConstants.BUILDING_CODE, CamsKeyConstants.ERROR_INVALID_BUILDING_CODE, new String[] { dtl.getCampusCode(), dtl.getBuildingCode() });
}
if (StringUtils.isNotBlank(dtl.getBuildingRoomNumber())) {
preTagMap.put(KFSPropertyConstants.BUILDING_ROOM_NUMBER, dtl.getBuildingRoomNumber());
bo = (Room) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(Room.class, preTagMap);
if (bo == null) {
GlobalVariables.getMessageMap().putError(KFSPropertyConstants.BUILDING_ROOM_NUMBER, CamsKeyConstants.ERROR_INVALID_ROOM_NUMBER, new String[] { dtl.getCampusCode(), dtl.getBuildingCode(), dtl.getBuildingRoomNumber() });
}
}
}
success &= GlobalVariables.getMessageMap().getErrorCount() == originalErrorCount;
return success;
}
/**
* This method returns number of matched active campusTagNumber
*
* @param map
* @return active pretagDetail with same campusTagNumber
*/
public int getMatchDetailCount(Map<String, String> tagMap) {
Collection<PretagDetail> pretagDetails = SpringContext.getBean(BusinessObjectService.class).findMatching(PretagDetail.class, tagMap);
return countActive(pretagDetails);
}
/**
* This method ensures that count {@link pretagDetail} active detail lines
*
* @param collection
* @return active pretagDetail count
*/
public int countActive(Collection<PretagDetail> pretagDetails) {
int activeCount = 0;
for (PretagDetail dtl : pretagDetails) {
if (dtl.isActive()) {
activeCount++;
}
}
return activeCount;
}
/**
* This method ensures that all {@link pretag} detail lines deactivated
*
* @param pretag
* @return deactive pretagDetails
*/
public void deactivePretagDetails(Pretag pretag) {
boolean inActive = false;
for (PretagDetail dtl : pretag.getPretagDetails()) {
dtl.setActive(inActive);
}
}
/**
* Gets the purchaseOrderService attribute.
*
* @return Returns the purchaseOrderService.
*/
protected PurApInfoService getPurApInfoService() {
return SpringContext.getBean(PurApInfoService.class);
}
}