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;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
......@@ -173,7 +174,7 @@ public class PlaceBean implements PlaceBeanLocal {
Map<Product, Integer> mockmap = getPlaceProductcount(places);
BigDecimal total = BigDecimal.ZERO;
BigDecimal total = Bill.BILL_SCALED_ZERO_PRICE;
Calendar now = Calendar.getInstance();
for (Entry<Product, Integer> entry : mockmap.entrySet()) {
......@@ -182,7 +183,7 @@ public class PlaceBean implements PlaceBeanLocal {
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) {
......@@ -479,7 +480,6 @@ public class PlaceBean implements PlaceBeanLocal {
return placeFacade.setBuyable(map, like, b);
}
/**
* Release reservation from user
*
......@@ -724,8 +724,6 @@ public class PlaceBean implements PlaceBeanLocal {
return placeFacade.getMapProducts(map);
}
@Override
public List<PlaceSlot> getFreePlaceslots(EventUser user, EventMap map) {
user = eventUserFacade.reload(user);
......
......@@ -19,6 +19,7 @@
package fi.codecrew.moya.model;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
......@@ -53,381 +54,402 @@ import fi.codecrew.moya.utilities.BillUtils;
@Table(name = "bills", uniqueConstraints = { @UniqueConstraint(columnNames = { Bill.EVENT_ID_COLUMN, "bill_number" }) })
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;
public static final String EVENT_ID_COLUMN = "event_id";
private static final long serialVersionUID = -6713643278149221869L;
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()
@JoinColumn(name = EVENT_ID_COLUMN, nullable = false)
private LanEvent event;
public boolean isPaid()
{
return accountEvent != null || paidDate != null;
}
/**
* 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 bill is paid this AccountEvent is created and this is a
* reference to that accountAction. if this bill
*/
@JoinColumn(name = "account_event_id", referencedColumnName = "id", updatable = false)
@OneToOne
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;
}
/**
* When the money has appeared on the bank account.
*/
@Column(name = "paid_date")
@Temporal(TemporalType.TIMESTAMP)
private Date paidDate;
public Integer getReferenceNumber()
{
return BillUtils.createReferenceNumber(getReferenceNumberBase());
}
/**
* 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;
/**
* Commodity function to calculate the total price of the bill.
*
* @return The total sum of the bill ( unitPrice * units * vat )
*/
public BigDecimal totalPrice() {
BigDecimal total = Bill.BILL_SCALED_ZERO_PRICE;
for (BillLine line : getBillLines()) {
total = total.add(line.getLinePrice());
}
return total;
}
/**
* 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;
public BigDecimal getTotalPrice() {
return this.totalPrice();
}
/**
* 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
@OneToMany(mappedBy = "bill", cascade = CascadeType.ALL)
private List<BillLine> billLines = new ArrayList<BillLine>();
/**
* Commodity function to return the vatless price of the bill
*
* @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()
{
return accountEvent != null || paidDate != null;
}
public Bill(LanEvent event, EventUser user, Calendar expires) {
this(event, expires);
this.setUser(user);
this.setAddr1(user.getUser().getFirstnames() + " " + user.getUser().getLastname());
this.setAddr2(user.getUser().getAddress());
this.setAddr3(user.getUser().getZip() + " " + user.getUser().getTown());
}
/**
* When the bill is paid this AccountEvent is created and this is a
* reference to that accountAction. if this bill
*/
@JoinColumn(name = "account_event_id", referencedColumnName = "id", updatable = false)
@OneToOne
private AccountEvent accountEvent;
public Bill(LanEvent event, Calendar expires) {
this();
this.expires = expires;
this.event = event;
}
/**
* 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()
{
return BillUtils.createReferenceNumber(getReferenceNumberBase());
}
/**
* Commodity function to calculate the total price of the bill.
*
* @return The total sum of the bill ( unitPrice * units * vat )
*/
public BigDecimal totalPrice() {
BigDecimal total = BigDecimal.ZERO;
for (BillLine line : getBillLines()) {
total = total.add(line.getLinePrice());
}
return total;
}
public BigDecimal getTotalPrice() {
return this.totalPrice();
}
/**
* 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 = BigDecimal.ZERO;
for (BillLine line : getBillLines()) {
total = total.add(line.getLineVat());
}
return total;
}
/**
* Commodity function to return the vatless price of the bill
*
* @return The total VAT-less sum of the bill ( unitPrice * units )
*/
public BigDecimal totalPriceVatless() {
BigDecimal total = BigDecimal.ZERO;
for (BillLine line : getBillLines()) {
total = total.add(line.getLinePriceVatless());
}
return total;
}
public Bill(LanEvent event, EventUser user, Calendar expires) {
this(event, expires);
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) {
this();
this.expires = expires;
this.event = event;
}
public Bill(LanEvent event, EventUser user, long expireTimeHours) {
this(event, user, Calendar.getInstance());
this.expires.setTimeInMillis((System.currentTimeMillis() + (expireTimeHours*60*60 * 1000 )));
}
public Bill(LanEvent event, long expireTimeHours) {
this(event, Calendar.getInstance());
this.expires.setTimeInMillis((System.currentTimeMillis() + (expireTimeHours*60*60 * 1000 )));
}
public Bill() {
super();
this.expires = Calendar.getInstance();
this.expires.setTimeInMillis((System.currentTimeMillis() + 1209600000)); // 2vk
}
public Calendar getDueDate() {
Calendar dueDate = Calendar.getInstance();
dueDate.setTime(this.getSentDate().getTime());
dueDate.add(Calendar.DATE, this.getPaymentTime());
return dueDate;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public List<BillLine> getBillLines() {
return billLines;
}
public void setBillLines(List<BillLine> billLineList) {
this.billLines = billLineList;
}
public AccountEvent getAccountEvent() {
return accountEvent;
}
public void setAccountEvent(AccountEvent accoutEventsId) {
this.accountEvent = accoutEventsId;
}
public EventUser getUser() {
return user;
}
public void setUser(EventUser usersId) {
this.user = usersId;
}
public Integer getBillNumber() {
return billNumber;
}
public String getAddr1() {
return addr1;
}
public void setAddr1(String addr1) {
this.addr1 = addr1;
}
public String getAddr2() {
return addr2;
}
public void setAddr2(String addr2) {
this.addr2 = addr2;
}
public String getAddr3() {
return addr3;
}
public void setAddr3(String addr3) {
this.addr3 = addr3;
}
public String getAddr4() {
return addr4;
}
public void setAddr4(String addr4) {
this.addr4 = addr4;
}
public String getAddr5() {
return addr5;
}
public void setAddr5(String addr5) {
this.addr5 = addr5;
}
public String getNoticetime() {
return noticetime;
}
public void setNoticetime(String noticetime) {
this.noticetime = noticetime;
}
public String getOurReference() {
return ourReference;
}
public void setOurReference(String ourReference) {
this.ourReference = ourReference;
}
public void setTheirReference(String theirReference) {
this.theirReference = theirReference;
}
public String getTheirReference() {
return theirReference;
}
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 Bill(LanEvent event, EventUser user, long expireTimeHours) {
this(event, user, Calendar.getInstance());
this.expires.setTimeInMillis((System.currentTimeMillis() + (expireTimeHours * 60 * 60 * 1000)));
}
public Bill(LanEvent event, long expireTimeHours) {
this(event, Calendar.getInstance());
this.expires.setTimeInMillis((System.currentTimeMillis() + (expireTimeHours * 60 * 60 * 1000)));
}
public Bill() {
super();
this.expires = Calendar.getInstance();
this.expires.setTimeInMillis((System.currentTimeMillis() + 1209600000)); // 2vk
}
public Calendar getDueDate() {
Calendar dueDate = Calendar.getInstance();
dueDate.setTime(this.getSentDate().getTime());
dueDate.add(Calendar.DATE, this.getPaymentTime());
return dueDate;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public List<BillLine> getBillLines() {
return billLines;
}
public void setBillLines(List<BillLine> billLineList) {
this.billLines = billLineList;
}
public AccountEvent getAccountEvent() {
return accountEvent;
}
public void setAccountEvent(AccountEvent accoutEventsId) {
this.accountEvent = accoutEventsId;
}
public EventUser getUser() {
return user;
}
public void setUser(EventUser usersId) {
this.user = usersId;
}
public Integer getBillNumber() {
return billNumber;
}
public String getAddr1() {
return addr1;
}
public void setAddr1(String addr1) {
this.addr1 = addr1;
}
public String getAddr2() {
return addr2;
}
public void setAddr2(String addr2) {
this.addr2 = addr2;
}
public String getAddr3() {
return addr3;
}
public void setAddr3(String addr3) {
this.addr3 = addr3;
}
public String getAddr4() {
return addr4;
}
public void setAddr4(String addr4) {
this.addr4 = addr4;
}
public String getAddr5() {
return addr5;
}
public void setAddr5(String addr5) {
this.addr5 = addr5;
}
public String getNoticetime() {
return noticetime;
}
public void setNoticetime(String noticetime) {
this.noticetime = noticetime;
}
public String getOurReference() {
return ourReference;
}
public void setOurReference(String ourReference) {
this.ourReference = ourReference;
}
public void setTheirReference(String theirReference) {
this.theirReference = theirReference;
}
public String getTheirReference() {
return theirReference;
}
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() {
for (BillLine bl : billLines)
......@@ -439,52 +461,52 @@ public class Bill extends GenericEntity {
return false;
}
public boolean isFoowavePaymentOver() {
for (BillLine bl : billLines)
{
public boolean isFoowavePaymentOver() {
for (BillLine bl : billLines)
{
if (bl.getFoodwave() != null) {
if (bl.getFoodwave().isPaymentOver())
{
{
return true;
}
}
}
}
}
}
return false;
}
public Calendar getExpires() {
return expires;
}
public void setExpires(Calendar expires) {
this.expires = expires;
}
public boolean isExpired() {
if(isPaid() || expires == null)
return false;
return Calendar.getInstance().after(expires);
}
public void markExpired() {
if(isExpired() || isPaid())
return;
expires = Calendar.getInstance();
}
public Calendar getExpires() {
return expires;
}
public void setExpires(Calendar expires) {
this.expires = expires;
}
public boolean isExpired() {
if (isPaid() || expires == null)
return false;
return Calendar.getInstance().after(expires);
}
public void markExpired() {
if (isExpired() || isPaid())
return;
expires = Calendar.getInstance();
}
@Transient
public BigDecimal getTotalQuantity() {
BigDecimal total = BigDecimal.ZERO;
for(BillLine l : getBillLines()) {
if(l == null || l.getQuantity() == null)
for (BillLine l : getBillLines()) {
if (l == null || l.getQuantity() == null)
continue;
total = total.add(l.getQuantity());
}
}
return total;
}
......@@ -493,11 +515,11 @@ public class Bill extends GenericEntity {
public String getProductSummary() {
String summary = "";
for(BillLine l : getBillLines()) {
if(l == null || l.getQuantity() == null)
for (BillLine l : getBillLines()) {
if (l == null || l.getQuantity() == null)
continue;
if(!summary.isEmpty()) {
if (!summary.isEmpty()) {
summary += ", ";
}
summary += l.getName();
......@@ -506,10 +528,3 @@ public class Bill extends GenericEntity {
return summary;
}
}
......@@ -42,7 +42,6 @@ import javax.persistence.Table;
public class BillLine extends GenericEntity {
private static final long serialVersionUID = 2L;
private static final BigDecimal DEFAULT_VAT = BigDecimal.ZERO;
/**
* Which bill this bill line belongs to
*/
......@@ -67,8 +66,8 @@ public class BillLine extends GenericEntity {
* How much one(1) unit of this product costs
*
*/
@Column(name = "unit_price", nullable = false, precision = 24, scale = 4)
private BigDecimal unitPrice = BigDecimal.ZERO;
@Column(name = "unit_price", nullable = false, precision = 24, scale = Bill.BILL_PRICE_SCALE)
private BigDecimal unitPrice = Bill.BILL_SCALED_ZERO_PRICE;
/**
*
......@@ -79,8 +78,8 @@ public class BillLine extends GenericEntity {
/**
* How much VAT this product contains ( 0, 0.22 ) etc
*/
@Column(name = "vat", nullable = false, precision = 4, scale = 3)
private BigDecimal vat = DEFAULT_VAT;
@Column(name = "vat", nullable = false, precision = 4, scale = Bill.VAT_SCALE)
private BigDecimal vat = Bill.VAT_SCALED_ZERO;
@JoinColumn(name = "lineProduct_id", referencedColumnName = "id", nullable = true, updatable = false)
@OneToOne
......@@ -111,7 +110,7 @@ public class BillLine extends GenericEntity {
*/
public BigDecimal getLinePriceVatless() {
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() {
......@@ -157,8 +156,8 @@ public class BillLine extends GenericEntity {
public BillLine(Bill bill2, Product product, Discount disc, BigDecimal count) {
super();
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.unitName = product.getUnitName();
this.quantity = count;
......
......@@ -19,6 +19,7 @@
package fi.codecrew.moya.model;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
......@@ -45,13 +46,15 @@ public class Discount extends GenericEntity {
private static final long serialVersionUID = 1L;
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()
@JoinColumn(name = EVENT_ID, nullable = false)
private LanEvent event;
@Column(name = "percentage", nullable = false, precision = 9, scale = 6)
private BigDecimal percentage = BigDecimal.ZERO;
private BigDecimal percentage = SCALE6_ZERO;
@Column(name = "code")
private String code;
......@@ -71,22 +74,22 @@ public class Discount extends GenericEntity {
private String shortdesc;
@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)
private BigDecimal amountMax = BigDecimal.ZERO;
private BigDecimal amountMax = SCALE4_ZERO;
@Column(name = "active", nullable = false)
private boolean active = false;
@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)
private BigDecimal perUser = BigDecimal.ZERO;
private BigDecimal perUser = SCALE4_ZERO;
@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")
private List<DiscountInstance> discountInstances;
......
......@@ -22,8 +22,8 @@
*/
package fi.codecrew.moya.model;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
......@@ -39,331 +39,332 @@ import org.eclipse.persistence.annotations.PrivateOwned;
@Table(name = "products")
public class Product extends GenericEntity {
private static final String PRODUCTFLAG_TABLE_PRODUCTID = "product_id";
private static final String PRODUCTFLAG_TABLE_COLUMN = "productflags";
private static final long serialVersionUID = 1L;
public static final String EVENT_ID_COLUMN = "event_id";
public static final Product EMPTY_PRODUCT = new Product("----");
@ManyToOne()
@JoinColumn(name = EVENT_ID_COLUMN, nullable = false)
private LanEvent event;
private String color;
@Column(name = "product_name")
private String name;
@Column(name = "description")
@Lob
private String description;
@Column(name = "price", nullable = false, precision = 24, scale = 4)
private BigDecimal price = BigDecimal.ZERO;
@Column(name = "buy_in_price", nullable = true, precision = 24, scale = 4)
private BigDecimal buyInPrice = BigDecimal.ZERO;
@Column(name = "sort", nullable = false)
private int sort;
@Column(name = "barcode")
private String barcode;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<LicenseTarget> licenseTargets;
@Enumerated(EnumType.STRING)
@PrivateOwned
@ElementCollection()
@CollectionTable(
joinColumns = @JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = ID_COLUMN),
uniqueConstraints = @UniqueConstraint(columnNames = { PRODUCTFLAG_TABLE_PRODUCTID, PRODUCTFLAG_TABLE_COLUMN }))
@Column(name = PRODUCTFLAG_TABLE_COLUMN)
private Set<ProductFlag> productFlags = new HashSet<ProductFlag>();
@JoinColumn(name = "provided_role_id", referencedColumnName = Role.ID_COLUMN)
@ManyToOne
private Role provides;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<Place> places;
@ManyToMany()
private List<ProductLimitation> productLimits;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<AccountEvent> accountEvents;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<InventoryEvent> inventoryEvents;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "product_discounts",
inverseJoinColumns = {
@JoinColumn(name = "discount_id", referencedColumnName = Discount.ID_COLUMN)
},
joinColumns = {
@JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = Product.ID_COLUMN)
})
private List<Discount> discounts;
@Column(name = "vat", nullable = false, precision = 4, scale = 3)
private BigDecimal vat = BigDecimal.ZERO;
private String unitName = "";
@ManyToMany()
@JoinTable(name = "product_foodwavetemplate",
joinColumns = {
@JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = Product.ID_COLUMN),
},
inverseJoinColumns = {
@JoinColumn(name = "food_wave_template_id", referencedColumnName = FoodWaveTemplate.ID_COLUMN)
},
uniqueConstraints = {
@UniqueConstraint(columnNames = { PRODUCTFLAG_TABLE_PRODUCTID, "food_wave_template_id" })
})
private List<FoodWaveTemplate> foodWaveTemplates;
public Product() {
super();
}
public Product(LanEvent event) {
super();
this.setEvent(event);
}
private static final String PRODUCTFLAG_TABLE_PRODUCTID = "product_id";
private static final String PRODUCTFLAG_TABLE_COLUMN = "productflags";
private static final long serialVersionUID = 1L;
public static final String EVENT_ID_COLUMN = "event_id";
public static final Product EMPTY_PRODUCT = new Product("----");
@ManyToOne()
@JoinColumn(name = EVENT_ID_COLUMN, nullable = false)
private LanEvent event;
private String color;
@Column(name = "product_name")
private String name;
@Column(name = "description")
@Lob
private String description;
@Column(name = "price", nullable = false, precision = 24, scale = Bill.BILL_PRICE_SCALE)
private BigDecimal price = Bill.BILL_SCALED_ZERO_PRICE;
@Column(name = "buy_in_price", nullable = true, precision = 24, scale = Bill.BILL_PRICE_SCALE)
private BigDecimal buyInPrice = Bill.BILL_SCALED_ZERO_PRICE;
@Column(name = "sort", nullable = false)
private int sort;
@Column(name = "barcode")
private String barcode;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<LicenseTarget> licenseTargets;
@Enumerated(EnumType.STRING)
@PrivateOwned
@ElementCollection()
@CollectionTable(
joinColumns = @JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = ID_COLUMN),
uniqueConstraints = @UniqueConstraint(columnNames = { PRODUCTFLAG_TABLE_PRODUCTID, PRODUCTFLAG_TABLE_COLUMN }))
@Column(name = PRODUCTFLAG_TABLE_COLUMN)
private Set<ProductFlag> productFlags = new HashSet<ProductFlag>();
@JoinColumn(name = "provided_role_id", referencedColumnName = Role.ID_COLUMN)
@ManyToOne
private Role provides;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<Place> places;
@ManyToMany()
private List<ProductLimitation> productLimits;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<AccountEvent> accountEvents;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<InventoryEvent> inventoryEvents;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "product_discounts",
inverseJoinColumns = {
@JoinColumn(name = "discount_id", referencedColumnName = Discount.ID_COLUMN)
},
joinColumns = {
@JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = Product.ID_COLUMN)
})
private List<Discount> discounts;
@Column(name = "vat", nullable = false, precision = 4, scale = Bill.VAT_SCALE)
private BigDecimal vat = Bill.VAT_SCALED_ZERO;
private String unitName = "";
@ManyToMany()
@JoinTable(name = "product_foodwavetemplate",
joinColumns = {
@JoinColumn(name = PRODUCTFLAG_TABLE_PRODUCTID, referencedColumnName = Product.ID_COLUMN),
},
inverseJoinColumns = {
@JoinColumn(name = "food_wave_template_id", referencedColumnName = FoodWaveTemplate.ID_COLUMN)
},
uniqueConstraints = {
@UniqueConstraint(columnNames = { PRODUCTFLAG_TABLE_PRODUCTID, "food_wave_template_id" })
})
private List<FoodWaveTemplate> foodWaveTemplates;
public Product() {
super();
}
public Product(LanEvent event) {
super();
this.setEvent(event);
}
private Product(String name) {
super();
super();
this.name = name;
}
public BigDecimal getSoldCash() {
BigDecimal tot = BigDecimal.ZERO;
for (AccountEvent ac : this.getAccountEvents()) {
if (ac.isCash()) {
tot = tot.add(ac.getQuantity());
}
}
return tot;
}
public BigDecimal getSoldBill() {
BigDecimal tot = BigDecimal.ZERO;
for (AccountEvent ac : this.getAccountEvents()) {
if (!ac.isCash()) {
tot = tot.add(ac.getQuantity());
}
}
return tot;
}
public BigDecimal getInventoryCount() {
BigDecimal ret = new BigDecimal(0);
if (accountEvents != null) {
for (AccountEvent ae : accountEvents) {
ret = ret.subtract(ae.getQuantity());
}
}
if (inventoryEvents != null) {
for (InventoryEvent ie : inventoryEvents) {
ret = ret.add(ie.getQuantity());
}
}
return ret;
}
public String getName() {
return name;
}
public void setName(String productName) {
this.name = productName;
}
/**
* Get product price, includes vat
* @return
*/
public BigDecimal getPrice() {
return price;
}
/**
* Set price, including vat
* @param price
*/
public void setPrice(BigDecimal price) {
this.price = price;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
public String getBarcode() {
return barcode;
}
public void setBarcode(String barcode) {
this.barcode = barcode;
}
public List<Place> getPlaces() {
return places;
}
public void setPlaces(List<Place> placeList) {
this.places = placeList;
}
public List<AccountEvent> getAccountEvents() {
return accountEvents;
}
public void setAccountEvents(List<AccountEvent> accountEventList) {
this.accountEvents = accountEventList;
}
public void setDiscounts(List<Discount> discounts) {
this.discounts = discounts;
}
public List<Discount> getDiscounts() {
return discounts;
}
public void setFoodWaveTemplates(List<FoodWaveTemplate> foodWaveTemplates) {
this.foodWaveTemplates = foodWaveTemplates;
}
public List<FoodWaveTemplate> getFoodWaveTemplates() {
return foodWaveTemplates;
}
/**
* Set product vat-%, value between 0 and 1
* @param vat
*/
public void setVat(BigDecimal vat) {
this.vat = vat;
}
/**
* Get product vat-%, value between 0 and 1
* @return
*/
public BigDecimal getVat() {
return vat;
}
public void setUnitName(String unitName) {
this.unitName = unitName;
}
public String getUnitName() {
return unitName;
}
public void setProvides(Role provides) {
this.provides = provides;
}
public Role getProvides() {
return provides;
}
public LanEvent getEvent() {
return event;
}
public void setEvent(LanEvent event) {
this.event = event;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Set<ProductFlag> getProductFlags() {
return productFlags;
}
public void setProductFlags(Set<ProductFlag> productFlags) {
this.productFlags = productFlags;
}
@Transient
public void addProductFlag(ProductFlag flag) {
Set<ProductFlag> flags = getProductFlags();
if(flags == null)
flags = new HashSet<ProductFlag>();
if(!flags.contains(flag)) {
flags.add(flag);
setProductFlags(flags);
}
}
public List<ProductLimitation> getProductLimits() {
return productLimits;
}
public void setProductLimits(List<ProductLimitation> productLimits) {
this.productLimits = productLimits;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public BigDecimal getBuyInPrice() {
return buyInPrice;
}
public void setBuyInPrice(BigDecimal buyInPrice) {
this.buyInPrice = buyInPrice;
}
public List<InventoryEvent> getInventoryEvents() {
return inventoryEvents;
}
public void setInventoryEvents(List<InventoryEvent> inventoryEvents) {
this.inventoryEvents = inventoryEvents;
}
public List<LicenseTarget> getLicenseTargets() {
return licenseTargets;
}
public void setLicenseTargets(List<LicenseTarget> licenseTargets) {
this.licenseTargets = licenseTargets;
}
public boolean isUsershopAutoproduct() {
return getProductFlags().contains(ProductFlag.USERSHOP_AUTOPRODUCT);
}
}
public BigDecimal getSoldCash() {
BigDecimal tot = BigDecimal.ZERO;
for (AccountEvent ac : this.getAccountEvents()) {
if (ac.isCash()) {
tot = tot.add(ac.getQuantity());
}
}
return tot;
}
public BigDecimal getSoldBill() {
BigDecimal tot = BigDecimal.ZERO;
for (AccountEvent ac : this.getAccountEvents()) {
if (!ac.isCash()) {
tot = tot.add(ac.getQuantity());
}
}
return tot;
}
public BigDecimal getInventoryCount() {
BigDecimal ret = new BigDecimal(0);
if (accountEvents != null) {
for (AccountEvent ae : accountEvents) {
ret = ret.subtract(ae.getQuantity());
}
}
if (inventoryEvents != null) {
for (InventoryEvent ie : inventoryEvents) {
ret = ret.add(ie.getQuantity());
}
}
return ret;
}
public String getName() {
return name;
}
public void setName(String productName) {
this.name = productName;
}
/**
* Get product price, includes vat
*
* @return
*/
public BigDecimal getPrice() {
return price;
}
/**
* Set price, including vat
*
* @param price
*/
public void setPrice(BigDecimal price) {
this.price = price;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
public String getBarcode() {
return barcode;
}
public void setBarcode(String barcode) {
this.barcode = barcode;
}
public List<Place> getPlaces() {
return places;
}
public void setPlaces(List<Place> placeList) {
this.places = placeList;
}
public List<AccountEvent> getAccountEvents() {
return accountEvents;
}
public void setAccountEvents(List<AccountEvent> accountEventList) {
this.accountEvents = accountEventList;
}
public void setDiscounts(List<Discount> discounts) {
this.discounts = discounts;
}
public List<Discount> getDiscounts() {
return discounts;
}
public void setFoodWaveTemplates(List<FoodWaveTemplate> foodWaveTemplates) {
this.foodWaveTemplates = foodWaveTemplates;
}
public List<FoodWaveTemplate> getFoodWaveTemplates() {
return foodWaveTemplates;
}
/**
* Set product vat-%, value between 0 and 1
*
* @param vat
*/
public void setVat(BigDecimal vat) {
this.vat = vat;
}
/**
* Get product vat-%, value between 0 and 1
*
* @return
*/
public BigDecimal getVat() {
return vat;
}
public void setUnitName(String unitName) {
this.unitName = unitName;
}
public String getUnitName() {
return unitName;
}
public void setProvides(Role provides) {
this.provides = provides;
}
public Role getProvides() {
return provides;
}
public LanEvent getEvent() {
return event;
}
public void setEvent(LanEvent event) {
this.event = event;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Set<ProductFlag> getProductFlags() {
return productFlags;
}
public void setProductFlags(Set<ProductFlag> productFlags) {
this.productFlags = productFlags;
}
@Transient
public void addProductFlag(ProductFlag flag) {
Set<ProductFlag> flags = getProductFlags();
if (flags == null)
flags = new HashSet<ProductFlag>();
if (!flags.contains(flag)) {
flags.add(flag);
setProductFlags(flags);
}
}
public List<ProductLimitation> getProductLimits() {
return productLimits;
}
public void setProductLimits(List<ProductLimitation> productLimits) {
this.productLimits = productLimits;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public BigDecimal getBuyInPrice() {
return buyInPrice;
}
public void setBuyInPrice(BigDecimal buyInPrice) {
this.buyInPrice = buyInPrice;
}
public List<InventoryEvent> getInventoryEvents() {
return inventoryEvents;
}
public void setInventoryEvents(List<InventoryEvent> inventoryEvents) {
this.inventoryEvents = inventoryEvents;
}
public List<LicenseTarget> getLicenseTargets() {
return licenseTargets;
}
public void setLicenseTargets(List<LicenseTarget> licenseTargets) {
this.licenseTargets = licenseTargets;
}
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!