package models;
import com.fasterxml.jackson.annotation.JsonBackReference;
import exceptions.PoseidonException;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.mvc.Http;
import service.PoseidonService;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
@Entity
@Table(name = "POS_ORDER")
public class OrderModel extends AuditedModel {
private final static Logger logger = LoggerFactory.getLogger(OrderModel.class);
public static Finder<Long, OrderModel> find = new Finder<>(OrderModel.class);
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
@Column(unique = true)
public String met_ref;
public String custref_po_calloff;
public String custref_contractnum;
public String custref_email;
public String customer_name;
public String customer_email;
public String customer_phone;
public Date start_date1;
public Integer start_termin1;
public Date end_date1;
public Integer end_termin1;
public String termins1; // comma-separated list of termin values, eg 0,3,6,18,21
public Date start_date2;
public Integer start_termin2;
public Date end_date2;
public Integer end_termin2;
public String termins2;
public Date start_date3;
public Integer start_termin3;
public Date end_date3;
public Integer end_termin3;
public String termins3;
public boolean startup_fee;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "PRODUCT_ID", referencedColumnName = "ID")
@JsonBackReference
public ProductModel product;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "POSITION_ID", referencedColumnName = "ID")
@JsonBackReference
public PositionModel position;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "CUSTOMER_ID", referencedColumnName = "ID")
@JsonBackReference
public CustomerModel customer;
public String position_name;
@Column(name = "task_description")
public String taskDescription;
@ManyToMany(mappedBy = "orders", cascade = CascadeType.PERSIST)
public List<RecipientModel> recipients;
@Column(columnDefinition = "TEXT")
public String info_for_meteorologist;
@Column(columnDefinition = "TEXT")
public String other_info;
@Transient
public OrderStatus orderStatus;
public void assignStatus(DateTime now) {
orderStatus = null;
if (isDeleted()) {
orderStatus = OrderStatus.DELETED;
} else if (isNew(now)) {
orderStatus = OrderStatus.NEW;
} else if (isExpired(now)) {
orderStatus = OrderStatus.EXPIRED;
} else if (isDormant(now)) {
orderStatus = OrderStatus.DORMANT;
} else if (isActiveNow(now)) {
orderStatus = OrderStatus.ACTIVE;
}
}
public Date findEndDate() {
if (start_date3 != null && end_date3 == null) return null; // no end
if (start_date3 == null && start_date2 != null && end_date2 == null) return null; // no end
if (start_date2 == null && start_date1 != null && end_date1 == null) return null;
if (end_date3 != null) return end_date3;
if (end_date2 != null) return end_date2;
if (end_date1 != null) return end_date1;
return null;
}
/**
* Returns the set of Termins an order is active for a particular day
*
* @param date
* @return
*/
public Set<Integer> getActiveTerminsForDayContainingTimestamp(DateTime date) {
logger.info("start getActiveTerminsForDayContainingTimestamp()");
DateTime startOfDay = date.withTimeAtStartOfDay();
logger.info("isActiveToday {}", isActiveToday(startOfDay));
if (!isActiveToday(startOfDay)) {
return null;
} else {
Set<Integer> result = null;
DateTimeZone tz = PoseidonService.getTimeZone();
DateTime start1 = new DateTime(start_date1, tz).withTimeAtStartOfDay();
DateTime start2 = new DateTime(start_date2, tz).withTimeAtStartOfDay();
DateTime start3 = new DateTime(start_date3, tz).withTimeAtStartOfDay();
DateTime end1 = new DateTime(end_date1, tz).withTime(23, 0, 0, 0);
DateTime end2 = new DateTime(end_date2, tz).withTime(23, 0, 0, 0);
DateTime end3 = new DateTime(end_date3, tz).withTime(23, 0, 0, 0);
boolean periodIsActiveThisDay = true;
Integer startTermin = null;
Integer endTermin = null;
if (isInPeriod(date, 3)) {
result = parseTermins(termins3);
if (start3.equals(date) && start_termin3 != null && !result.contains(start_termin3)) {
startTermin = start_termin3;
}
if (end3.equals(date) && end_termin3 != null && !result.contains(end_termin3)) {
endTermin = end_termin3;
}
} else if (isInPeriod(date, 2)) {
result = parseTermins(termins2);
if (start2.equals(date) && start_termin2 != null && !result.contains(start_termin2)) {
startTermin = start_termin2;
}
if (end2.equals(date) && end_termin2 != null && !result.contains(end_termin2)) {
endTermin = end_termin2;
}
} else if (isInPeriod(date, 1)) {
logger.info("isInPeriod(date, 1) = {}", isInPeriod(date, 1));
result = parseTermins(termins1);
if (start1.equals(date) && start_termin1 != null && !result.contains(start_termin1)) {
startTermin = start_termin1;
}
if (end1.equals(date) && end_termin1 != null && !result.contains(end_termin1)) {
endTermin = end_termin2;
}
}
if ( startTermin != null) result.add(startTermin);
if ( endTermin != null) result.add(endTermin);
return result;
}
}
public Set<Integer> parseTermins(String terminString) {
TreeSet<Integer> result = new TreeSet<>();
if (terminString != null && !terminString.isEmpty()) {
String[] terminArray = terminString.split(",");
for (int i = 0; i < terminArray.length; i++) {
String termin = terminArray[i];
try {
Integer terminValue = Integer.parseInt(termin);
if (terminValue != null && !result.contains(terminValue)) {
result.add(terminValue);
}
} catch (NumberFormatException nfe) {
// ignore
logger.warn("Ugyldig verdi for termin {} på ordre {},kan ikke konvertere til integer", termin, id);
}
}
}
return result;
}
/*
An order is 'new' until the day delivery starts
*/
private boolean isNew(DateTime now) {
if (start_date1 == null) return false;
DateTime start = new DateTime(start_date1, PoseidonService.getTimeZone()).withTimeAtStartOfDay();
return now.isBefore(start);
}
/*
An order is dormant if all delivery periods are unspecified, or if 'now' is
outside the start and end of all of the periods
*/
private boolean isDormant(DateTime now) {
boolean dormant = false;
DateTimeZone tz = PoseidonService.getTimeZone();
DateTime start1 = start_date1 != null ? new DateTime(start_date1, tz).withTimeAtStartOfDay() : null;
DateTime start2 = start_date2 != null ? new DateTime(start_date2, tz).withTimeAtStartOfDay() : null;
DateTime start3 = start_date3 != null ? new DateTime(start_date3, tz).withTimeAtStartOfDay() : null;
DateTime end1 = end_date1 != null ? new DateTime(end_date1, tz).plusDays(1).withTimeAtStartOfDay(): null;
DateTime end2 = end_date2 != null ? new DateTime(end_date2, tz).plusDays(1).withTimeAtStartOfDay(): null;
DateTime end3 = end_date3 != null ? new DateTime(end_date3, tz).plusDays(1).withTimeAtStartOfDay(): null;
if (start1 == null && start2 == null && start3 == null &&
end1 == null && end2 == null && end3 == null) {
dormant = true;
} else if (end1 != null && start2 != null && now.isBefore(start2) && now.isAfter(end1)) {
dormant = true;
} else if (end2 != null && start3 != null && now.isBefore(start3) && now.isAfter(end2)) {
dormant = true;
} else if (end1 != null && start1 == null && now.isBefore(end1)) {
dormant = true;
} else if (end2 != null && start2 == null && now.isBefore(end2)) {
dormant = true;
} else if (end3 != null && start3 == null && now.isBefore(end3)) {
dormant = true;
}
return dormant;
}
/*
An order is active if 'now' falls within one of the delivery dates.
*/
private boolean isActiveNow(DateTime now) {
boolean active = false;
DateTimeZone tz = PoseidonService.getTimeZone();
DateTime start1 = start_date1 != null ? new DateTime(start_date1, tz).withTimeAtStartOfDay() : null;
DateTime start2 = start_date2 != null ? new DateTime(start_date2, tz).withTimeAtStartOfDay() : null;
DateTime start3 = start_date3 != null ? new DateTime(start_date3, tz).withTimeAtStartOfDay() : null;
DateTime end1 = end_date1 != null ? new DateTime(end_date1, tz).plusDays(1).withTimeAtStartOfDay() : null;
DateTime end2 = end_date2 != null ? new DateTime(end_date2, tz).plusDays(1).withTimeAtStartOfDay() : null;
DateTime end3 = end_date3 != null ? new DateTime(end_date3, tz).plusDays(1).withTimeAtStartOfDay() : null;
if (start3 != null && end3 == null && start3.isBefore(now)) active = true;
else if (start3 != null && end3 != null && start3.isBefore(now) && end3.isAfter(now)) active = true;
else if (start2 != null && end2 == null && start2.isBefore(now)) active = true;
else if (start2 != null && end2 != null && start2.isBefore(now) && end2.isAfter(now)) active = true;
else if (start1 != null && end1 == null && (start1.isBefore(now) || start1.isEqual(now))) active = true;
else if (start1 != null && end1 != null && (start1.isBefore(now) || start1.isEqual(now) && end1.isAfter(now)))
active = true;
return active;
}
private boolean isActiveToday(DateTime now) {
boolean active = false;
DateTimeZone tz = PoseidonService.getTimeZone();
DateTime start1 = new DateTime(start_date1, tz).withTimeAtStartOfDay();
DateTime start2 = new DateTime(start_date2, tz).withTimeAtStartOfDay();
DateTime start3 = new DateTime(start_date3, tz).withTimeAtStartOfDay();
DateTime end1 = new DateTime(end_date1, tz).plusDays(1).withTimeAtStartOfDay();
DateTime end2 = new DateTime(end_date2, tz).plusDays(1).withTimeAtStartOfDay();
DateTime end3 = new DateTime(end_date3, tz).plusDays(1).withTimeAtStartOfDay();
if (start3 != null && end3 == null && start3.isBefore(now)) active = true;
else if (start3 != null && end3 != null && (start3.isBefore(now) || start3.isEqual(now)) && end3.isAfter(now))
active = true;
else if (start2 != null && end2 == null && (start2.isBefore(now) || start2.isEqual(now))) active = true;
else if (start2 != null && end2 != null && (start2.isBefore(now) || (start2.isEqual(now)) && end2.isAfter(now)))
active = true;
else if (start1 != null && end1 == null && (start1.isBefore(now) || start1.isEqual(now))) active = true;
else if (start1 != null && end1 != null && (start1.isBefore(now) || start1.isEqual(now)) && end1.isAfter(now))
active = true;
return active;
}
/*
An order is expired if 'now' is after the latest end_date
An order is not expired if the last end is unspecified
*/
private boolean isExpired(DateTime now) {
DateTimeZone tz = PoseidonService.getTimeZone();
DateTime end1 = end_date1 != null ? new DateTime(end_date1, tz).plusDays(1).withTimeAtStartOfDay():null;
DateTime end2 = end_date2 != null ? new DateTime(end_date2, tz).plusDays(1).withTimeAtStartOfDay():null;
DateTime end3 = end_date3 != null ? new DateTime(end_date3, tz).plusDays(1).withTimeAtStartOfDay():null;
if (end3 != null && now.isAfter(end3)) return true;
if (end2 != null && start_date3 == null && now.isAfter(end2)) return true;
if (end1 != null && start_date2 == null && now.isAfter(end1)) return true;
return false;
}
/**
* Returns true if an order period is active at the specified point in time
*
* @param now
* @param period 1st, 2nd or 3rd delivery period
* @return
*/
public boolean isInPeriod(DateTime now, int period) {
DateTimeZone tz = PoseidonService.getTimeZone();
boolean result = false;
DateTime start;
DateTime end;
switch (period) {
case 1:
start = start_date1 != null ? new DateTime(start_date1, tz).withTimeAtStartOfDay() : null;
end = end_date1 != null ? new DateTime(end_date1, tz).plusDays(1).withTimeAtStartOfDay() : null;
break;
case 2:
start = start_date2 != null ? new DateTime(start_date2, tz).withTimeAtStartOfDay() : null;
end = end_date2 != null ? new DateTime(end_date2, tz).plusDays(1).withTimeAtStartOfDay() : null;
break;
case 3:
start = start_date3 != null ? new DateTime(start_date3, tz).withTimeAtStartOfDay() : null;
end = end_date3 != null ? new DateTime(end_date3, tz).plusDays(1).withTimeAtStartOfDay() : null;
break;
default:
throw new PoseidonException(Http.Status.INTERNAL_SERVER_ERROR, "Feilen har blitt logget.");
}
if (start != null) {
if (end == null) {
result = start.isBefore(now) || start.isEqual(now);
} else {
result = (start.isBefore(now) || start.isEqual(now)) && end.isAfter(now);
}
}
return result;
}
public static OrderModel findByMetref(String metref) {
return find.fetch("customer").fetch("product").where().ieq("met_ref", metref).findUnique();
}
}