Unit Conversion

I’m running into an issue with unit conversion on BOM pricing. There’s two issues, really.

I have a component product (CompA) which is priced per ton (Unit = Ton, Cost Price = 200).
I have a finished goods product (ProdA), and have created a BOM for it. This BOM contains 500 Pounds of CompA.
Additionally, I have two unit conversions set up: Tons = 2000 X Pounds, and Pounds = 0.0005 X Tons

When I calculate price, the code is taking my Price Per Ton ($200 / 1 Ton) and multiplying it by the quantity of my component (500 Pound). This returns a value of $100,000. Then, it applies the Ton to Pounds conversion, multiplying that value by a further 2,000, resulting in a final computed price of $200,000,000, when it should be a mere $50. This is easy enough to fix. Instead of doing:

BigDecimal costPrice = unitConversionService.convert(product.getUnit(), unit,
        product.getCostPrice().multiply(consumptionQty));

we should be doing this:

BigDecimal costPrice = product.getCostPrice().multiply(unitConversionService.convert(
        unit, product.getUnit(), consumptionQty, appProductionService.getNbDecimalDigitForBomQty()));

In effect, we should be converting the quantity first into the unit that the product defines its price in (alternatively, we should be dividing by the coefficient, not multiplying, but that’s nested down in the unitConversionService, which we don’t want to handle this). However, this leads to a second problem. The coefficient to convert Pounds to Tons (0.0005) is returned in a BigDecimal with a scale of 3, so we are truncating the coefficient to be 0.000. The model correctly defines the scale as being 12, and the database stores the coefficient correctly with a scale of 12. Further, the Unit Conversions page correctly displays the coefficient as 0.000500000000. I’ve tried removing my Pound to Ton conversion (so the unit converter will invert the Ton to Pound conversion), but I run into the same issue.

The problem I need help with is determining where the coefficient is being truncated to a scale of only 3. The UnitConversionRepo already has the coefficient stored with a scale of 3 (which incorrectly truncates my coefficient), and the UnitConversionService#getInverseCoefficientScale is also not returning a large enough value.


Edit:

I filed two tickets to cover the incorrect scale calculation and the incorrect unit price conversion. The solution to my above issues can be found below in my reply, or in the respective tickets.

Update: I figured out the problem. It wasn’t actually a problem with the storage of the UnitConversions. The problem lies in the inverse conversion functions within the UnitConversionService.

The UnitConversionService determines scale for an inverse coefficient like this:

(int) Math.log10(unitConversion.getCoef().intValue());

Which for a value of 2000 will return a scale of 3. However, the inverse of 2000 is 0.0005, which is a scale of 4. The problem is that Math.log10(2000) returns 3.30103 which, when cast to an int, is truncated to 3. Instead, UnitConversionService should be doing this:

(int) Math.ceil(Math.log10(unitConversion.getCoef().intValue()));

Note the addition of Math.ceil(...), which will round 3.30103 up to 4, giving us the correct scale. On the border cases (i.e., Math.log10(100)), we will pass a whole integer to Math.ceil(...), which will return the original integer.

The reason this issue arose in my case is because the getCoefficient(...) method in UnitConversionService stops as soon as it reaches a UnitConversion where both units on the conversion match the parameter units. As such, it is finding my Ton -> Pound conversion before my Pound -> Ton conversion, and inverting it, which uncovers the bug regarding the incorrect scale calculation. I have not tried removing my Ton -> Pound conversion to see if the coefficient for my Pound -> Ton is being properly scaled or not. I leave this to another exercise.