blob: e0181e7fb2e06a73d7879a2872f3f6cc247f38b3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// This script can be used for testing right now but it should not be used for production because
// it does not work for marketing packages and more importantly, if there is a new product without any
// inventory items, it will not show up on the inventory report even if it had a ProductFacility record.
// These problems need to be addressed before this can be used in production.
// This script counts the inventory in the facility using a complex and
// pretty efficient dynamic view entity.
// However, since the quantities are not computed using the inventory
// services (getInventoryAvailableByFacility and getMktgPackagesAvailable)
// there are some limitations: the virtual inventory of marketing packages
// is not computed; you can use the ViewFacilityInventoryByProduct.groovy if you
// need it (but it is slower than this one).
import org.ofbiz.base.util.Debug
import org.ofbiz.base.util.ObjectType
import org.ofbiz.entity.*
import org.ofbiz.entity.condition.*
import org.ofbiz.entity.transaction.*
import org.ofbiz.entity.util.*
import org.ofbiz.entity.model.DynamicViewEntity
import org.ofbiz.entity.model.ModelKeyMap
import org.ofbiz.entity.model.ModelViewEntity.ComplexAlias
import org.ofbiz.entity.model.ModelViewEntity.ComplexAliasField
import org.ofbiz.entity.model.ModelViewEntity.ComplexAliasMember
import org.ofbiz.product.inventory.*
action = request.getParameter("action");
searchParameterString = "";
searchParameterString = "action=Y&facilityId=" + facilityId;
offsetQOH = -1;
offsetATP = -1;
hasOffsetQOH = false;
hasOffsetATP = false;
EntityListIterator prodsEli = null;
rows = [] as ArrayList;
if (action) {
// ------------------------------
prodView = new DynamicViewEntity();
atpDiffComplexAlias = new ComplexAlias("-");
conditionMap = [facilityId : facilityId];
if (offsetQOHQty) {
try {
offsetQOH = Integer.parseInt(offsetQOHQty);
hasOffsetQOH = true;
searchParameterString = searchParameterString + "&offsetQOHQty=" + offsetQOH;
} catch (NumberFormatException nfe) {
}
}
if (offsetATPQty) {
try {
offsetATP = Integer.parseInt(offsetATPQty);
hasOffsetATP = true;
searchParameterString = searchParameterString + "&offsetATPQty=" + offsetATP;
} catch (NumberFormatException nfe) {
}
}
prodView.addMemberEntity("PRFA", "ProductFacility");
prodView.addAlias("PRFA", "productId", null, null, null, Boolean.TRUE, null);
prodView.addAlias("PRFA", "minimumStock", null, null, null, Boolean.TRUE, null);
prodView.addAlias("PRFA", "reorderQuantity", null, null, null, Boolean.TRUE, null);
prodView.addAlias("PRFA", "daysToShip", null, null, null, Boolean.TRUE, null);
prodView.addAlias("PRFA", "facilityId", null, null, null, Boolean.TRUE, null);
prodView.addMemberEntity("PROD", "Product");
prodView.addViewLink("PROD", "PRFA", Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId"));
prodView.addAlias("PROD", "internalName", null, null, null, Boolean.TRUE, null);
prodView.addAlias("PROD", "isVirtual", null, null, null, Boolean.TRUE, null);
prodView.addAlias("PROD", "salesDiscontinuationDate", null, null, null, Boolean.TRUE, null);
if (productTypeId) {
prodView.addAlias("PROD", "productTypeId", null, null, null, Boolean.TRUE, null);
conditionMap.productTypeId = productTypeId;
searchParameterString = searchParameterString + "&productTypeId=" + productTypeId;
}
prodView.addMemberEntity("IITE", "InventoryItem");
prodView.addViewLink("PRFA", "IITE", Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId", "productId", "facilityId", "facilityId"));
prodView.addAlias("IITE", "totalQuantityOnHandTotal", "quantityOnHandTotal", null, null, null, "sum");
prodView.addAlias("IITE", "totalAvailableToPromiseTotal", "availableToPromiseTotal", null, null, null, "sum");
qohDiffComplexAlias = new ComplexAlias("-");
qohDiffComplexAlias.addComplexAliasMember(new ComplexAliasField("IITE", "quantityOnHandTotal", null, "sum"));
qohDiffComplexAlias.addComplexAliasMember(new ComplexAliasField("PRFA", "minimumStock", null, null));
prodView.addAlias(null, "offsetQOHQtyAvailable", null, null, null, null, null, qohDiffComplexAlias);
atpDiffComplexAlias = new ComplexAlias("-");
atpDiffComplexAlias.addComplexAliasMember(new ComplexAliasField("IITE", "availableToPromiseTotal", null, "sum"));
atpDiffComplexAlias.addComplexAliasMember(new ComplexAliasField("PRFA", "minimumStock", null, null));
prodView.addAlias(null, "offsetATPQtyAvailable", null, null, null, null, null, atpDiffComplexAlias);
if (searchInProductCategoryId) {
prodView.addMemberEntity("PRCA", "ProductCategoryMember");
prodView.addViewLink("PRFA", "PRCA", Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId"));
prodView.addAlias("PRCA", "productCategoryId", null, null, null, Boolean.TRUE, null);
conditionMap.productCategoryId = searchInProductCategoryId;
searchParameterString = searchParameterString + "&searchInProductCategoryId=" + searchInProductCategoryId;
}
if (productSupplierId) {
prodView.addMemberEntity("SPPR", "SupplierProduct");
prodView.addViewLink("PRFA", "SPPR", Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId"));
prodView.addAlias("SPPR", "partyId", null, null, null, Boolean.TRUE, null);
conditionMap.partyId = productSupplierId;
searchParameterString = searchParameterString + "&productSupplierId=" + productSupplierId;
}
// set distinct on so we only get one row per product
findOpts = new EntityFindOptions(true, EntityFindOptions.TYPE_SCROLL_INSENSITIVE, EntityFindOptions.CONCUR_READ_ONLY, true);
searchCondition = EntityCondition.makeCondition(conditionMap, EntityOperator.AND);
notVirtualCondition = EntityCondition.makeCondition(EntityCondition.makeCondition("isVirtual", EntityOperator.EQUALS, null),
EntityOperator.OR,
EntityCondition.makeCondition("isVirtual", EntityOperator.NOT_EQUAL, "Y"));
whereConditionsList = [searchCondition, notVirtualCondition];
// add the discontinuation date condition
if (productsSoldThruTimestamp) {
discontinuationDateCondition = EntityCondition.makeCondition(
[
EntityCondition.makeCondition("salesDiscontinuationDate", EntityOperator.EQUALS, null),
EntityCondition.makeCondition("salesDiscontinuationDate", EntityOperator.GREATER_THAN,productsSoldThruTimestamp)
],
EntityOperator.OR);
whereConditionsList.add(discontinuationDateCondition);
searchParameterString = searchParameterString + "&productsSoldThruTimestamp=" + productsSoldThruTimestamp;
}
// add search on internal name
if (internalName) {
whereConditionsList.add(EntityCondition.makeCondition("internalName", EntityOperator.LIKE, "%" + internalName + "%"));
searchParameterString = searchParameterString + "&internalName=" + internalName;
}
// add search on productId
if (productId) {
whereConditionsList.add(EntityCondition.makeCondition("productId", EntityOperator.LIKE, productId + "%"));
searchParameterString = searchParameterString + "&productId=" + productId;
}
orderBy = [];
if (hasOffsetATP) {
orderBy.add("offsetATPQtyAvailable");
}
if (hasOffsetQOH) {
orderBy.add("offsetQOHQtyAvailable");
}
orderBy.add("productId");
// If the user has specified a number of months over which to sum usage quantities, define the correct timestamp
checkTime = null;
monthsInPastLimitStr = request.getParameter("monthsInPastLimit");
if (monthsInPastLimitStr) {
try {
monthsInPastLimit = Integer.parseInt(monthsInPastLimitStr);
cal = UtilDateTime.toCalendar(null);
cal.add(Calendar.MONTH, 0 - monthsInPastLimit);
checkTime = UtilDateTime.toTimestamp(cal.getTime());
searchParameterString += "&monthsInPastLimit=" + monthsInPastLimitStr;
} catch (Exception e) {
// Ignore
}
}
if (checkTime) {
// Construct a dynamic view entity to search against for sales usage quantities
salesUsageViewEntity = new DynamicViewEntity();
salesUsageViewEntity.addMemberEntity("OI", "OrderItem");
salesUsageViewEntity.addMemberEntity("OH", "OrderHeader");
salesUsageViewEntity.addMemberEntity("ItIss", "ItemIssuance");
salesUsageViewEntity.addMemberEntity("InvIt", "InventoryItem");
salesUsageViewEntity.addViewLink("OI", "OH", false, ModelKeyMap.makeKeyMapList("orderId"));
salesUsageViewEntity.addViewLink("OI", "ItIss", false, ModelKeyMap.makeKeyMapList("orderId", "orderId", "orderItemSeqId", "orderItemSeqId"));
salesUsageViewEntity.addViewLink("ItIss", "InvIt", false, ModelKeyMap.makeKeyMapList("inventoryItemId"));
salesUsageViewEntity.addAlias("OI", "productId");
salesUsageViewEntity.addAlias("OH", "statusId");
salesUsageViewEntity.addAlias("OH", "orderTypeId");
salesUsageViewEntity.addAlias("OH", "orderDate");
salesUsageViewEntity.addAlias("ItIss", "inventoryItemId");
salesUsageViewEntity.addAlias("ItIss", "quantity");
salesUsageViewEntity.addAlias("InvIt", "facilityId");
// Construct a dynamic view entity to search against for production usage quantities
productionUsageViewEntity = new DynamicViewEntity();
productionUsageViewEntity.addMemberEntity("WEIA", "WorkEffortInventoryAssign");
productionUsageViewEntity.addMemberEntity("WE", "WorkEffort");
productionUsageViewEntity.addMemberEntity("II", "InventoryItem");
productionUsageViewEntity.addViewLink("WEIA", "WE", false, ModelKeyMap.makeKeyMapList("workEffortId"));
productionUsageViewEntity.addViewLink("WEIA", "II", false, ModelKeyMap.makeKeyMapList("inventoryItemId"));
productionUsageViewEntity.addAlias("WEIA", "quantity");
productionUsageViewEntity.addAlias("WE", "actualCompletionDate");
productionUsageViewEntity.addAlias("WE", "workEffortTypeId");
productionUsageViewEntity.addAlias("II", "facilityId");
productionUsageViewEntity.addAlias("II", "productId");
}
whereCondition = EntityCondition.makeCondition(whereConditionsList, EntityOperator.AND);
beganTransaction = false;
List prods = null;
try {
beganTransaction = TransactionUtil.begin();
// get the indexes for the partial list
lowIndex = ((viewIndex.intValue() * viewSize.intValue()) + 1);
highIndex = (viewIndex.intValue() + 1) * viewSize.intValue();
findOpts.setMaxRows(highIndex);
prodsEli = delegator.findListIteratorByCondition(prodView, whereCondition, null, null, orderBy, findOpts);
// get the partial list for this page
prods = prodsEli.getPartialList(lowIndex, highIndex);
prodsIt = prods.iterator();
while (prodsIt) {
oneProd = prodsIt.next();
offsetQOHQtyAvailable = oneProd.getBigDecimal("offsetQOHQtyAvailable");
offsetATPQtyAvailable = oneProd.getBigDecimal("offsetATPQtyAvailable");
if (hasOffsetATP) {
if (offsetATPQtyAvailable && offsetATPQtyAvailable.doubleValue() > offsetATP) {
break;
}
}
if (hasOffsetQOH) {
if (offsetQOHQtyAvailable && offsetQOHQtyAvailable.doubleValue() > offsetQOH) {
break;
}
}
oneInventory = [:];
oneInventory.productId = oneProd.productId;
oneInventory.minimumStock = oneProd.getBigDecimal("minimumStock");
oneInventory.reorderQuantity = oneProd.getBigDecimal("reorderQuantity");
oneInventory.daysToShip = oneProd.getString("daysToShip");
oneInventory.totalQuantityOnHand = oneProd.totalQuantityOnHandTotal;
oneInventory.totalAvailableToPromise = oneProd.totalAvailableToPromiseTotal;
oneInventory.offsetQOHQtyAvailable = offsetQOHQtyAvailable;
oneInventory.offsetATPQtyAvailable = offsetATPQtyAvailable;
oneInventory.quantityOnOrder = InventoryWorker.getOutstandingPurchasedQuantity(oneProd.productId, delegator);
if (checkTime) {
// Make a query against the sales usage view entity
salesUsageIt = delegator.findListIteratorByCondition(salesUsageViewEntity,
EntityCondition.makeCondition(
[EntityCondition.makeCondition("facilityId", EntityOperator.EQUALS, facilityId),
EntityCondition.makeCondition("productId", EntityOperator.EQUALS, oneProd.productId),
EntityCondition.makeCondition("statusId", EntityOperator.IN, ['ORDER_COMPLETED', 'ORDER_APPROVED', 'ORDER_HELD']),
EntityCondition.makeCondition("orderTypeId", EntityOperator.EQUALS, "SALES_ORDER"),
EntityCondition.makeCondition("orderDate", EntityOperator.GREATER_THAN_EQUAL_TO, checkTime)
],
EntityOperator.AND), null, null, null, null);
// Sum the sales usage quantities found
salesUsageQuantity = 0;
salesUsageIt.each { salesUsageItem ->
if (salesUsageItem.quantity) {
try {
salesUsageQuantity += salesUsageItem.getDouble("quantity").doubleValue();
} catch (Exception e) {
// Ignore
}
}
}
salesUsageIt.close();
// Make a query against the production usage view entity
productionUsageIt = delegator.findListIteratorByCondition(productionUsageViewEntity,
EntityCondition.makeCondition(
[EntityCondition.makeCondition("facilityId", EntityOperator.EQUALS, facilityId),
EntityCondition.makeCondition("productId", EntityOperator.EQUALS, oneProd.productId),
EntityCondition.makeCondition("workEffortTypeId", EntityOperator.EQUALS, "PROD_ORDER_TASK"),
EntityCondition.makeCondition("actualCompletionDate", EntityOperator.GREATER_THAN_EQUAL_TO, checkTime)
],
EntityOperator.AND), null, null, null, null);
// Sum the production usage quantities found
productionUsageQuantity = 0;
productionUsageIt.each { productionUsageItem ->
if (productionUsageItem.quantity) {
try {
productionUsageQuantity += productionUsageItem.getDouble("quantity").doubleValue();
} catch (Exception e) {
// Ignore
}
}
}
productionUsageIt.close();
oneInventory.usageQuantity = salesUsageQuantity + productionUsageQuantity;
}
rows.add(oneInventory);
}
if (rows.size() < viewSize.intValue()) {
productListSize = lowIndex + rows.size() - 1;
} else {
// attempt to get the full size
if (hasOffsetQOH || hasOffsetATP) {
rowProcessed = 0;
while (nextValue = prodsEli.next()) {
offsetQOHQtyAvailable = nextValue.getDouble("offsetQOHQtyAvailable");
offsetATPQtyAvailable = nextValue.getDouble("offsetATPQtyAvailable");
if (hasOffsetATP) {
if (offsetATPQtyAvailable && offsetATPQtyAvailable.doubleValue() > offsetATP) {
break;
}
}
if (hasOffsetQOH) {
if (offsetQOHQtyAvailable && offsetQOHQtyAvailable.doubleValue() > offsetQOH) {
break;
}
}
rowProcessed++;
}
productListSize = lowIndex + rows.size() + rowProcessed - 1;
} else {
productListSize = prodsEli.getResultsSizeAfterPartialList();
}
}
prodsEli.close();
if (highIndex > productListSize) {
highIndex = productListSize;
}
context.overrideListSize = productListSize;
context.highIndex = highIndex;
context.lowIndex = lowIndex;
} catch (GenericEntityException e) {
errMsg = "Failure in operation, rolling back transaction";
Debug.logError(e, errMsg, "ViewFacilityInventoryByProduct");
try {
// only rollback the transaction if we started one...
TransactionUtil.rollback(beganTransaction, errMsg, e);
} catch (GenericEntityException e2) {
Debug.logError(e2, "Could not rollback transaction: " + e2.toString(), "ViewFacilityInventoryByProduct");
}
// after rolling back, rethrow the exception
throw e;
} finally {
if (prodsEli != null) {
try {
prodsEli.close();
} catch (Exception exc) {}
}
// only commit the transaction if we started one... this will throw an exception if it fails
TransactionUtil.commit(beganTransaction);
}
}
context.inventoryByProduct = rows;
context.searchParameterString = searchParameterString;