Commit 7a882794 by Juho Juopperi

Merge branch 'billrounding' into 'master'

Bill rounding

Make product price scale a constant in Bill. We want to calculate all product prices with 4 decimal precision, just in case. And to make a reference quote for future generations: "This should be enough for everyone". We still want to display the price to users with scale 2, but the scaling should be done at the view level, after all calculations have been made.

This commit does not have any functional changes because all data going through database has scale set already to 4. This just makes the value constant and adds some comments to remind that the scale and rounding mode have real world impact..

By default BigDecimal has a huge scale, which causes BigDecimal to act like a float and these become true:

  * new BigDecimal(555.55) -> 555.549999999999954525264911353588104248046875
  * 55554 == new BigDecimal(555.55).multiply(new BigDecimal(100)).intValue();

See merge request !260
2 parents 2c43b839 095c260d
...@@ -24,6 +24,7 @@ package fi.codecrew.moya.beans; ...@@ -24,6 +24,7 @@ package fi.codecrew.moya.beans;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
...@@ -173,7 +174,7 @@ public class PlaceBean implements PlaceBeanLocal { ...@@ -173,7 +174,7 @@ public class PlaceBean implements PlaceBeanLocal {
Map<Product, Integer> mockmap = getPlaceProductcount(places); Map<Product, Integer> mockmap = getPlaceProductcount(places);
BigDecimal total = BigDecimal.ZERO; BigDecimal total = Bill.BILL_SCALED_ZERO_PRICE;
Calendar now = Calendar.getInstance(); Calendar now = Calendar.getInstance();
for (Entry<Product, Integer> entry : mockmap.entrySet()) { for (Entry<Product, Integer> entry : mockmap.entrySet()) {
...@@ -182,7 +183,7 @@ public class PlaceBean implements PlaceBeanLocal { ...@@ -182,7 +183,7 @@ public class PlaceBean implements PlaceBeanLocal {
total = total.add(productBean.calculateTotal(entry.getKey(), new BigDecimal(entry.getValue()), now, user)); total = total.add(productBean.calculateTotal(entry.getKey(), new BigDecimal(entry.getValue()), now, user));
} }
} }
return total; return total.setScale(Bill.BILL_PRICE_SCALE, RoundingMode.HALF_UP);
} }
private static Map<Product, Integer> getPlaceProductcount(Collection<Place> places) { private static Map<Product, Integer> getPlaceProductcount(Collection<Place> places) {
...@@ -479,7 +480,6 @@ public class PlaceBean implements PlaceBeanLocal { ...@@ -479,7 +480,6 @@ public class PlaceBean implements PlaceBeanLocal {
return placeFacade.setBuyable(map, like, b); return placeFacade.setBuyable(map, like, b);
} }
/** /**
* Release reservation from user * Release reservation from user
* *
...@@ -724,8 +724,6 @@ public class PlaceBean implements PlaceBeanLocal { ...@@ -724,8 +724,6 @@ public class PlaceBean implements PlaceBeanLocal {
return placeFacade.getMapProducts(map); return placeFacade.getMapProducts(map);
} }
@Override @Override
public List<PlaceSlot> getFreePlaceslots(EventUser user, EventMap map) { public List<PlaceSlot> getFreePlaceslots(EventUser user, EventMap map) {
user = eventUserFacade.reload(user); user = eventUserFacade.reload(user);
......
...@@ -42,7 +42,6 @@ import javax.persistence.Table; ...@@ -42,7 +42,6 @@ import javax.persistence.Table;
public class BillLine extends GenericEntity { public class BillLine extends GenericEntity {
private static final long serialVersionUID = 2L; private static final long serialVersionUID = 2L;
private static final BigDecimal DEFAULT_VAT = BigDecimal.ZERO;
/** /**
* Which bill this bill line belongs to * Which bill this bill line belongs to
*/ */
...@@ -67,8 +66,8 @@ public class BillLine extends GenericEntity { ...@@ -67,8 +66,8 @@ public class BillLine extends GenericEntity {
* How much one(1) unit of this product costs * How much one(1) unit of this product costs
* *
*/ */
@Column(name = "unit_price", nullable = false, precision = 24, scale = 4) @Column(name = "unit_price", nullable = false, precision = 24, scale = Bill.BILL_PRICE_SCALE)
private BigDecimal unitPrice = BigDecimal.ZERO; private BigDecimal unitPrice = Bill.BILL_SCALED_ZERO_PRICE;
/** /**
* *
...@@ -79,8 +78,8 @@ public class BillLine extends GenericEntity { ...@@ -79,8 +78,8 @@ public class BillLine extends GenericEntity {
/** /**
* How much VAT this product contains ( 0, 0.22 ) etc * How much VAT this product contains ( 0, 0.22 ) etc
*/ */
@Column(name = "vat", nullable = false, precision = 4, scale = 3) @Column(name = "vat", nullable = false, precision = 4, scale = Bill.VAT_SCALE)
private BigDecimal vat = DEFAULT_VAT; private BigDecimal vat = Bill.VAT_SCALED_ZERO;
@JoinColumn(name = "lineProduct_id", referencedColumnName = "id", nullable = true, updatable = false) @JoinColumn(name = "lineProduct_id", referencedColumnName = "id", nullable = true, updatable = false)
@OneToOne @OneToOne
...@@ -111,7 +110,7 @@ public class BillLine extends GenericEntity { ...@@ -111,7 +110,7 @@ public class BillLine extends GenericEntity {
*/ */
public BigDecimal getLinePriceVatless() { public BigDecimal getLinePriceVatless() {
BigDecimal vatMultiplicand = BigDecimal.ONE.add(getVat()); BigDecimal vatMultiplicand = BigDecimal.ONE.add(getVat());
return getLinePrice().divide(vatMultiplicand, 2, RoundingMode.HALF_UP); return getLinePrice().divide(vatMultiplicand, Bill.BILL_PRICE_SCALE, RoundingMode.HALF_UP);
} }
public BillLine() { public BillLine() {
...@@ -157,8 +156,8 @@ public class BillLine extends GenericEntity { ...@@ -157,8 +156,8 @@ public class BillLine extends GenericEntity {
public BillLine(Bill bill2, Product product, Discount disc, BigDecimal count) { public BillLine(Bill bill2, Product product, Discount disc, BigDecimal count) {
super(); super();
this.bill = bill2; this.bill = bill2;
BigDecimal unitPrice = product.getPrice().subtract(product.getPrice().multiply(disc.getPercentage())).negate().setScale(2, RoundingMode.HALF_UP); BigDecimal unitPrice = product.getPrice().subtract(product.getPrice().multiply(disc.getPercentage())).negate().setScale(Bill.BILL_PRICE_SCALE, RoundingMode.HALF_UP);
this.name = disc.getShortdesc(); this.name = disc.getShortdesc();
this.unitName = product.getUnitName(); this.unitName = product.getUnitName();
this.quantity = count; this.quantity = count;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
package fi.codecrew.moya.model; package fi.codecrew.moya.model;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
...@@ -45,13 +46,15 @@ public class Discount extends GenericEntity { ...@@ -45,13 +46,15 @@ public class Discount extends GenericEntity {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String EVENT_ID = "event_id"; private static final String EVENT_ID = "event_id";
private static final BigDecimal SCALE4_ZERO = BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP);
private static final BigDecimal SCALE6_ZERO = BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP);
@ManyToOne() @ManyToOne()
@JoinColumn(name = EVENT_ID, nullable = false) @JoinColumn(name = EVENT_ID, nullable = false)
private LanEvent event; private LanEvent event;
@Column(name = "percentage", nullable = false, precision = 9, scale = 6) @Column(name = "percentage", nullable = false, precision = 9, scale = 6)
private BigDecimal percentage = BigDecimal.ZERO; private BigDecimal percentage = SCALE6_ZERO;
@Column(name = "code") @Column(name = "code")
private String code; private String code;
...@@ -71,22 +74,22 @@ public class Discount extends GenericEntity { ...@@ -71,22 +74,22 @@ public class Discount extends GenericEntity {
private String shortdesc; private String shortdesc;
@Column(name = "amount_min", nullable = false, precision = 24, scale = 4) @Column(name = "amount_min", nullable = false, precision = 24, scale = 4)
private BigDecimal amountMin = BigDecimal.ZERO; private BigDecimal amountMin = SCALE4_ZERO;
@Column(name = "amount_max", nullable = false, precision = 24, scale = 4) @Column(name = "amount_max", nullable = false, precision = 24, scale = 4)
private BigDecimal amountMax = BigDecimal.ZERO; private BigDecimal amountMax = SCALE4_ZERO;
@Column(name = "active", nullable = false) @Column(name = "active", nullable = false)
private boolean active = false; private boolean active = false;
@Column(name = "max_num", nullable = false, precision = 24, scale = 4) @Column(name = "max_num", nullable = false, precision = 24, scale = 4)
private BigDecimal maxNum = BigDecimal.ZERO; private BigDecimal maxNum = SCALE4_ZERO;
@Column(name = "per_user", nullable = false, precision = 24, scale = 4) @Column(name = "per_user", nullable = false, precision = 24, scale = 4)
private BigDecimal perUser = BigDecimal.ZERO; private BigDecimal perUser = SCALE4_ZERO;
@Column(name = "total_count", nullable = false, precision = 24, scale = 4) @Column(name = "total_count", nullable = false, precision = 24, scale = 4)
private BigDecimal totalCount = BigDecimal.ZERO; private BigDecimal totalCount = SCALE4_ZERO;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "discount") @OneToMany(cascade = CascadeType.ALL, mappedBy = "discount")
private List<DiscountInstance> discountInstances; private List<DiscountInstance> discountInstances;
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!