IDEMPIERE-5118 MUOMConversion fix and improvements (#1075)
This commit is contained in:
parent
93ebe43cbd
commit
b88a77ffec
|
@ -0,0 +1,23 @@
|
|||
SET SQLBLANKLINES ON
|
||||
SET DEFINE OFF
|
||||
|
||||
-- IDEMPIERE-5118 MUOMConversion fix and improvements
|
||||
-- Dec 21, 2021, 3:31:26 PM MYT
|
||||
UPDATE AD_IndexColumn SET SeqNo=4,Updated=TO_DATE('2021-12-21 15:31:26','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_IndexColumn_ID=200656
|
||||
;
|
||||
|
||||
-- Dec 21, 2021, 3:32:58 PM MYT
|
||||
INSERT INTO AD_IndexColumn (AD_Client_ID,AD_Org_ID,AD_IndexColumn_ID,AD_IndexColumn_UU,Created,CreatedBy,EntityType,IsActive,Updated,UpdatedBy,AD_Column_ID,AD_TableIndex_ID,SeqNo) VALUES (0,0,201444,'9c133f49-0a9e-42ce-a8da-86ed15f7e40a',TO_DATE('2021-12-21 15:32:57','YYYY-MM-DD HH24:MI:SS'),100,'D','Y',TO_DATE('2021-12-21 15:32:57','YYYY-MM-DD HH24:MI:SS'),100,1003,200562,3)
|
||||
;
|
||||
|
||||
-- Dec 21, 2021, 3:33:18 PM MYT
|
||||
DROP INDEX c_uom_conversion_product
|
||||
;
|
||||
|
||||
-- Dec 21, 2021, 3:33:19 PM MYT
|
||||
CREATE UNIQUE INDEX c_uom_conversion_product ON C_UOM_Conversion (C_UOM_ID,C_UOM_To_ID,AD_Client_ID,COALESCE(M_Product_ID,-1))
|
||||
;
|
||||
|
||||
SELECT register_migration_script('202112210900_IDEMPIERE-5118.sql') FROM dual
|
||||
;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
-- IDEMPIERE-5118 MUOMConversion fix and improvements
|
||||
-- Dec 21, 2021, 3:31:26 PM MYT
|
||||
UPDATE AD_IndexColumn SET SeqNo=4,Updated=TO_TIMESTAMP('2021-12-21 15:31:26','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_IndexColumn_ID=200656
|
||||
;
|
||||
|
||||
-- Dec 21, 2021, 3:32:58 PM MYT
|
||||
INSERT INTO AD_IndexColumn (AD_Client_ID,AD_Org_ID,AD_IndexColumn_ID,AD_IndexColumn_UU,Created,CreatedBy,EntityType,IsActive,Updated,UpdatedBy,AD_Column_ID,AD_TableIndex_ID,SeqNo) VALUES (0,0,201444,'9c133f49-0a9e-42ce-a8da-86ed15f7e40a',TO_TIMESTAMP('2021-12-21 15:32:57','YYYY-MM-DD HH24:MI:SS'),100,'D','Y',TO_TIMESTAMP('2021-12-21 15:32:57','YYYY-MM-DD HH24:MI:SS'),100,1003,200562,3)
|
||||
;
|
||||
|
||||
-- Dec 21, 2021, 3:33:18 PM MYT
|
||||
DROP INDEX c_uom_conversion_product
|
||||
;
|
||||
|
||||
-- Dec 21, 2021, 3:33:19 PM MYT
|
||||
CREATE UNIQUE INDEX c_uom_conversion_product ON C_UOM_Conversion (C_UOM_ID,C_UOM_To_ID,AD_Client_ID,COALESCE(M_Product_ID,-1))
|
||||
;
|
||||
|
||||
SELECT register_migration_script('202112210900_IDEMPIERE-5118.sql') FROM dual
|
||||
;
|
||||
|
|
@ -18,7 +18,6 @@ package org.compiere.model;
|
|||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
@ -316,7 +315,7 @@ public class CalloutEngine implements Callout
|
|||
BigDecimal rate2 = Env.ZERO;
|
||||
|
||||
if (rate1.signum() != 0.0) // no divide by zero
|
||||
rate2 = Env.ONE.divide(rate1, 12, RoundingMode.HALF_UP);
|
||||
rate2 = MUOMConversion.getOppositeRate(rate1);
|
||||
//
|
||||
if (mField.getColumnName().equals("MultiplyRate"))
|
||||
mTab.setValue("DivideRate", rate2);
|
||||
|
|
|
@ -449,7 +449,6 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
return retValue;
|
||||
} // convert
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* Convert PRICE expressed in entered UoM to equivalent price in product UoM and round. <br/>
|
||||
* OR Convert QTY in product UOM to qty in entered UoM and round. <br/>
|
||||
|
@ -465,6 +464,26 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
*/
|
||||
static public BigDecimal convertProductTo (Properties ctx,
|
||||
int M_Product_ID, int C_UOM_To_ID, BigDecimal qtyPrice)
|
||||
{
|
||||
return convertProductTo(ctx, M_Product_ID, C_UOM_To_ID, qtyPrice, -1);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Convert PRICE expressed in entered UoM to equivalent price in product UoM and round. <br/>
|
||||
* OR Convert QTY in product UOM to qty in entered UoM and round. <br/>
|
||||
*
|
||||
* eg: $6/6pk => $1/ea <br/>
|
||||
* OR 6 X ea => 1 X 6pk
|
||||
*
|
||||
* @param ctx context
|
||||
* @param M_Product_ID product
|
||||
* @param C_UOM_To_ID entered UOM
|
||||
* @param qtyPrice quantity or price
|
||||
* @param precision Rounding precision, -1 to use precision from UOM
|
||||
* @return Product: Qty/Price (precision rounded)
|
||||
*/
|
||||
static public BigDecimal convertProductTo (Properties ctx,
|
||||
int M_Product_ID, int C_UOM_To_ID, BigDecimal qtyPrice, int precision)
|
||||
{
|
||||
if (qtyPrice == null || qtyPrice.signum() == 0
|
||||
|| M_Product_ID == 0 || C_UOM_To_ID == 0)
|
||||
|
@ -475,11 +494,18 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
{
|
||||
if (Env.ONE.compareTo(retValue) == 0)
|
||||
return qtyPrice;
|
||||
if (precision >= 0)
|
||||
{
|
||||
return retValue.multiply(qtyPrice).setScale(precision, RoundingMode.HALF_UP);
|
||||
}
|
||||
else
|
||||
{
|
||||
MUOM uom = MUOM.get (ctx, C_UOM_To_ID);
|
||||
if (uom != null)
|
||||
return uom.round(retValue.multiply(qtyPrice), true);
|
||||
return retValue.multiply(qtyPrice);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} // convertProductTo
|
||||
|
||||
|
@ -496,6 +522,8 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
{
|
||||
if (M_Product_ID == 0)
|
||||
return null;
|
||||
|
||||
//first check product specific conversion
|
||||
MUOMConversion[] rates = getProductConversions(ctx, M_Product_ID);
|
||||
|
||||
for (int i = 0; i < rates.length; i++)
|
||||
|
@ -505,8 +533,10 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
return rate.getMultiplyRate();
|
||||
}
|
||||
|
||||
List<MUOMConversion> conversions = new Query(ctx, Table_Name, "C_UOM_ID=? AND C_UOM_TO_ID=?", null)
|
||||
.setParameters(MProduct.get(ctx, M_Product_ID).getC_UOM_ID(), C_UOM_To_ID)
|
||||
//fall back to generic conversion
|
||||
List<MUOMConversion> conversions = new Query(ctx, Table_Name, "C_UOM_ID=? AND C_UOM_TO_ID=? AND M_Product_ID IS NULL AND AD_Client_ID IN (0, ?)", null)
|
||||
.setParameters(MProduct.get(ctx, M_Product_ID).getC_UOM_ID(), C_UOM_To_ID, Env.getAD_Client_ID(ctx))
|
||||
.setOrderBy("AD_Client_ID Desc")
|
||||
.setOnlyActiveRecords(true)
|
||||
.list();
|
||||
for (int i = 0; i < conversions.size(); i++)
|
||||
|
@ -533,6 +563,26 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
*/
|
||||
static public BigDecimal convertProductFrom (Properties ctx,
|
||||
int M_Product_ID, int C_UOM_To_ID, BigDecimal qtyPrice)
|
||||
{
|
||||
return convertProductFrom(ctx, M_Product_ID, C_UOM_To_ID, qtyPrice, -1);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Convert PRICE expressed in product UoM to equivalent price in entered UoM and round. <br/>
|
||||
* OR Convert QTY in entered UOM to qty in product UoM and round. <br/>
|
||||
*
|
||||
* eg: $1/ea => $6/6pk <br/>
|
||||
* OR 1 X 6pk => 6 X ea
|
||||
*
|
||||
* @param ctx context
|
||||
* @param M_Product_ID product
|
||||
* @param C_UOM_To_ID entered UOM
|
||||
* @param qtyPrice quantity or price
|
||||
* @param precision Rounding precision, -1 to use precision from UOM
|
||||
* @return Product: Qty/Price (precision rounded)
|
||||
*/
|
||||
static public BigDecimal convertProductFrom (Properties ctx,
|
||||
int M_Product_ID, int C_UOM_To_ID, BigDecimal qtyPrice, int precision)
|
||||
{
|
||||
// No conversion
|
||||
if (qtyPrice == null || qtyPrice.compareTo(Env.ZERO)==0
|
||||
|
@ -547,11 +597,18 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
{
|
||||
if (Env.ONE.compareTo(retValue) == 0)
|
||||
return qtyPrice;
|
||||
if (precision >= 0)
|
||||
{
|
||||
return retValue.multiply(qtyPrice).setScale(precision, RoundingMode.HALF_UP);
|
||||
}
|
||||
else
|
||||
{
|
||||
MUOM uom = MUOM.get (ctx, C_UOM_To_ID);
|
||||
if (uom != null)
|
||||
return uom.round(retValue.multiply(qtyPrice), true);
|
||||
return retValue.multiply(qtyPrice);
|
||||
}
|
||||
}
|
||||
if (s_log.isLoggable(Level.FINE)) s_log.fine("No Rate M_Product_ID=" + M_Product_ID);
|
||||
return null;
|
||||
} // convertProductFrom
|
||||
|
@ -567,6 +624,10 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
static public BigDecimal getProductRateFrom (Properties ctx,
|
||||
int M_Product_ID, int C_UOM_To_ID)
|
||||
{
|
||||
if (M_Product_ID == 0)
|
||||
return null;
|
||||
|
||||
//first, check product specific conversion
|
||||
MUOMConversion[] rates = getProductConversions(ctx, M_Product_ID);
|
||||
|
||||
for (int i = 0; i < rates.length; i++)
|
||||
|
@ -576,8 +637,10 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
return rate.getDivideRate();
|
||||
}
|
||||
|
||||
List<MUOMConversion> conversions = new Query(ctx, Table_Name, "C_UOM_ID=? AND C_UOM_TO_ID=?", null)
|
||||
.setParameters(MProduct.get(ctx, M_Product_ID).getC_UOM_ID(), C_UOM_To_ID)
|
||||
//fall back to generic conversion
|
||||
List<MUOMConversion> conversions = new Query(ctx, Table_Name, "C_UOM_ID=? AND C_UOM_TO_ID=? AND M_Product_ID IS NULL AND AD_Client_ID IN (0, ?)", null)
|
||||
.setParameters(MProduct.get(ctx, M_Product_ID).getC_UOM_ID(), C_UOM_To_ID, Env.getAD_Client_ID(ctx))
|
||||
.setOrderBy("AD_Client_ID Desc")
|
||||
.setOnlyActiveRecords(true)
|
||||
.list();
|
||||
for (int i = 0; i < conversions.size(); i++)
|
||||
|
@ -746,6 +809,18 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
log.saveError("Error", Msg.parseTranslation(getCtx(), "@C_UOM_ID@ = @C_UOM_To_ID@"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getMultiplyRate() != null && getMultiplyRate().signum() != 0)
|
||||
{
|
||||
if (getDivideRate() == null || getDivideRate().signum() == 0)
|
||||
setDivideRate(getOppositeRate(getMultiplyRate()));
|
||||
}
|
||||
else if (getDivideRate() != null && getDivideRate().signum() != 0)
|
||||
{
|
||||
if (getMultiplyRate() == null || getMultiplyRate().signum() == 0)
|
||||
setMultiplyRate(getOppositeRate(getDivideRate()));
|
||||
}
|
||||
|
||||
// Nothing to convert
|
||||
if (getMultiplyRate().compareTo(Env.ZERO) <= 0)
|
||||
{
|
||||
|
@ -807,4 +882,12 @@ public class MUOMConversion extends X_C_UOM_Conversion implements ImmutablePOSup
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate opposite conversion rate, i.e calculate divide rate for multiply rate and vice versa.
|
||||
* @param rate
|
||||
* @return {@link BigDecimal}
|
||||
*/
|
||||
public static BigDecimal getOppositeRate(BigDecimal rate) {
|
||||
return Env.ONE.divide(rate, 12, RoundingMode.HALF_UP);
|
||||
}
|
||||
} // UOMConversion
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/***********************************************************************
|
||||
* This file is part of iDempiere ERP Open Source *
|
||||
* http://www.idempiere.org *
|
||||
* *
|
||||
* Copyright (C) Contributors *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* 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 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., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301, USA. *
|
||||
* *
|
||||
* Contributors: *
|
||||
* - hengsin *
|
||||
**********************************************************************/
|
||||
package org.idempiere.test.model;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import org.compiere.model.MUOM;
|
||||
import org.compiere.model.MUOMConversion;
|
||||
import org.compiere.model.PO;
|
||||
import org.compiere.util.CacheMgt;
|
||||
import org.compiere.util.DB;
|
||||
import org.compiere.util.Env;
|
||||
import org.idempiere.test.AbstractTestCase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
public class MUOMConversionTest extends AbstractTestCase {
|
||||
|
||||
private final static int EACH_ID = 100;
|
||||
private final static int HOUR_ID = 101;
|
||||
private static final int PRODUCT_OAK_TREE = 123;
|
||||
|
||||
public MUOMConversionTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConversion() {
|
||||
|
||||
MUOM each = new MUOM(Env.getCtx(), EACH_ID, getTrxName());
|
||||
MUOM hour = new MUOM(Env.getCtx(), HOUR_ID, getTrxName());
|
||||
|
||||
//conversion1 at system level
|
||||
MUOMConversion conv1 = new MUOMConversion(each);
|
||||
conv1.set_TrxName(null);
|
||||
conv1.setC_UOM_To_ID(HOUR_ID);
|
||||
conv1.setMultiplyRate(new BigDecimal("1.15"));
|
||||
conv1.setDivideRate(BigDecimal.ZERO);
|
||||
try {
|
||||
PO.setCrossTenantSafe();
|
||||
conv1.saveEx();
|
||||
} finally {
|
||||
PO.clearCrossTenantSafe();
|
||||
}
|
||||
|
||||
MUOMConversion conv2 = null;
|
||||
MUOMConversion conv3 = null;
|
||||
try {
|
||||
BigDecimal converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"));
|
||||
assertEquals(new BigDecimal("1.15"), converted);
|
||||
converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"), -1);
|
||||
assertEquals(new BigDecimal("1.15"), converted);
|
||||
converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"), 1);
|
||||
assertEquals(new BigDecimal("1.2"), converted);
|
||||
|
||||
//conversion2 at tenant level
|
||||
conv2 = new MUOMConversion(Env.getCtx(), 0, null);
|
||||
conv2.setC_UOM_ID(EACH_ID);
|
||||
conv2.setC_UOM_To_ID(HOUR_ID);
|
||||
conv2.setMultiplyRate(new BigDecimal("1.35"));
|
||||
conv2.saveEx();
|
||||
|
||||
converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"));
|
||||
assertEquals(new BigDecimal("1.35"), converted);
|
||||
converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"), -1);
|
||||
assertEquals(new BigDecimal("1.35"), converted);
|
||||
converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"), 1);
|
||||
assertEquals(new BigDecimal("1.4"), converted);
|
||||
|
||||
//conversion3 at tenant and product level
|
||||
conv3 = new MUOMConversion(Env.getCtx(), 0, null);
|
||||
conv3.setM_Product_ID(PRODUCT_OAK_TREE);
|
||||
conv3.setC_UOM_ID(EACH_ID);
|
||||
conv3.setC_UOM_To_ID(HOUR_ID);
|
||||
conv3.setMultiplyRate(new BigDecimal("0.75"));
|
||||
conv3.saveEx();
|
||||
CacheMgt.get().reset();
|
||||
|
||||
converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"));
|
||||
assertEquals(new BigDecimal("0.75"), converted);
|
||||
converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"), -1);
|
||||
assertEquals(new BigDecimal("0.75"), converted);
|
||||
converted = MUOMConversion.convertProductTo(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"), 1);
|
||||
assertEquals(new BigDecimal("0.8"), converted);
|
||||
|
||||
converted = MUOMConversion.convertProductFrom(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"));
|
||||
assertEquals(hour.round(conv3.getDivideRate(),true), converted);
|
||||
|
||||
conv3.deleteEx(true);
|
||||
conv3 = null;
|
||||
CacheMgt.get().reset();
|
||||
converted = MUOMConversion.convertProductFrom(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"));
|
||||
assertEquals(hour.round(conv2.getDivideRate(),true), converted);
|
||||
converted = MUOMConversion.convertProductFrom(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"), 1);
|
||||
assertEquals(conv2.getDivideRate().setScale(1, RoundingMode.HALF_UP), converted);
|
||||
|
||||
conv2.deleteEx(true);
|
||||
conv2 = null;
|
||||
converted = MUOMConversion.convertProductFrom(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"));
|
||||
assertEquals(hour.round(conv1.getDivideRate(),true), converted);
|
||||
converted = MUOMConversion.convertProductFrom(Env.getCtx(), PRODUCT_OAK_TREE, HOUR_ID, new BigDecimal("1"), 1);
|
||||
assertEquals(conv1.getDivideRate().setScale(1, RoundingMode.HALF_UP), converted);
|
||||
} finally {
|
||||
DB.executeUpdateEx("DELETE FROM C_UOM_Conversion WHERE C_UOM_Conversion_ID=?", new Object[] {conv1.get_ID()}, null);
|
||||
if (conv2 != null)
|
||||
conv2.deleteEx(true);
|
||||
if (conv3 != null)
|
||||
conv3.deleteEx(true);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue