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);
......
...@@ -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;
...@@ -53,381 +54,402 @@ import fi.codecrew.moya.utilities.BillUtils; ...@@ -53,381 +54,402 @@ import fi.codecrew.moya.utilities.BillUtils;
@Table(name = "bills", uniqueConstraints = { @UniqueConstraint(columnNames = { Bill.EVENT_ID_COLUMN, "bill_number" }) }) @Table(name = "bills", uniqueConstraints = { @UniqueConstraint(columnNames = { Bill.EVENT_ID_COLUMN, "bill_number" }) })
public class Bill extends GenericEntity { public class Bill extends GenericEntity {
/** /**
* <p>
* With how many decimals we want to calculate prices, discounts, etc. If we
* use default scale we will hit rounding problems at some point. By default
* BigDecimal acts as a float and So we get this to be true. Even if we
* calculate prices with scale '4', we most likely want to display the
* prices to users with scale '2'
* <p>
* <p>
* NOTICE! This value exists also in database. If changed, remember to also
* modify the database
* </p>
* <h2>And for future generations, an undying quote</h2> <cite> BigDecimal
* scale 4 should be enough for everybody. - Tuomari<cite>
*/
public static final int BILL_PRICE_SCALE = 4;
public static final BigDecimal BILL_SCALED_ZERO_PRICE = BigDecimal.ZERO.setScale(Bill.BILL_PRICE_SCALE, RoundingMode.HALF_UP);
public static final int VAT_SCALE = 3;
public static final BigDecimal VAT_SCALED_ZERO = BigDecimal.ZERO.setScale(VAT_SCALE, RoundingMode.HALF_UP);
/**
* *
*/ */
private static final long serialVersionUID = -6713643278149221869L; private static final long serialVersionUID = -6713643278149221869L;
public static final String EVENT_ID_COLUMN = "event_id"; public static final String EVENT_ID_COLUMN = "event_id";
@ManyToOne()
@JoinColumn(name = EVENT_ID_COLUMN, nullable = false)
private LanEvent event;
/**
* When the bill is due to be paid.
*/
// Can be calculated from SentDate + paymentTime
// @Column(name = "due_date")
// @Temporal(TemporalType.TIMESTAMP)
// private Calendar dueDate;
/**
* When the money has appeared on the bank account.
*/
@Column(name = "paid_date")
@Temporal(TemporalType.TIMESTAMP)
private Date paidDate;
/**
* The bill number from which the reference number will be generated
*
* @see http://www.fkl.fi/www/page/fk_www_1293
*
*/
@Column(name = "bill_number")
private Integer billNumber;
/**
* Address where the bill should be sent;
*/
private String addr1;
private String addr2;
private String addr3;
private String addr4;
private String addr5;
@Column(nullable = false, name = "sent_time")
@Temporal(TemporalType.TIMESTAMP)
private Calendar sentDate = Calendar.getInstance();
@Column(name = "payment_time", nullable = false)
private Integer paymentTime = 0;
@Column(name = "notice_days", nullable = false)
private String noticetime = "8 vrk";
@Column(name = "their_reference", nullable = false)
private String theirReference = "";
@Column(name = "our_reference", nullable = false)
private String ourReference = "";
@Column(name = "delivery_terms", nullable = false)
private String deliveryTerms = "";
@Column(name = "delay_intrest", nullable = false)
private Integer delayIntrest = 11;
@Column(name = "expires", nullable = true)
@Temporal(TemporalType.TIMESTAMP)
private Calendar expires = null;
/**
* Notes for the event organisators about the bill.
*/
@Lob
@Column(name = "notes")
private String notes;
/**
* Bill may have multiple items on multiple rows.
*/
@OrderBy("id")
// @PrivateOwned
@OneToMany(mappedBy = "bill", cascade = CascadeType.ALL)
private List<BillLine> billLines = new ArrayList<BillLine>();
@ManyToOne() public boolean isPaid()
@JoinColumn(name = EVENT_ID_COLUMN, nullable = false) {
private LanEvent event; return accountEvent != null || paidDate != null;
}
/** /**
* When the bill is due to be paid. * When the bill is paid this AccountEvent is created and this is a
*/ * reference to that accountAction. if this bill
// Can be calculated from SentDate + paymentTime */
// @Column(name = "due_date") @JoinColumn(name = "account_event_id", referencedColumnName = "id", updatable = false)
// @Temporal(TemporalType.TIMESTAMP) @OneToOne
// private Calendar dueDate; private AccountEvent accountEvent;
/**
* User who should pay this bill.
*/
@ManyToOne(optional = false)
@JoinColumn(updatable = false, name = "eventuser_id")
private EventUser user;
@SuppressWarnings("unused")
private static final Logger logger = LoggerFactory.getLogger(Bill.class);
public Integer getReferenceNumberBase() {
if (getEvent() != null && getEvent().getReferenceNumberBase() != null && getBillNumber() != null) {
return getEvent().getReferenceNumberBase() + getBillNumber();
}
return null;
}
/** public Integer getReferenceNumber()
* When the money has appeared on the bank account. {
*/ return BillUtils.createReferenceNumber(getReferenceNumberBase());
@Column(name = "paid_date") }
@Temporal(TemporalType.TIMESTAMP)
private Date paidDate;
/** /**
* The bill number from which the reference number will be generated * Commodity function to calculate the total price of the bill.
* *
* @see http://www.fkl.fi/www/page/fk_www_1293 * @return The total sum of the bill ( unitPrice * units * vat )
* */
*/ public BigDecimal totalPrice() {
@Column(name = "bill_number") BigDecimal total = Bill.BILL_SCALED_ZERO_PRICE;
private Integer billNumber; for (BillLine line : getBillLines()) {
total = total.add(line.getLinePrice());
}
return total;
}
/** public BigDecimal getTotalPrice() {
* Address where the bill should be sent; return this.totalPrice();
*/ }
private String addr1;
private String addr2;
private String addr3;
private String addr4;
private String addr5;
@Column(nullable = false, name = "sent_time")
@Temporal(TemporalType.TIMESTAMP)
private Calendar sentDate = Calendar.getInstance();
@Column(name = "payment_time", nullable = false)
private Integer paymentTime = 0;
@Column(name = "notice_days", nullable = false)
private String noticetime = "8 vrk";
@Column(name = "their_reference", nullable = false)
private String theirReference = "";
@Column(name = "our_reference", nullable = false)
private String ourReference = "";
@Column(name = "delivery_terms", nullable = false)
private String deliveryTerms = "";
@Column(name = "delay_intrest", nullable = false)
private Integer delayIntrest = 11;
@Column(name = "expires", nullable = true)
@Temporal(TemporalType.TIMESTAMP)
private Calendar expires = null;
/**
* Notes for the event organisators about the bill.
*/
@Lob
@Column(name = "notes")
private String notes;
/** /**
* Bill may have multiple items on multiple rows. * Commodity function to calculate the total price of the bill.
*/ *
* @return The total sum of the bill ( unitPrice * units * vat )
*/
public BigDecimal totalVat() {
BigDecimal total = Bill.BILL_SCALED_ZERO_PRICE;
for (BillLine line : getBillLines()) {
total = total.add(line.getLineVat());
}
return total;
}
@OrderBy("id") /**
// @PrivateOwned * Commodity function to return the vatless price of the bill
@OneToMany(mappedBy = "bill", cascade = CascadeType.ALL) *
private List<BillLine> billLines = new ArrayList<BillLine>(); * @return The total VAT-less sum of the bill ( unitPrice * units )
*/
public BigDecimal totalPriceVatless() {
BigDecimal total = Bill.BILL_SCALED_ZERO_PRICE;
for (BillLine line : getBillLines()) {
total = total.add(line.getLinePriceVatless());
}
return total;
}
public boolean isPaid() public Bill(LanEvent event, EventUser user, Calendar expires) {
{ this(event, expires);
return accountEvent != null || paidDate != null; this.setUser(user);
} this.setAddr1(user.getUser().getFirstnames() + " " + user.getUser().getLastname());
this.setAddr2(user.getUser().getAddress());
this.setAddr3(user.getUser().getZip() + " " + user.getUser().getTown());
}
/** public Bill(LanEvent event, Calendar expires) {
* When the bill is paid this AccountEvent is created and this is a this();
* reference to that accountAction. if this bill this.expires = expires;
*/ this.event = event;
@JoinColumn(name = "account_event_id", referencedColumnName = "id", updatable = false) }
@OneToOne
private AccountEvent accountEvent;
/** public Bill(LanEvent event, EventUser user, long expireTimeHours) {
* User who should pay this bill. this(event, user, Calendar.getInstance());
*/ this.expires.setTimeInMillis((System.currentTimeMillis() + (expireTimeHours * 60 * 60 * 1000)));
@ManyToOne(optional = false) }
@JoinColumn(updatable = false, name = "eventuser_id")
private EventUser user; public Bill(LanEvent event, long expireTimeHours) {
this(event, Calendar.getInstance());
@SuppressWarnings("unused") this.expires.setTimeInMillis((System.currentTimeMillis() + (expireTimeHours * 60 * 60 * 1000)));
private static final Logger logger = LoggerFactory.getLogger(Bill.class); }
public Integer getReferenceNumberBase() { public Bill() {
if (getEvent() != null && getEvent().getReferenceNumberBase() != null && getBillNumber() != null) { super();
return getEvent().getReferenceNumberBase() + getBillNumber();
} this.expires = Calendar.getInstance();
return null; this.expires.setTimeInMillis((System.currentTimeMillis() + 1209600000)); // 2vk
} }
public Integer getReferenceNumber() public Calendar getDueDate() {
{
return BillUtils.createReferenceNumber(getReferenceNumberBase()); Calendar dueDate = Calendar.getInstance();
} dueDate.setTime(this.getSentDate().getTime());
dueDate.add(Calendar.DATE, this.getPaymentTime());
/** return dueDate;
* Commodity function to calculate the total price of the bill. }
*
* @return The total sum of the bill ( unitPrice * units * vat ) public String getNotes() {
*/ return notes;
public BigDecimal totalPrice() { }
BigDecimal total = BigDecimal.ZERO;
for (BillLine line : getBillLines()) { public void setNotes(String notes) {
total = total.add(line.getLinePrice()); this.notes = notes;
} }
return total;
} public List<BillLine> getBillLines() {
return billLines;
public BigDecimal getTotalPrice() { }
return this.totalPrice();
} public void setBillLines(List<BillLine> billLineList) {
this.billLines = billLineList;
/** }
* Commodity function to calculate the total price of the bill.
* public AccountEvent getAccountEvent() {
* @return The total sum of the bill ( unitPrice * units * vat ) return accountEvent;
*/ }
public BigDecimal totalVat() {
BigDecimal total = BigDecimal.ZERO; public void setAccountEvent(AccountEvent accoutEventsId) {
for (BillLine line : getBillLines()) { this.accountEvent = accoutEventsId;
total = total.add(line.getLineVat()); }
}
return total; public EventUser getUser() {
} return user;
}
/**
* Commodity function to return the vatless price of the bill public void setUser(EventUser usersId) {
* this.user = usersId;
* @return The total VAT-less sum of the bill ( unitPrice * units ) }
*/
public BigDecimal totalPriceVatless() { public Integer getBillNumber() {
BigDecimal total = BigDecimal.ZERO; return billNumber;
for (BillLine line : getBillLines()) { }
total = total.add(line.getLinePriceVatless());
} public String getAddr1() {
return total; return addr1;
} }
public Bill(LanEvent event, EventUser user, Calendar expires) { public void setAddr1(String addr1) {
this(event, expires); this.addr1 = addr1;
this.setUser(user); }
this.setAddr1(user.getUser().getFirstnames() + " " + user.getUser().getLastname());
this.setAddr2(user.getUser().getAddress()); public String getAddr2() {
this.setAddr3(user.getUser().getZip() + " " + user.getUser().getTown()); return addr2;
} }
public Bill(LanEvent event, Calendar expires) { public void setAddr2(String addr2) {
this(); this.addr2 = addr2;
this.expires = expires; }
this.event = event;
} public String getAddr3() {
return addr3;
public Bill(LanEvent event, EventUser user, long expireTimeHours) { }
this(event, user, Calendar.getInstance());
this.expires.setTimeInMillis((System.currentTimeMillis() + (expireTimeHours*60*60 * 1000 ))); public void setAddr3(String addr3) {
} this.addr3 = addr3;
}
public Bill(LanEvent event, long expireTimeHours) {
this(event, Calendar.getInstance()); public String getAddr4() {
this.expires.setTimeInMillis((System.currentTimeMillis() + (expireTimeHours*60*60 * 1000 ))); return addr4;
} }
public Bill() { public void setAddr4(String addr4) {
super(); this.addr4 = addr4;
}
this.expires = Calendar.getInstance();
this.expires.setTimeInMillis((System.currentTimeMillis() + 1209600000)); // 2vk public String getAddr5() {
} return addr5;
}
public Calendar getDueDate() {
public void setAddr5(String addr5) {
Calendar dueDate = Calendar.getInstance(); this.addr5 = addr5;
dueDate.setTime(this.getSentDate().getTime()); }
dueDate.add(Calendar.DATE, this.getPaymentTime());
return dueDate; public String getNoticetime() {
} return noticetime;
}
public String getNotes() {
return notes; public void setNoticetime(String noticetime) {
} this.noticetime = noticetime;
}
public void setNotes(String notes) {
this.notes = notes; public String getOurReference() {
} return ourReference;
}
public List<BillLine> getBillLines() {
return billLines; public void setOurReference(String ourReference) {
} this.ourReference = ourReference;
}
public void setBillLines(List<BillLine> billLineList) {
this.billLines = billLineList; public void setTheirReference(String theirReference) {
} this.theirReference = theirReference;
}
public AccountEvent getAccountEvent() {
return accountEvent; public String getTheirReference() {
} return theirReference;
}
public void setAccountEvent(AccountEvent accoutEventsId) {
this.accountEvent = accoutEventsId; public void setDeliveryTerms(String deliveryTerms) {
} this.deliveryTerms = deliveryTerms;
}
public EventUser getUser() {
return user; public String getDeliveryTerms() {
} return deliveryTerms;
}
public void setUser(EventUser usersId) {
this.user = usersId; public void setPaymentTime(Integer paymentTime) {
} this.paymentTime = paymentTime;
}
public Integer getBillNumber() {
return billNumber; public Integer getPaymentTime() {
} return paymentTime;
}
public String getAddr1() {
return addr1; public void setDelayIntrest(Integer delayIntrest) {
} this.delayIntrest = delayIntrest;
}
public void setAddr1(String addr1) {
this.addr1 = addr1; public Integer getDelayIntrest() {
} return delayIntrest;
}
public String getAddr2() {
return addr2; public void setBillNumber(Integer billNumber) {
} this.billNumber = billNumber;
}
public void setAddr2(String addr2) {
this.addr2 = addr2; public LanEvent getEvent() {
} return event;
}
public String getAddr3() {
return addr3; public void setEvent(LanEvent event) {
} this.event = event;
}
public void setAddr3(String addr3) {
this.addr3 = addr3; public Date getPaidDate() {
} return paidDate;
}
public String getAddr4() {
return addr4; public void setPaidDate(Date paidDate) {
} if (paidDate != null)
expires = null;
public void setAddr4(String addr4) {
this.addr4 = addr4; this.paidDate = paidDate;
} }
public String getAddr5() { public Date getSentDateTime()
return addr5; {
} Date ret = null;
if (sentDate != null)
public void setAddr5(String addr5) { {
this.addr5 = addr5; ret = sentDate.getTime();
} }
return ret;
public String getNoticetime() { }
return noticetime;
} public void setSentDateTime(Date date)
{
public void setNoticetime(String noticetime) { if (date == null)
this.noticetime = noticetime; {
} sentDate = null;
} else {
public String getOurReference() { if (sentDate == null)
return ourReference; {
} sentDate = Calendar.getInstance();
}
public void setOurReference(String ourReference) { sentDate.setTime(date);
this.ourReference = ourReference; }
} }
public void setTheirReference(String theirReference) { public Calendar getSentDate() {
this.theirReference = theirReference; return sentDate;
} }
public String getTheirReference() { public void setSentDate(Calendar sentDate) {
return theirReference; this.sentDate = sentDate;
} }
public void setDeliveryTerms(String deliveryTerms) {
this.deliveryTerms = deliveryTerms;
}
public String getDeliveryTerms() {
return deliveryTerms;
}
public void setPaymentTime(Integer paymentTime) {
this.paymentTime = paymentTime;
}
public Integer getPaymentTime() {
return paymentTime;
}
public void setDelayIntrest(Integer delayIntrest) {
this.delayIntrest = delayIntrest;
}
public Integer getDelayIntrest() {
return delayIntrest;
}
public void setBillNumber(Integer billNumber) {
this.billNumber = billNumber;
}
public LanEvent getEvent() {
return event;
}
public void setEvent(LanEvent event) {
this.event = event;
}
public Date getPaidDate() {
return paidDate;
}
public void setPaidDate(Date paidDate) {
if(paidDate != null)
expires = null;
this.paidDate = paidDate;
}
public Date getSentDateTime()
{
Date ret = null;
if (sentDate != null)
{
ret = sentDate.getTime();
}
return ret;
}
public void setSentDateTime(Date date)
{
if (date == null)
{
sentDate = null;
} else {
if (sentDate == null)
{
sentDate = Calendar.getInstance();
}
sentDate.setTime(date);
}
}
public Calendar getSentDate() {
return sentDate;
}
public void setSentDate(Calendar sentDate) {
this.sentDate = sentDate;
}
public boolean isFoodwaveBill() { public boolean isFoodwaveBill() {
for (BillLine bl : billLines) for (BillLine bl : billLines)
...@@ -439,52 +461,52 @@ public class Bill extends GenericEntity { ...@@ -439,52 +461,52 @@ public class Bill extends GenericEntity {
return false; return false;
} }
public boolean isFoowavePaymentOver() { public boolean isFoowavePaymentOver() {
for (BillLine bl : billLines) for (BillLine bl : billLines)
{ {
if (bl.getFoodwave() != null) { if (bl.getFoodwave() != null) {
if (bl.getFoodwave().isPaymentOver()) if (bl.getFoodwave().isPaymentOver())
{ {
return true; return true;
} }
} }
} }
return false; return false;
} }
public Calendar getExpires() { public Calendar getExpires() {
return expires; return expires;
} }
public void setExpires(Calendar expires) { public void setExpires(Calendar expires) {
this.expires = expires; this.expires = expires;
} }
public boolean isExpired() { public boolean isExpired() {
if(isPaid() || expires == null) if (isPaid() || expires == null)
return false; return false;
return Calendar.getInstance().after(expires); return Calendar.getInstance().after(expires);
} }
public void markExpired() { public void markExpired() {
if(isExpired() || isPaid()) if (isExpired() || isPaid())
return; return;
expires = Calendar.getInstance(); expires = Calendar.getInstance();
} }
@Transient @Transient
public BigDecimal getTotalQuantity() { public BigDecimal getTotalQuantity() {
BigDecimal total = BigDecimal.ZERO; BigDecimal total = BigDecimal.ZERO;
for(BillLine l : getBillLines()) { for (BillLine l : getBillLines()) {
if(l == null || l.getQuantity() == null) if (l == null || l.getQuantity() == null)
continue; continue;
total = total.add(l.getQuantity()); total = total.add(l.getQuantity());
} }
return total; return total;
} }
...@@ -493,11 +515,11 @@ public class Bill extends GenericEntity { ...@@ -493,11 +515,11 @@ public class Bill extends GenericEntity {
public String getProductSummary() { public String getProductSummary() {
String summary = ""; String summary = "";
for(BillLine l : getBillLines()) { for (BillLine l : getBillLines()) {
if(l == null || l.getQuantity() == null) if (l == null || l.getQuantity() == null)
continue; continue;
if(!summary.isEmpty()) { if (!summary.isEmpty()) {
summary += ", "; summary += ", ";
} }
summary += l.getName(); summary += l.getName();
...@@ -506,10 +528,3 @@ public class Bill extends GenericEntity { ...@@ -506,10 +528,3 @@ public class Bill extends GenericEntity {
return summary; return summary;
} }
} }
...@@ -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;
......
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
*/ */
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.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
...@@ -39,331 +39,332 @@ import org.eclipse.persistence.annotations.PrivateOwned; ...@@ -39,331 +39,332 @@ import org.eclipse.persistence.annotations.PrivateOwned;
@Table(name = "products") @Table(name = "products")
public class Product extends GenericEntity { public class Product extends GenericEntity {
private static final String PRODUCTFLAG_TABLE_PRODUCTID = "product_id"; private static final String PRODUCTFLAG_TABLE_PRODUCTID = "product_id";
private static final String PRODUCTFLAG_TABLE_COLUMN = "productflags"; private static final String PRODUCTFLAG_TABLE_COLUMN = "productflags";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final String EVENT_ID_COLUMN = "event_id"; public static final String EVENT_ID_COLUMN = "event_id";
public static final Product EMPTY_PRODUCT = new Product("----"); public static final Product EMPTY_PRODUCT = new Product("----");
@ManyToOne() @ManyToOne()
@JoinColumn(name = EVENT_ID_COLUMN, nullable = false) @JoinColumn(name = EVENT_ID_COLUMN, nullable = false)
private LanEvent event; private LanEvent event;
private String color; private String color;
@Column(name = "product_name") @Column(name = "product_name")
private String name; private String name;
@Column(name = "description") @Column(name = "description")
@Lob @Lob
private String description; private String description;
@Column(name = "price", nullable = false, precision = 24, scale = 4) @Column(name = "price", nullable = false, precision = 24, scale = Bill.BILL_PRICE_SCALE)
private BigDecimal price = BigDecimal.ZERO; private BigDecimal price = Bill.BILL_SCALED_ZERO_PRICE;
@Column(name = "buy_in_price", nullable = true, precision = 24, scale = 4) @Column(name = "buy_in_price", nullable = true, precision = 24, scale = Bill.BILL_PRICE_SCALE)
private BigDecimal buyInPrice = BigDecimal.ZERO; private BigDecimal buyInPrice = Bill.BILL_SCALED_ZERO_PRICE;
@Column(name = "sort", nullable = false) @Column(name = "sort", nullable = false)
private int sort; private int sort;
@Column(name = "barcode") @Column(name = "barcode")
private String barcode; private String barcode;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product") private List<LicenseTarget> licenseTargets;
private List<LicenseTarget> licenseTargets;
@Enumerated(EnumType.STRING)
@PrivateOwned
@Enumerated(EnumType.STRING) @ElementCollection()
@PrivateOwned @CollectionTable(
@ElementCollection() joinColumns = @JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = ID_COLUMN),
@CollectionTable( uniqueConstraints = @UniqueConstraint(columnNames = { PRODUCTFLAG_TABLE_PRODUCTID, PRODUCTFLAG_TABLE_COLUMN }))
joinColumns = @JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = ID_COLUMN), @Column(name = PRODUCTFLAG_TABLE_COLUMN)
uniqueConstraints = @UniqueConstraint(columnNames = { PRODUCTFLAG_TABLE_PRODUCTID, PRODUCTFLAG_TABLE_COLUMN })) private Set<ProductFlag> productFlags = new HashSet<ProductFlag>();
@Column(name = PRODUCTFLAG_TABLE_COLUMN)
private Set<ProductFlag> productFlags = new HashSet<ProductFlag>(); @JoinColumn(name = "provided_role_id", referencedColumnName = Role.ID_COLUMN)
@ManyToOne
@JoinColumn(name = "provided_role_id", referencedColumnName = Role.ID_COLUMN) private Role provides;
@ManyToOne
private Role provides; @OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<Place> places;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<Place> places; @ManyToMany()
private List<ProductLimitation> productLimits;
@ManyToMany()
private List<ProductLimitation> productLimits; @OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<AccountEvent> accountEvents;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<AccountEvent> accountEvents; @OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<InventoryEvent> inventoryEvents;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<InventoryEvent> inventoryEvents; @ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "product_discounts",
@ManyToMany(cascade = CascadeType.ALL) inverseJoinColumns = {
@JoinTable(name = "product_discounts", @JoinColumn(name = "discount_id", referencedColumnName = Discount.ID_COLUMN)
inverseJoinColumns = { },
@JoinColumn(name = "discount_id", referencedColumnName = Discount.ID_COLUMN) joinColumns = {
}, @JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = Product.ID_COLUMN)
joinColumns = { })
@JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = Product.ID_COLUMN) private List<Discount> discounts;
})
private List<Discount> discounts; @Column(name = "vat", nullable = false, precision = 4, scale = Bill.VAT_SCALE)
private BigDecimal vat = Bill.VAT_SCALED_ZERO;
@Column(name = "vat", nullable = false, precision = 4, scale = 3) private String unitName = "";
private BigDecimal vat = BigDecimal.ZERO;
private String unitName = ""; @ManyToMany()
@JoinTable(name = "product_foodwavetemplate",
@ManyToMany() joinColumns = {
@JoinTable(name = "product_foodwavetemplate", @JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = Product.ID_COLUMN),
joinColumns = { },
@JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = Product.ID_COLUMN), inverseJoinColumns = {
}, @JoinColumn(name = "food_wave_template_id", referencedColumnName = FoodWaveTemplate.ID_COLUMN)
inverseJoinColumns = { },
@JoinColumn(name = "food_wave_template_id", referencedColumnName = FoodWaveTemplate.ID_COLUMN) uniqueConstraints = {
}, @UniqueConstraint(columnNames = { PRODUCTFLAG_TABLE_PRODUCTID, "food_wave_template_id" })
uniqueConstraints = { })
@UniqueConstraint(columnNames = { PRODUCTFLAG_TABLE_PRODUCTID, "food_wave_template_id" }) private List<FoodWaveTemplate> foodWaveTemplates;
})
private List<FoodWaveTemplate> foodWaveTemplates; public Product() {
super();
public Product() { }
super();
} public Product(LanEvent event) {
super();
public Product(LanEvent event) { this.setEvent(event);
super();
this.setEvent(event); }
}
private Product(String name) { private Product(String name) {
super(); super();
this.name = name; this.name = name;
} }
public BigDecimal getSoldCash() { public BigDecimal getSoldCash() {
BigDecimal tot = BigDecimal.ZERO; BigDecimal tot = BigDecimal.ZERO;
for (AccountEvent ac : this.getAccountEvents()) { for (AccountEvent ac : this.getAccountEvents()) {
if (ac.isCash()) { if (ac.isCash()) {
tot = tot.add(ac.getQuantity()); tot = tot.add(ac.getQuantity());
} }
} }
return tot; return tot;
} }
public BigDecimal getSoldBill() { public BigDecimal getSoldBill() {
BigDecimal tot = BigDecimal.ZERO; BigDecimal tot = BigDecimal.ZERO;
for (AccountEvent ac : this.getAccountEvents()) { for (AccountEvent ac : this.getAccountEvents()) {
if (!ac.isCash()) { if (!ac.isCash()) {
tot = tot.add(ac.getQuantity()); tot = tot.add(ac.getQuantity());
} }
} }
return tot; return tot;
} }
public BigDecimal getInventoryCount() { public BigDecimal getInventoryCount() {
BigDecimal ret = new BigDecimal(0); BigDecimal ret = new BigDecimal(0);
if (accountEvents != null) { if (accountEvents != null) {
for (AccountEvent ae : accountEvents) { for (AccountEvent ae : accountEvents) {
ret = ret.subtract(ae.getQuantity()); ret = ret.subtract(ae.getQuantity());
} }
} }
if (inventoryEvents != null) { if (inventoryEvents != null) {
for (InventoryEvent ie : inventoryEvents) { for (InventoryEvent ie : inventoryEvents) {
ret = ret.add(ie.getQuantity()); ret = ret.add(ie.getQuantity());
} }
} }
return ret; return ret;
} }
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String productName) { public void setName(String productName) {
this.name = productName; this.name = productName;
} }
/** /**
* Get product price, includes vat * Get product price, includes vat
* @return *
*/ * @return
public BigDecimal getPrice() { */
return price; public BigDecimal getPrice() {
} return price;
}
/**
* Set price, including vat /**
* @param price * Set price, including vat
*/ *
public void setPrice(BigDecimal price) { * @param price
this.price = price; */
} public void setPrice(BigDecimal price) {
this.price = price;
public int getSort() { }
return sort;
} public int getSort() {
return sort;
public void setSort(int sort) { }
this.sort = sort;
} public void setSort(int sort) {
this.sort = sort;
public String getBarcode() { }
return barcode;
} public String getBarcode() {
return barcode;
public void setBarcode(String barcode) { }
this.barcode = barcode;
} public void setBarcode(String barcode) {
this.barcode = barcode;
public List<Place> getPlaces() { }
return places;
} public List<Place> getPlaces() {
return places;
public void setPlaces(List<Place> placeList) { }
this.places = placeList;
} public void setPlaces(List<Place> placeList) {
this.places = placeList;
public List<AccountEvent> getAccountEvents() { }
return accountEvents;
} public List<AccountEvent> getAccountEvents() {
return accountEvents;
public void setAccountEvents(List<AccountEvent> accountEventList) { }
this.accountEvents = accountEventList;
} public void setAccountEvents(List<AccountEvent> accountEventList) {
this.accountEvents = accountEventList;
public void setDiscounts(List<Discount> discounts) { }
this.discounts = discounts;
} public void setDiscounts(List<Discount> discounts) {
this.discounts = discounts;
public List<Discount> getDiscounts() { }
return discounts;
} public List<Discount> getDiscounts() {
return discounts;
public void setFoodWaveTemplates(List<FoodWaveTemplate> foodWaveTemplates) { }
this.foodWaveTemplates = foodWaveTemplates;
} public void setFoodWaveTemplates(List<FoodWaveTemplate> foodWaveTemplates) {
this.foodWaveTemplates = foodWaveTemplates;
public List<FoodWaveTemplate> getFoodWaveTemplates() { }
return foodWaveTemplates;
} public List<FoodWaveTemplate> getFoodWaveTemplates() {
return foodWaveTemplates;
/** }
* Set product vat-%, value between 0 and 1
* @param vat /**
*/ * Set product vat-%, value between 0 and 1
public void setVat(BigDecimal vat) { *
this.vat = vat; * @param vat
} */
public void setVat(BigDecimal vat) {
/** this.vat = vat;
* Get product vat-%, value between 0 and 1 }
* @return
*/ /**
public BigDecimal getVat() { * Get product vat-%, value between 0 and 1
return vat; *
} * @return
*/
public void setUnitName(String unitName) { public BigDecimal getVat() {
this.unitName = unitName; return vat;
} }
public String getUnitName() { public void setUnitName(String unitName) {
return unitName; this.unitName = unitName;
} }
public void setProvides(Role provides) { public String getUnitName() {
this.provides = provides; return unitName;
} }
public Role getProvides() { public void setProvides(Role provides) {
return provides; this.provides = provides;
} }
public LanEvent getEvent() { public Role getProvides() {
return event; return provides;
} }
public void setEvent(LanEvent event) { public LanEvent getEvent() {
this.event = event; return event;
} }
public String getColor() { public void setEvent(LanEvent event) {
return color; this.event = event;
} }
public void setColor(String color) { public String getColor() {
this.color = color; return color;
} }
public Set<ProductFlag> getProductFlags() { public void setColor(String color) {
return productFlags; this.color = color;
} }
public void setProductFlags(Set<ProductFlag> productFlags) { public Set<ProductFlag> getProductFlags() {
this.productFlags = productFlags; return productFlags;
} }
@Transient public void setProductFlags(Set<ProductFlag> productFlags) {
public void addProductFlag(ProductFlag flag) { this.productFlags = productFlags;
}
Set<ProductFlag> flags = getProductFlags();
@Transient
if(flags == null) public void addProductFlag(ProductFlag flag) {
flags = new HashSet<ProductFlag>();
Set<ProductFlag> flags = getProductFlags();
if(!flags.contains(flag)) {
flags.add(flag); if (flags == null)
setProductFlags(flags); flags = new HashSet<ProductFlag>();
}
} if (!flags.contains(flag)) {
flags.add(flag);
public List<ProductLimitation> getProductLimits() { setProductFlags(flags);
return productLimits; }
} }
public void setProductLimits(List<ProductLimitation> productLimits) { public List<ProductLimitation> getProductLimits() {
this.productLimits = productLimits; return productLimits;
} }
public String getDescription() { public void setProductLimits(List<ProductLimitation> productLimits) {
return description; this.productLimits = productLimits;
} }
public void setDescription(String description) { public String getDescription() {
this.description = description; return description;
} }
public BigDecimal getBuyInPrice() { public void setDescription(String description) {
return buyInPrice; this.description = description;
} }
public void setBuyInPrice(BigDecimal buyInPrice) { public BigDecimal getBuyInPrice() {
this.buyInPrice = buyInPrice; return buyInPrice;
} }
public List<InventoryEvent> getInventoryEvents() { public void setBuyInPrice(BigDecimal buyInPrice) {
return inventoryEvents; this.buyInPrice = buyInPrice;
} }
public void setInventoryEvents(List<InventoryEvent> inventoryEvents) { public List<InventoryEvent> getInventoryEvents() {
this.inventoryEvents = inventoryEvents; return inventoryEvents;
} }
public List<LicenseTarget> getLicenseTargets() { public void setInventoryEvents(List<InventoryEvent> inventoryEvents) {
return licenseTargets; this.inventoryEvents = inventoryEvents;
} }
public void setLicenseTargets(List<LicenseTarget> licenseTargets) { public List<LicenseTarget> getLicenseTargets() {
this.licenseTargets = licenseTargets; return licenseTargets;
} }
public void setLicenseTargets(List<LicenseTarget> licenseTargets) {
public boolean isUsershopAutoproduct() { this.licenseTargets = licenseTargets;
return getProductFlags().contains(ProductFlag.USERSHOP_AUTOPRODUCT); }
}
public boolean isUsershopAutoproduct() {
return getProductFlags().contains(ProductFlag.USERSHOP_AUTOPRODUCT);
}
} }
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!