IDEMPIERE-1180 Cost Adjustment Document for Standard and Average Costing.

This commit is contained in:
Heng Sin Low 2013-07-22 20:21:02 +08:00
parent 0cead2d7f9
commit db5e974ab7
21 changed files with 4039 additions and 99 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,11 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ds.core.builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>

View File

@ -6,4 +6,6 @@ Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: org.adempiere.base;bundle-version="1.0.0"
Eclipse-RegisterBuddy: org.adempiere.base
Service-Component: OSGI-INF/costadjustmentcalloutfactory.xml
Bundle-ActivationPolicy: lazy

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.adempiere.base.callout.CostAdjustmentCalloutFactory">
<implementation class="org.adempiere.base.callout.CostAdjustmentCalloutFactory"/>
<service>
<provide interface="org.adempiere.base.IColumnCalloutFactory"/>
</service>
</scr:component>

View File

@ -1,4 +1,6 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
.,\
OSGI-INF/costadjustmentcalloutfactory.xml,\
OSGI-INF/
source.. = src/

View File

@ -0,0 +1,128 @@
/******************************************************************************
* Copyright (C) 2013 Heng Sin Low *
* Copyright (C) 2013 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.base.callout;
import java.math.BigDecimal;
import java.util.Properties;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.IColumnCalloutFactory;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.I_M_InventoryLine;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClient;
import org.compiere.model.MCost;
import org.compiere.model.MCostElement;
import org.compiere.model.MDocType;
import org.compiere.model.MInventory;
import org.compiere.model.MProduct;
import org.compiere.util.Env;
import org.compiere.util.Msg;
/**
* @author hengsin
*
*/
public class CostAdjustmentCalloutFactory implements IColumnCalloutFactory {
/**
*
*/
public CostAdjustmentCalloutFactory() {
}
/* (non-Javadoc)
* @see org.adempiere.base.IColumnCalloutFactory#getColumnCallouts(java.lang.String, java.lang.String)
*/
@Override
public IColumnCallout[] getColumnCallouts(String tableName,
String columnName) {
if (tableName.equalsIgnoreCase(I_M_InventoryLine.Table_Name)) {
if (columnName.equalsIgnoreCase(I_M_InventoryLine.COLUMNNAME_M_Product_ID))
return new IColumnCallout[]{new CostAdjustmentLineProduct()};
else if (columnName.equalsIgnoreCase(I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID))
return new IColumnCallout[]{new CostAdjustmentLineASI()};
}
return null;
}
/**
* callout for m_product_id
*/
private static class CostAdjustmentLineProduct implements IColumnCallout {
@Override
public String start(Properties ctx, int WindowNo, GridTab mTab,
GridField mField, Object value, Object oldValue) {
MInventory inventory = MInventory.get(ctx, (Integer) mTab.getValue("M_Inventory_ID"));
if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) {
String costingMethod = inventory.getCostingMethod();
if (value == null) {
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO);
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO);
} else {
MProduct product = MProduct.get(ctx, (Integer) value);
MClient client = MClient.get(ctx);
MAcctSchema as = client.getAcctSchema();
Object asiValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
int M_ASI_ID = asiValue != null ? (Integer)asiValue : 0;
int AD_Org_ID = inventory.getAD_Org_ID();
MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod);
if (cost == null) {
if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) {
mTab.setValue(mField, null);
return Msg.getMsg(Env.getCtx(), "NoCostingRecord");
}
}
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice());
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice());
}
}
return null;
}
}
/**
* callout for m_attributesetinstance_id
*/
private class CostAdjustmentLineASI implements IColumnCallout {
@Override
public String start(Properties ctx, int WindowNo, GridTab mTab,
GridField mField, Object value, Object oldValue) {
MInventory inventory = MInventory.get(ctx, (Integer) mTab.getValue("M_Inventory_ID"));
if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) {
String costingMethod = inventory.getCostingMethod();
Object productValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_Product_ID);
if (productValue == null || ((Integer)productValue).intValue() == 0) return null;
MProduct product = MProduct.get(ctx, (Integer)productValue);
int M_ASI_ID = value != null ? (Integer)value : 0;
int AD_Org_ID = inventory.getAD_Org_ID();
MClient client = MClient.get(ctx);
MAcctSchema as = client.getAcctSchema();
MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod);
if (cost != null) {
BigDecimal currentCost = (BigDecimal) mTab.getValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice);
if (currentCost == null || currentCost.compareTo(cost.getCurrentCostPrice())==0) return null;
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice());
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice());
}
}
return null;
}
}
}

View File

@ -19,17 +19,27 @@ package org.compiere.process;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import org.compiere.model.I_C_DocType;
import org.compiere.model.I_M_Inventory;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClient;
import org.compiere.model.MCost;
import org.compiere.model.MCostElement;
import org.compiere.model.MDocType;
import org.compiere.model.MInventory;
import org.compiere.model.MInventoryLine;
import org.compiere.model.MProduct;
import org.compiere.util.AdempiereSystemError;
import org.compiere.util.AdempiereUserError;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
/**
* Standard Cost Update
@ -48,6 +58,8 @@ public class CostUpdate extends SvrProcess
/** PLV */
private int p_M_PriceList_Version_ID = 0;
private int p_C_DocType_ID = 0;
private static final String TO_AveragePO = "A";
private static final String TO_AverageInvoiceHistory = "DI";
@ -67,6 +79,7 @@ public class CostUpdate extends SvrProcess
private MAcctSchema[] m_ass = null;
/** Map of Cost Elements */
private HashMap<String,MCostElement> m_ces = new HashMap<String,MCostElement>();
private MDocType m_docType = null;
/**
@ -89,6 +102,8 @@ public class CostUpdate extends SvrProcess
p_SetStandardCostTo = (String)para[i].getParameter();
else if (name.equals("M_PriceList_Version_ID"))
p_M_PriceList_Version_ID = para[i].getParameterAsInt();
else if (name.equals("C_DocType_ID"))
p_C_DocType_ID = para[i].getParameterAsInt();
else
log.log(Level.SEVERE, "Unknown Parameter: " + name);
}
@ -114,6 +129,13 @@ public class CostUpdate extends SvrProcess
{
return "-";
}
if (!Util.isEmpty(p_SetStandardCostTo))
{
if (p_C_DocType_ID <= 0)
throw new AdempiereUserError ("@FillMandatory@ @C_DocType_ID@");
else
m_docType = MDocType.get(getCtx(), p_C_DocType_ID);
}
// PLV required
if (p_M_PriceList_Version_ID == 0
&& (p_SetFutureCostTo.equals(TO_PriceListLimit) || p_SetStandardCostTo.equals(TO_PriceListLimit)))
@ -253,6 +275,10 @@ public class CostUpdate extends SvrProcess
ResultSet rs = null;
try
{
List<MInventoryLine> lines = new ArrayList<MInventoryLine>();
MClient client = MClient.get(getCtx());
MAcctSchema primarySchema = client.getAcctSchema();
MInventory inventoryDoc = null;
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt (1, m_ce.getM_CostElement_ID());
if (p_M_Product_Category_ID != 0)
@ -267,15 +293,65 @@ public class CostUpdate extends SvrProcess
if (m_ass[i].getC_AcctSchema_ID() == cost.getC_AcctSchema_ID()
&& m_ass[i].getM_CostType_ID() == cost.getM_CostType_ID())
{
if (update (cost))
if (m_ass[i].getC_AcctSchema_ID() == primarySchema.getC_AcctSchema_ID())
{
if (update (cost, lines))
counter++;
}
else
{
if (update (cost, null))
counter++;
}
}
}
}
if (lines.size() > 0)
{
inventoryDoc = new MInventory(getCtx(), 0, get_TrxName());
inventoryDoc.setC_DocType_ID(p_C_DocType_ID);
inventoryDoc.setCostingMethod(MCostElement.COSTINGMETHOD_StandardCosting);
inventoryDoc.setDocAction(DocAction.ACTION_Complete);
inventoryDoc.saveEx();
for(MInventoryLine line : lines)
{
line.setM_Inventory_ID(inventoryDoc.getM_Inventory_ID());
line.saveEx();
}
if (!DocumentEngine.processIt(inventoryDoc, DocAction.ACTION_Complete))
{
StringBuilder msg = new StringBuilder();
msg.append(Msg.getMsg(getCtx(), "ProcessFailed")).append(": ");
if (Env.isBaseLanguage(getCtx(), I_C_DocType.Table_Name))
msg.append(m_docType.getName());
else
msg.append(m_docType.get_Translation(I_C_DocType.COLUMNNAME_Name));
throw new AdempiereUserError(msg.toString());
}
else
{
inventoryDoc.saveEx();
StringBuilder msg = new StringBuilder();
if (Env.isBaseLanguage(getCtx(), I_C_DocType.Table_Name))
msg.append(m_docType.getName()).append(" ").append(inventoryDoc.getDocumentNo());
else
msg.append(m_docType.get_Translation(I_C_DocType.COLUMNNAME_Name)).append(" ").append(inventoryDoc.getDocumentNo());
addLog(getAD_PInstance_ID(), null, null, msg.toString(), I_M_Inventory.Table_ID, inventoryDoc.getM_Inventory_ID());
}
}
}
catch (Exception e)
{
log.log (Level.SEVERE, sql, e);
if (e instanceof RuntimeException)
{
throw (RuntimeException)e;
}
else
{
throw new RuntimeException(e);
}
}
finally
{
@ -291,10 +367,11 @@ public class CostUpdate extends SvrProcess
/**
* Update Cost Records
* @param cost cost
* @param inventoryDoc
* @return true if updated
* @throws Exception
*/
private boolean update (MCost cost) throws Exception
private boolean update (MCost cost, List<MInventoryLine> lines) throws Exception
{
boolean updated = false;
if (p_SetFutureCostTo.equals(p_SetStandardCostTo))
@ -303,21 +380,37 @@ public class CostUpdate extends SvrProcess
if (costs != null && costs.signum() != 0)
{
cost.setFutureCostPrice(costs);
cost.setCurrentCostPrice(costs);
updated = true;
}
if (lines != null)
{
MInventoryLine line = new MInventoryLine(getCtx(), 0, get_TrxName());
line.setM_Product_ID(cost.getM_Product_ID());
line.setCurrentCostPrice(cost.getCurrentCostPrice());
line.setNewCostPrice(costs);
line.setM_Locator_ID(0);
lines.add(line);
}
}
else
{
if (p_SetStandardCostTo.length() > 0)
{
if (lines != null)
{
BigDecimal costs = getCosts(cost, p_SetStandardCostTo);
if (costs != null && costs.signum() != 0)
{
cost.setCurrentCostPrice(costs);
MInventoryLine line = new MInventoryLine(getCtx(), 0, get_TrxName());
line.setM_Product_ID(cost.getM_Product_ID());
line.setCurrentCostPrice(cost.getCurrentCostPrice());
line.setNewCostPrice(costs);
line.setM_Locator_ID(0);
lines.add(line);
updated = true;
}
}
}
if (p_SetFutureCostTo.length() > 0)
{
BigDecimal costs = getCosts(cost, p_SetFutureCostTo);

View File

@ -0,0 +1,66 @@
/******************************************************************************
* Copyright (C) 2013 Heng Sin Low *
* Copyright (C) 2013 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.idempiere.process;
import java.math.BigDecimal;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClient;
import org.compiere.model.MCost;
import org.compiere.model.MInventoryLine;
import org.compiere.model.MProduct;
import org.compiere.process.SvrProcess;
/**
* @author hengsin
*
*/
public class CostAdjustmentLineRefreshCost extends SvrProcess {
/**
*
*/
public CostAdjustmentLineRefreshCost() {
}
/* (non-Javadoc)
* @see org.compiere.process.SvrProcess#prepare()
*/
@Override
protected void prepare() {
}
/* (non-Javadoc)
* @see org.compiere.process.SvrProcess#doIt()
*/
@Override
protected String doIt() throws Exception {
MInventoryLine line = new MInventoryLine(getCtx(), getRecord_ID(), null);
MProduct product = line.getProduct();
MClient client = MClient.get(getCtx(), line.getAD_Client_ID());
MAcctSchema as = client.getAcctSchema();
MCost cost = product.getCostingRecord(as, line.getAD_Org_ID(), line.getM_AttributeSetInstance_ID(), line.getM_Inventory().getCostingMethod());
if (cost != null) {
line.setCurrentCostPrice(cost.getCurrentCostPrice());
line.setNewCostPrice(cost.getCurrentCostPrice());
line.saveEx();
} else {
line.setCurrentCostPrice(BigDecimal.ZERO);
line.setNewCostPrice(BigDecimal.ZERO);
line.saveEx();
}
return "@Ok@";
}
}

View File

@ -23,11 +23,17 @@ import java.util.logging.Level;
import org.compiere.model.MAccount;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClient;
import org.compiere.model.MConversionRate;
import org.compiere.model.MCost;
import org.compiere.model.MCostDetail;
import org.compiere.model.MCostElement;
import org.compiere.model.MDocType;
import org.compiere.model.MInventory;
import org.compiere.model.MInventoryLine;
import org.compiere.model.MProduct;
import org.compiere.model.ProductCost;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Util;
@ -49,6 +55,7 @@ public class Doc_Inventory extends Doc
private int m_Reversal_ID = 0;
@SuppressWarnings("unused")
private String m_DocStatus = "";
private String parentDocSubTypeInv;
/**
* Constructor
@ -67,12 +74,23 @@ public class Doc_Inventory extends Doc
*/
protected String loadDocumentDetails()
{
setC_Currency_ID (NO_CURRENCY);
MInventory inventory = (MInventory)getPO();
setDateDoc (inventory.getMovementDate());
setDateAcct(inventory.getMovementDate());
m_Reversal_ID = inventory.getReversal_ID();//store original (voided/reversed) document
m_DocStatus = inventory.getDocStatus();
MDocType dt = MDocType.get(getCtx(), getC_DocType_ID());
parentDocSubTypeInv = dt.getDocSubTypeInv();
if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(parentDocSubTypeInv))
{
MClient client = MClient.get(getCtx(), inventory.getAD_Client_ID());
int C_Currency_ID = client.getAcctSchema().getC_Currency_ID();
setC_Currency_ID(C_Currency_ID);
}
else
{
setC_Currency_ID (NO_CURRENCY);
}
// Contained Objects
p_lines = loadLines(inventory);
if (log.isLoggable(Level.FINE)) log.fine("Lines=" + p_lines.length);
@ -86,8 +104,6 @@ public class Doc_Inventory extends Doc
*/
private DocLine[] loadLines(MInventory inventory)
{
MDocType dt = MDocType.get(getCtx(), getC_DocType_ID());
String parentDocSubTypeInv = dt.getDocSubTypeInv();
ArrayList<DocLine> list = new ArrayList<DocLine>();
MInventoryLine[] lines = inventory.getLines(false);
for (int i = 0; i < lines.length; i++)
@ -106,16 +122,23 @@ public class Doc_Inventory extends Doc
}
BigDecimal qtyDiff = Env.ZERO;
BigDecimal amtDiff = Env.ZERO;
if (MDocType.DOCSUBTYPEINV_InternalUseInventory.equals(docSubTypeInv))
qtyDiff = line.getQtyInternalUse().negate();
else if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv))
qtyDiff = line.getQtyCount().subtract(line.getQtyBook());
else if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(docSubTypeInv))
amtDiff = line.getNewCostPrice().subtract(line.getCurrentCostPrice());
// nothing to post
if (qtyDiff.signum() == 0)
if (qtyDiff.signum() == 0 && amtDiff.signum() == 0)
continue;
//
DocLine docLine = new DocLine (line, this);
docLine.setQty (qtyDiff, false); // -5 => -5
if (amtDiff.signum() != 0)
{
docLine.setAmount(amtDiff);
}
docLine.setReversalLine_ID(line.getReversalLine_ID());
if (log.isLoggable(Level.FINE)) log.fine(docLine.toString());
list.add (docLine);
@ -158,10 +181,54 @@ public class Doc_Inventory extends Doc
FactLine dr = null;
FactLine cr = null;
MInventory inventory = (MInventory) getPO();
boolean costAdjustment = MDocType.DOCSUBTYPEINV_CostAdjustment.equals(parentDocSubTypeInv);
String docCostingMethod = inventory.getCostingMethod();
for (int i = 0; i < p_lines.length; i++)
{
DocLine line = p_lines[i];
boolean doPosting = true;
String costingLevel = null;
MProduct product = null;
if (costAdjustment)
{
product = line.getProduct();
String productCostingMethod = product.getCostingMethod(as);
costingLevel = product.getCostingLevel(as);
if (!docCostingMethod.equals(productCostingMethod))
{
doPosting = false;
}
}
BigDecimal costs = null;
BigDecimal adjustmentDiff = null;
if (costAdjustment)
{
costs = line.getAmtSource();
int orgId = line.getAD_Org_ID();
int asiId = line.getM_AttributeSetInstance_ID();
if (MAcctSchema.COSTINGLEVEL_Client.equals(costingLevel))
{
orgId = 0;
asiId = 0;
}
else if (MAcctSchema.COSTINGLEVEL_Organization.equals(costingLevel))
asiId = 0;
else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel))
orgId = 0;
MCostElement ce = MCostElement.getMaterialCostElement(getCtx(), docCostingMethod, orgId);
MCost cost = MCost.get(product, asiId, as,
orgId, ce.getM_CostElement_ID(), getTrxName());
DB.getDatabase().forUpdate(cost, 120);
BigDecimal currentQty = cost.getCurrentQty();
adjustmentDiff = costs;
costs = costs.multiply(currentQty);
}
else
{
if (!isReversal(line))
{
// MZ Goodwill
@ -176,15 +243,21 @@ public class Doc_Inventory extends Doc
}
else
{
costs = BigDecimal.ZERO;
//updated below
costs = Env.ONE;
}
}
if (doPosting)
{
int C_Currency_ID = getC_Currency_ID() > 0 ? getC_Currency_ID() : as.getC_Currency_ID();
// Inventory DR CR
dr = fact.createLine(line,
line.getAccount(ProductCost.ACCTTYPE_P_Asset, as),
as.getC_Currency_ID(), costs);
C_Currency_ID, costs);
// may be zero difference - no line created.
if (dr == null)
continue;
if (dr != null)
{
dr.setM_Locator_ID(line.getM_Locator_ID());
if (isReversal(line))
{
@ -208,11 +281,20 @@ public class Doc_Inventory extends Doc
}
if (invDiff == null)
{
if (costAdjustment)
{
invDiff = line.getProductCost().getAccount(ProductCost.ACCTTYPE_P_CostAdjustment, as);
}
else
{
invDiff = getAccount(Doc.ACCTTYPE_InvDifferences, as);
}
}
cr = fact.createLine(line, invDiff,
as.getC_Currency_ID(), costs.negate());
if (cr == null)
continue;
C_Currency_ID, costs.negate());
if (cr != null)
{
cr.setM_Locator_ID(line.getM_Locator_ID());
cr.setQty(line.getQty().negate());
if (line.getC_Charge_ID() != 0) // explicit overwrite for charge
@ -229,18 +311,31 @@ public class Doc_Inventory extends Doc
}
costs = cr.getAcctBalance(); //get original cost
}
}
}
}
if (doPosting || costAdjustment)
{
BigDecimal costDetailAmt = costAdjustment ? adjustmentDiff : costs;
if (costAdjustment && getC_Currency_ID() > 0 && getC_Currency_ID() != as.getC_Currency_ID())
{
costDetailAmt = MConversionRate.convert (getCtx(),
costDetailAmt, getC_Currency_ID(), as.getC_Currency_ID(),
getDateAcct(), 0, getAD_Client_ID(), getAD_Org_ID());
}
// Cost Detail
if (!MCostDetail.createInventory(as, line.getAD_Org_ID(),
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
line.get_ID(), 0,
costs, line.getQty(),
costDetailAmt, line.getQty(),
line.getDescription(), getTrxName()))
{
p_Error = "Failed to create cost detail record";
return null;
}
}
}
//
ArrayList<Fact> facts = new ArrayList<Fact>();
facts.add(fact);

View File

@ -133,6 +133,19 @@ public interface I_M_Inventory
public org.compiere.model.I_C_DocType getC_DocType() throws RuntimeException;
/** Column name CostingMethod */
public static final String COLUMNNAME_CostingMethod = "CostingMethod";
/** Set Costing Method.
* Indicates how Costs will be calculated
*/
public void setCostingMethod (String CostingMethod);
/** Get Costing Method.
* Indicates how Costs will be calculated
*/
public String getCostingMethod();
/** Column name C_Project_ID */
public static final String COLUMNNAME_C_Project_ID = "C_Project_ID";

View File

@ -93,6 +93,19 @@ public interface I_M_InventoryLine
*/
public int getCreatedBy();
/** Column name CurrentCostPrice */
public static final String COLUMNNAME_CurrentCostPrice = "CurrentCostPrice";
/** Set Current Cost Price.
* The currently used cost price
*/
public void setCurrentCostPrice (BigDecimal CurrentCostPrice);
/** Get Current Cost Price.
* The currently used cost price
*/
public BigDecimal getCurrentCostPrice();
/** Column name Description */
public static final String COLUMNNAME_Description = "Description";
@ -227,6 +240,19 @@ public interface I_M_InventoryLine
public org.compiere.model.I_M_Product getM_Product() throws RuntimeException;
/** Column name NewCostPrice */
public static final String COLUMNNAME_NewCostPrice = "NewCostPrice";
/** Set New Cost Price.
* New current cost price after processing of M_CostDetail
*/
public void setNewCostPrice (BigDecimal NewCostPrice);
/** Get New Cost Price.
* New current cost price after processing of M_CostDetail
*/
public BigDecimal getNewCostPrice();
/** Column name Processed */
public static final String COLUMNNAME_Processed = "Processed";

View File

@ -1037,13 +1037,22 @@ public class MCostDetail extends X_M_CostDetail
|| getPP_Cost_Collector_ID() != 0)
{
boolean addition = qty.signum() > 0;
boolean adjustment = getM_InventoryLine_ID() > 0 && qty.signum() == 0 && amt.signum() != 0;
boolean isVendorRMA = isVendorRMA();
//
if (ce.isAverageInvoice())
{
if (!isVendorRMA)
{
if (addition)
if (adjustment)
{
String costingMethod = getM_InventoryLine().getM_Inventory().getCostingMethod();
if (MCostElement.COSTINGMETHOD_AverageInvoice.equals(costingMethod))
{
cost.setWeightedAverage(amt.multiply(cost.getCurrentQty()), qty);
}
}
else if (addition)
{
cost.setWeightedAverage(amt, qty);
//shouldn't accumulate reversal of customer shipment qty and amt
@ -1060,7 +1069,15 @@ public class MCostDetail extends X_M_CostDetail
}
else if (ce.isAveragePO())
{
if (addition)
if (adjustment)
{
String costingMethod = getM_InventoryLine().getM_Inventory().getCostingMethod();
if (MCostElement.COSTINGMETHOD_AveragePO.equals(costingMethod))
{
cost.setWeightedAverage(amt.multiply(cost.getCurrentQty()), qty);
}
}
else if (addition)
{
cost.setWeightedAverage(amt, qty);
//shouldn't accumulate reversal of customer shipment qty and amt
@ -1085,7 +1102,7 @@ public class MCostDetail extends X_M_CostDetail
}
else if (ce.isFifo() || ce.isLifo())
{
if (!isVendorRMA)
if (!isVendorRMA && !adjustment)
{
if (addition)
{
@ -1110,19 +1127,28 @@ public class MCostDetail extends X_M_CostDetail
if (log.isLoggable(Level.FINER)) log.finer("QtyAdjust - FiFo/Lifo - " + cost);
}
}
else if (ce.isLastInvoice() && !isVendorRMA)
else if (ce.isLastInvoice() && !isVendorRMA && !adjustment)
{
cost.setCurrentQty(cost.getCurrentQty().add(qty));
if (log.isLoggable(Level.FINER)) log.finer("QtyAdjust - LastInv - " + cost);
}
else if (ce.isLastPOPrice() && !isVendorRMA)
else if (ce.isLastPOPrice() && !isVendorRMA && !adjustment)
{
cost.setCurrentQty(cost.getCurrentQty().add(qty));
if (log.isLoggable(Level.FINER)) log.finer("QtyAdjust - LastPO - " + cost);
}
else if (ce.isStandardCosting() && !isVendorRMA)
{
if (addition)
if (adjustment)
{
String costingMethod = getM_InventoryLine().getM_Inventory().getCostingMethod();
if (MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod))
{
cost.add(amt.multiply(cost.getCurrentQty()), qty);
cost.setCurrentCostPrice(cost.getCurrentCostPrice().add(amt));
}
}
else if (addition)
{
cost.add(amt, qty);
// Initial
@ -1140,7 +1166,7 @@ public class MCostDetail extends X_M_CostDetail
}
if (log.isLoggable(Level.FINER)) log.finer("QtyAdjust - Standard - " + cost);
}
else if (ce.isUserDefined() && !isVendorRMA)
else if (ce.isUserDefined() && !isVendorRMA && !adjustment)
{
// Interface
if (addition)

View File

@ -105,6 +105,27 @@ public class MCostElement extends X_M_CostElement
return retValue;
} // getMaterialCostElement
/**
* Get first Material Cost Element
* @param ctx context
* @param CostingMethod costing method
* @return Cost Element or null
*/
public static MCostElement getMaterialCostElement(Properties ctx, String CostingMethod, int AD_Org_ID)
{
final String whereClause = "AD_Client_ID=? AND CostingMethod=? AND CostElementType=? AND AD_Org_ID In (0, ?)";
List<MCostElement> list = new Query(ctx, I_M_CostElement.Table_Name, whereClause, null)
.setParameters(Env.getAD_Client_ID(ctx),CostingMethod,COSTELEMENTTYPE_Material,AD_Org_ID)
.setOrderBy(I_M_CostElement.COLUMNNAME_AD_Org_ID + " Desc")
.list();
MCostElement retValue = null;
if (list.size() > 0)
retValue = list.get(0);
if (list.size() > 1)
if (s_log.isLoggable(Level.INFO)) s_log.info("More then one Material Cost Element for CostingMethod=" + CostingMethod);
return retValue;
} // getMaterialCostElement
/**
* Get active Material Cost Element for client
* @param po parent

View File

@ -412,6 +412,21 @@ public class MInventory extends X_M_Inventory implements DocAction
qtyDiff = line.getQtyInternalUse().negate();
else if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv))
qtyDiff = line.getQtyCount().subtract(line.getQtyBook());
else if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(docSubTypeInv))
{
if (!isReversal())
{
BigDecimal currentCost = line.getCurrentCostPrice();
MClient client = MClient.get(getCtx(), getAD_Client_ID());
MAcctSchema as = client.getAcctSchema();
MCost cost = product.getCostingRecord(as, getAD_Org_ID(), line.getM_AttributeSetInstance_ID(), getCostingMethod());
if (cost != null && cost.getCurrentCostPrice().compareTo(currentCost) != 0)
{
m_processMsg = "Current Cost for Line " + line.getLine() + " have changed.";
return DocAction.STATUS_Invalid;
}
}
}
//If Quantity Count minus Quantity Book = Zero, then no change in Inventory
if (qtyDiff.signum() == 0)

View File

@ -346,7 +346,33 @@ public class MInventoryLine extends X_M_InventoryLine
log.saveError("Quantity", Msg.getElement(getCtx(), COLUMNNAME_QtyInternalUse));
return false;
}
} else if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(docSubTypeInv)) {
if (getNewCostPrice().signum() == 0) {
log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_NewCostPrice));
return false;
}
int M_ASI_ID = getM_AttributeSetInstance_ID();
MProduct product = getProduct();
MClient client = MClient.get(getCtx());
MAcctSchema as = client.getAcctSchema();
String costingLevel = product.getCostingLevel(as);
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel)) {
if (M_ASI_ID == 0) {
log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_M_AttributeSetInstance_ID));
return false;
}
}
String costingMethod = getParent().getCostingMethod();
int AD_Org_ID = getAD_Org_ID();
MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod);
if (cost == null) {
if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) {
log.saveError("NoCostingRecord", "");
return false;
}
}
} else {
log.saveError("Error", "Document inventory subtype not configured, cannot complete");
return false;

View File

@ -869,4 +869,34 @@ public class MProduct extends X_M_Product
}
return costingMethod;
}
public MCost getCostingRecord(MAcctSchema as, int AD_Org_ID, int M_ASI_ID)
{
return getCostingRecord(as, AD_Org_ID, M_ASI_ID, getCostingMethod(as));
}
public MCost getCostingRecord(MAcctSchema as, int AD_Org_ID, int M_ASI_ID, String costingMethod)
{
String costingLevel = getCostingLevel(as);
if (MAcctSchema.COSTINGLEVEL_Client.equals(costingLevel))
{
AD_Org_ID = 0;
M_ASI_ID = 0;
}
else if (MAcctSchema.COSTINGLEVEL_Organization.equals(costingLevel))
M_ASI_ID = 0;
else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel))
{
AD_Org_ID = 0;
if (M_ASI_ID == 0)
return null;
}
MCostElement ce = MCostElement.getMaterialCostElement(getCtx(), costingMethod, AD_Org_ID);
if (ce == null) {
return null;
}
MCost cost = MCost.get(this, M_ASI_ID, as, AD_Org_ID, ce.getM_CostElement_ID(), (String)null);
return cost.is_new() ? null : cost;
}
} // MProduct

View File

@ -683,11 +683,15 @@ public final class MSetup
createDocType("Material Movement", Msg.getElement(m_ctx, "M_Movement_ID", false),
MDocType.DOCBASETYPE_MaterialMovement, null, 0, 0, 610000, GL_MM, false);
createDocType("Physical Inventory", Msg.getElement(m_ctx, "M_Inventory_ID", false),
MDocType.DOCBASETYPE_MaterialPhysicalInventory, null, 0, 0, 620000, GL_MM, false);
MDocType.DOCBASETYPE_MaterialPhysicalInventory, MDocType.DOCSUBTYPEINV_PhysicalInventory, 0, 0, 620000, GL_MM, false);
createDocType("Material Production", Msg.getElement(m_ctx, "M_Production_ID", false),
MDocType.DOCBASETYPE_MaterialProduction, null, 0, 0, 630000, GL_MM, false);
createDocType("Project Issue", Msg.getElement(m_ctx, "C_ProjectIssue_ID", false),
MDocType.DOCBASETYPE_ProjectIssue, null, 0, 0, 640000, GL_MM, false);
createDocType("Internal Use Inventory", "Internal Use Inventory",
MDocType.DOCBASETYPE_MaterialPhysicalInventory, MDocType.DOCSUBTYPEINV_InternalUseInventory, 0, 0, 650000, GL_MM, false);
createDocType("Cost Adjustment", "Cost Adjustment",
MDocType.DOCBASETYPE_MaterialPhysicalInventory, MDocType.DOCSUBTYPEINV_CostAdjustment, 0, 0, 660000, GL_MM, false);
// Order Entry
createDocType("Binding offer", "Quotation",
@ -891,7 +895,16 @@ public final class MSetup
if (PrintName != null && PrintName.length() > 0)
dt.setPrintName(PrintName); // Defaults to Name
if (DocSubTypeSO != null)
{
if (MDocType.DOCBASETYPE_MaterialPhysicalInventory.equals(DocBaseType))
{
dt.setDocSubTypeInv(DocSubTypeSO);
}
else
{
dt.setDocSubTypeSO(DocSubTypeSO);
}
}
if (C_DocTypeShipment_ID != 0)
dt.setC_DocTypeShipment_ID(C_DocTypeShipment_ID);
if (C_DocTypeInvoice_ID != 0)

View File

@ -30,7 +30,7 @@ public class X_C_DocType extends PO implements I_C_DocType, I_Persistent
/**
*
*/
private static final long serialVersionUID = 20130626L;
private static final long serialVersionUID = 20130717L;
/** Standard Constructor */
public X_C_DocType (Properties ctx, int C_DocType_ID, String trxName)
@ -429,6 +429,8 @@ public class X_C_DocType extends PO implements I_C_DocType, I_Persistent
public static final String DOCSUBTYPEINV_PhysicalInventory = "PI";
/** Internal Use Inventory = IU */
public static final String DOCSUBTYPEINV_InternalUseInventory = "IU";
/** Cost Adjustment = CA */
public static final String DOCSUBTYPEINV_CostAdjustment = "CA";
/** Set Inv Sub Type.
@param DocSubTypeInv
Inventory Sub Type

View File

@ -33,7 +33,7 @@ public class X_M_Inventory extends PO implements I_M_Inventory, I_Persistent
/**
*
*/
private static final long serialVersionUID = 20130626L;
private static final long serialVersionUID = 20130717L;
/** Standard Constructor */
public X_M_Inventory (Properties ctx, int M_Inventory_ID, String trxName)
@ -212,6 +212,44 @@ public class X_M_Inventory extends PO implements I_M_Inventory, I_Persistent
return ii.intValue();
}
/** CostingMethod AD_Reference_ID=122 */
public static final int COSTINGMETHOD_AD_Reference_ID=122;
/** Standard Costing = S */
public static final String COSTINGMETHOD_StandardCosting = "S";
/** Average PO = A */
public static final String COSTINGMETHOD_AveragePO = "A";
/** Lifo = L */
public static final String COSTINGMETHOD_Lifo = "L";
/** Fifo = F */
public static final String COSTINGMETHOD_Fifo = "F";
/** Last PO Price = p */
public static final String COSTINGMETHOD_LastPOPrice = "p";
/** Average Invoice = I */
public static final String COSTINGMETHOD_AverageInvoice = "I";
/** Last Invoice = i */
public static final String COSTINGMETHOD_LastInvoice = "i";
/** User Defined = U */
public static final String COSTINGMETHOD_UserDefined = "U";
/** _ = x */
public static final String COSTINGMETHOD__ = "x";
/** Set Costing Method.
@param CostingMethod
Indicates how Costs will be calculated
*/
public void setCostingMethod (String CostingMethod)
{
set_Value (COLUMNNAME_CostingMethod, CostingMethod);
}
/** Get Costing Method.
@return Indicates how Costs will be calculated
*/
public String getCostingMethod ()
{
return (String)get_Value(COLUMNNAME_CostingMethod);
}
public org.compiere.model.I_C_Project getC_Project() throws RuntimeException
{
return (org.compiere.model.I_C_Project)MTable.get(getCtx(), org.compiere.model.I_C_Project.Table_Name)

View File

@ -32,7 +32,7 @@ public class X_M_InventoryLine extends PO implements I_M_InventoryLine, I_Persis
/**
*
*/
private static final long serialVersionUID = 20130626L;
private static final long serialVersionUID = 20130717L;
/** Standard Constructor */
public X_M_InventoryLine (Properties ctx, int M_InventoryLine_ID, String trxName)
@ -111,6 +111,26 @@ public class X_M_InventoryLine extends PO implements I_M_InventoryLine, I_Persis
return ii.intValue();
}
/** Set Current Cost Price.
@param CurrentCostPrice
The currently used cost price
*/
public void setCurrentCostPrice (BigDecimal CurrentCostPrice)
{
set_ValueNoCheck (COLUMNNAME_CurrentCostPrice, CurrentCostPrice);
}
/** Get Current Cost Price.
@return The currently used cost price
*/
public BigDecimal getCurrentCostPrice ()
{
BigDecimal bd = (BigDecimal)get_Value(COLUMNNAME_CurrentCostPrice);
if (bd == null)
return Env.ZERO;
return bd;
}
/** Set Description.
@param Description
Optional short description of the record
@ -329,6 +349,26 @@ public class X_M_InventoryLine extends PO implements I_M_InventoryLine, I_Persis
return ii.intValue();
}
/** Set New Cost Price.
@param NewCostPrice
New current cost price after processing of M_CostDetail
*/
public void setNewCostPrice (BigDecimal NewCostPrice)
{
set_Value (COLUMNNAME_NewCostPrice, NewCostPrice);
}
/** Get New Cost Price.
@return New current cost price after processing of M_CostDetail
*/
public BigDecimal getNewCostPrice ()
{
BigDecimal bd = (BigDecimal)get_Value(COLUMNNAME_NewCostPrice);
if (bd == null)
return Env.ZERO;
return bd;
}
/** Set Processed.
@param Processed
The document has been processed