Posted by: Bayu Cahya P on: August 8, 2008
As explained in our previous journal, we were facing to wrong calculation result within cost details records for our USD currency. As per our test, this error result firstly came from matching PO/Invoice regarding its Purchase Orders documents. Cause our transaction within IDR currency and the wrong calculation was within USD currency, it seems that we had problem with conversion rate area. Digging into source codes, this is our culprit createMatchPOCostDetail method which called by completeIt in M_InOut files
private String createMatchPOCostDetail(MMatchPO po, MOrderLine m_oLine)
{
// Get Account Schemas to create MCostDetail
MAcctSchema[] acctschemas = MAcctSchema.getClientAcctSchema(getCtx(), getAD_Client_ID());
for(int asn = 0; asn < acctschemas.length; asn++)
{
MAcctSchema as = acctschemas[asn];
boolean skip = false;
if (as.getAD_OrgOnly_ID() != 0)
{
if (as.getOnlyOrgs() == null)
as.setOnlyOrgs(MReportTree.getChildIDs(getCtx(),
0, MAcctSchemaElement.ELEMENTTYPE_Organization,
as.getAD_OrgOnly_ID()));
// Header Level Org
skip = as.isSkipOrg(getAD_Org_ID());
// Line Level Org
skip = as.isSkipOrg(m_oLine.getAD_Org_ID());
}
if (skip)
continue;
// Purchase Order Line
BigDecimal poCost = m_oLine.getPriceCost();
if (poCost == null || poCost.signum() == 0)
poCost = m_oLine.getPriceActual();
poCost = poCost.multiply(po.getQty()); // Delivered so far
// Different currency
if (m_oLine.getC_Currency_ID() != as.getC_Currency_ID())
{
MOrder order = m_oLine.getParent();
BigDecimal rate = MConversionRate.getRate(
order.getC_Currency_ID(), as.getC_Currency_ID(),
order.getDateAcct(), order.getC_ConversionType_ID(),
m_oLine.getAD_Client_ID(), m_oLine.getAD_Org_ID());
if (rate == null)
{
return "Purchase Order not convertible - " + as.getName();
}
poCost = poCost.multiply(rate);
if (poCost.scale() > as.getCostingPrecision())
poCost = poCost.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP);
}
// Source from Doc_MatchPO.createFacts(MAcctSchema)
MInOutLine receiptLine = new MInOutLine (getCtx(), po.getM_InOutLine_ID(), po.get_TrxName());
MInOut inOut = receiptLine.getParent();
boolean isReturnTrx = inOut.getMovementType().equals(X_M_InOut.MOVEMENTTYPE_VendorReturns);
// Create PO Cost Detail Record first
// MZ Goodwill
// Create Cost Detail Matched PO using Total Amount and Total Qty based on OrderLine
MMatchPO[] mPO = MMatchPO.getOrderLine(getCtx(), m_oLine.getC_OrderLine_ID(), po.get_TrxName());
BigDecimal tQty = Env.ZERO;
BigDecimal tAmt = Env.ZERO;
for (int i = 0 ; i < mPO.length ; i++)
{
if (mPO[i].isPosted()
&& mPO[i].getM_AttributeSetInstance_ID() == po.getM_AttributeSetInstance_ID()
&& mPO[i].getM_MatchPO_ID() != get_ID())
{
BigDecimal qty = (isReturnTrx ? mPO[i].getQty().negate() : mPO[i].getQty());
tQty = tQty.add(qty);
tAmt = tAmt.add(poCost.multiply(qty));
}
}
tAmt = tAmt.add(isReturnTrx ? poCost.negate() : poCost);
tQty = tQty.add(isReturnTrx ? po.getQty().negate() : po.getQty());
poCost = poCost.multiply(po.getQty()); // Delivered so far
// Different currency
String costingMethod = as.getCostingMethod();
if (m_oLine.getC_Currency_ID() != as.getC_Currency_ID())
{
MOrder order = m_oLine.getParent();
Timestamp dateAcct = order.getDateAcct();
if (MAcctSchema.COSTINGMETHOD_AveragePO.equals(costingMethod) ||
MAcctSchema.COSTINGMETHOD_LastPOPrice.equals(costingMethod) )
dateAcct = po.getDateAcct(); //Movement Date
BigDecimal rate = MConversionRate.getRate(
order.getC_Currency_ID(), as.getC_Currency_ID(),
dateAcct, order.getC_ConversionType_ID(),
m_oLine.getAD_Client_ID(), m_oLine.getAD_Org_ID());
if (rate == null)
{
return "Purchase Order not convertible - " + as.getName();
}
poCost = poCost.multiply(rate);
if (poCost.scale() > as.getCostingPrecision())
poCost = poCost.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP);
tAmt = tAmt.multiply(rate);
if (tAmt.scale() > as.getCostingPrecision())
tAmt = tAmt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP);
}
// Set Total Amount and Total Quantity from Matched PO
MCostDetail.createOrder(as, m_oLine.getAD_Org_ID(),
po.getM_Product_ID(), po.getM_AttributeSetInstance_ID(),
m_oLine.getC_OrderLine_ID(), 0, // no cost element
tAmt, tQty, // Delivered
m_oLine.getDescription(), po.get_TrxName());
// end MZ
// end
}
return "";
}
Oh dear, with this source, Adempiere tells to me that they converted some value which already converted. It means they converted IDR values to USD and then converted again. Well, its okay. While we have Adempiere source code, we can remove re-converted codes by my self. Creating some fixes and then, we were doing cross check with the createMatchPOCostDetail method codes within Adempiere trunk for M_InOut.
That was good. Fortunately, while comparing our fix codes with the codes within trunk, this codes already fixed. Here is the fixed codes for those methods within Adempiere 352a patches, revision 6135
private String createMatchPOCostDetail(MMatchPO po, MOrderLine m_oLine)
{
// Get Account Schemas to create MCostDetail
MAcctSchema[] acctschemas = MAcctSchema.getClientAcctSchema(getCtx(), getAD_Client_ID());
for(int asn = 0; asn < acctschemas.length; asn++)
{
MAcctSchema as = acctschemas[asn];
boolean skip = false;
if (as.getAD_OrgOnly_ID() != 0)
{
if (as.getOnlyOrgs() == null)
as.setOnlyOrgs(MReportTree.getChildIDs(getCtx(),
0, MAcctSchemaElement.ELEMENTTYPE_Organization,
as.getAD_OrgOnly_ID()));
// Header Level Org
skip = as.isSkipOrg(getAD_Org_ID());
// Line Level Org
skip = as.isSkipOrg(m_oLine.getAD_Org_ID());
}
if (skip)
continue;
// Purchase Order Line
BigDecimal poCost = m_oLine.getPriceCost();
if (poCost == null || poCost.signum() == 0)
poCost = m_oLine.getPriceActual();
// Source from Doc_MatchPO.createFacts(MAcctSchema)
MInOutLine receiptLine = new MInOutLine (getCtx(), po.getM_InOutLine_ID(), po.get_TrxName());
MInOut inOut = receiptLine.getParent();
boolean isReturnTrx = inOut.getMovementType().equals(X_M_InOut.MOVEMENTTYPE_VendorReturns);
// Create PO Cost Detail Record first
// MZ Goodwill
// Create Cost Detail Matched PO using Total Amount and Total Qty based on OrderLine
MMatchPO[] mPO = MMatchPO.getOrderLine(getCtx(), m_oLine.getC_OrderLine_ID(), po.get_TrxName());
BigDecimal tQty = Env.ZERO;
BigDecimal tAmt = Env.ZERO;
for (int i = 0 ; i < mPO.length ; i++)
{
if (mPO[i].isPosted()
&& mPO[i].getM_AttributeSetInstance_ID() == po.getM_AttributeSetInstance_ID()
&& mPO[i].getM_MatchPO_ID() != get_ID())
{
BigDecimal qty = (isReturnTrx ? mPO[i].getQty().negate() : mPO[i].getQty());
tQty = tQty.add(qty);
tAmt = tAmt.add(poCost.multiply(qty));
}
}
poCost = poCost.multiply(po.getQty()); // Delivered so far
tAmt = tAmt.add(isReturnTrx ? poCost.negate() : poCost);
tQty = tQty.add(isReturnTrx ? po.getQty().negate() : po.getQty());
// Different currency
String costingMethod = as.getCostingMethod();
if (m_oLine.getC_Currency_ID() != as.getC_Currency_ID())
{
MOrder order = m_oLine.getParent();
Timestamp dateAcct = order.getDateAcct();
if (MAcctSchema.COSTINGMETHOD_AveragePO.equals(costingMethod) ||
MAcctSchema.COSTINGMETHOD_LastPOPrice.equals(costingMethod) )
dateAcct = po.getDateAcct(); //Movement Date
BigDecimal rate = MConversionRate.getRate(
order.getC_Currency_ID(), as.getC_Currency_ID(),
dateAcct, order.getC_ConversionType_ID(),
m_oLine.getAD_Client_ID(), m_oLine.getAD_Org_ID());
if (rate == null)
{
return "Purchase Order not convertible - " + as.getName();
}
poCost = poCost.multiply(rate);
if (poCost.scale() > as.getCostingPrecision())
poCost = poCost.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP);
tAmt = tAmt.multiply(rate);
if (tAmt.scale() > as.getCostingPrecision())
tAmt = tAmt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP);
}
// Set Total Amount and Total Quantity from Matched PO
MCostDetail.createOrder(as, m_oLine.getAD_Org_ID(),
po.getM_Product_ID(), po.getM_AttributeSetInstance_ID(),
m_oLine.getC_OrderLine_ID(), 0, // no cost element
tAmt, tQty, // Delivered
m_oLine.getDescription(), po.get_TrxName());
// end MZ
// end
}
return "";
}
Thanks to Adempiere community with this fixes of codes. For now, what will we do after rectified this code? You absolutely right, we have to build our patch within customization. (Note: while we can built this codes by our self, you can directly download Adempiere patches for 352a version. As far as I know, Adempiere provides regular weekly patches, which containing the last fixed/improvement codes from Adempiere trunk). Run silent setup to merged this patch/customization within our Adempiere core and test your transaction again.
Okay, it seems working for now. Take a look within below figure

It is enough for now. And hope this helpful.
Recent Comments