ProductDemand persistence + draft of events projection
This commit is contained in:
@@ -2,7 +2,9 @@ package pl.com.bottega.factory.demand.forecasting;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import pl.com.bottega.tools.TechnicalId;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.LocalDate;
|
||||
@@ -22,4 +24,76 @@ public class DemandEntity {
|
||||
@Column
|
||||
LocalDate date;
|
||||
|
||||
@Column
|
||||
Long level;
|
||||
@Column
|
||||
Demand.Schema schema;
|
||||
|
||||
@Column
|
||||
Long adjustmentLevel;
|
||||
@Column
|
||||
Demand.Schema adjustmentSchema;
|
||||
@Column
|
||||
boolean adjustmentStrong;
|
||||
|
||||
DemandEntity(ProductDemandEntity product, LocalDate date) {
|
||||
this.product = product;
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
static Demand getDemand(DemandEntity entity) {
|
||||
return entity == null || entity.level == null
|
||||
? null
|
||||
: Demand.of(entity.level, entity.schema);
|
||||
}
|
||||
|
||||
static Adjustment getAdjustment(DemandEntity entity) {
|
||||
return entity == null || entity.adjustmentLevel == null
|
||||
? null
|
||||
: new Adjustment(
|
||||
Demand.of(entity.adjustmentLevel, entity.adjustmentSchema),
|
||||
entity.adjustmentStrong
|
||||
);
|
||||
}
|
||||
|
||||
void setDemand(Demand demand) {
|
||||
if (demand == null) {
|
||||
setLevel(null);
|
||||
setSchema(null);
|
||||
} else {
|
||||
setLevel(demand.getLevel());
|
||||
setSchema(demand.getSchema());
|
||||
}
|
||||
}
|
||||
|
||||
void setAdjustment(Adjustment adjustment) {
|
||||
if (adjustment == null) {
|
||||
setAdjustmentLevel(null);
|
||||
setAdjustmentSchema(null);
|
||||
setAdjustmentStrong(false);
|
||||
} else {
|
||||
setAdjustmentLevel(adjustment.getDemand().getLevel());
|
||||
setAdjustmentSchema(adjustment.getDemand().getSchema());
|
||||
setAdjustmentStrong(adjustment.isStrong());
|
||||
}
|
||||
}
|
||||
|
||||
DemandEntityId createId() {
|
||||
return new DemandEntityId(product.getRefNo(), date, id);
|
||||
}
|
||||
|
||||
@Getter
|
||||
static class DemandEntityId extends DailyId implements TechnicalId {
|
||||
|
||||
Long id;
|
||||
|
||||
DemandEntityId(String refNo, LocalDate date) {
|
||||
super(refNo, date);
|
||||
}
|
||||
|
||||
DemandEntityId(String refNo, LocalDate date, Long id) {
|
||||
super(refNo, date);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,18 @@ package pl.com.bottega.factory.demand.forecasting;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
import pl.com.bottega.factory.demand.forecasting.projection.CurrentDemandsProjection;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
class DemandEventsMapping implements DemandEvents {
|
||||
|
||||
CurrentDemandsProjection demands;
|
||||
//ShortagePredictionMapping predictions;
|
||||
|
||||
@Override
|
||||
public void emit(DemandedLevelsChanged event) {
|
||||
demands.emit(event);
|
||||
//predictions.emit(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,22 @@ package pl.com.bottega.factory.demand.forecasting;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import pl.com.bottega.factory.demand.forecasting.DailyDemand.DemandUpdated;
|
||||
import pl.com.bottega.factory.demand.forecasting.DemandEntity.DemandEntityId;
|
||||
import pl.com.bottega.factory.demand.forecasting.persistence.DemandDao;
|
||||
import pl.com.bottega.factory.demand.forecasting.persistence.ProductDemandDao;
|
||||
import pl.com.bottega.factory.product.management.RefNoId;
|
||||
import pl.com.bottega.tools.TechnicalId;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.LockModeType;
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
@@ -13,13 +25,62 @@ class DemandRepository {
|
||||
|
||||
private Clock clock;
|
||||
private DemandEventsMapping events;
|
||||
private EntityManager em;
|
||||
private ProductDemandDao rootDao;
|
||||
private DemandDao demandDao;
|
||||
|
||||
ProductDemand get(String refNo) {
|
||||
return null;
|
||||
ProductDemandEntity root = rootDao.findByRefNo(refNo);
|
||||
RefNoId id = root.createId();
|
||||
|
||||
Map<LocalDate, DemandEntity> data =
|
||||
demandDao.findByProductRefNoAndDateGreaterThanEqual(refNo, LocalDate.now(clock)).stream()
|
||||
.collect(toMap(
|
||||
DemandEntity::getDate,
|
||||
Function.identity()
|
||||
));
|
||||
|
||||
Demands demands = new Demands();
|
||||
demands.fetch = date -> map(refNo, date, data, demands);
|
||||
|
||||
return new ProductDemand(id, demands, clock, events);
|
||||
}
|
||||
|
||||
private DailyDemand map(String refNo, LocalDate date,
|
||||
Map<LocalDate, DemandEntity> data,
|
||||
Demands demands) {
|
||||
return ofNullable(data.get(date))
|
||||
.map(entity -> new DailyDemand(
|
||||
entity.createId(),
|
||||
demands,
|
||||
DemandEntity.getDemand(entity),
|
||||
DemandEntity.getAdjustment(entity)))
|
||||
.orElseGet(() -> new DailyDemand(
|
||||
new DemandEntityId(refNo, date),
|
||||
demands,
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
|
||||
void save(ProductDemand model) {
|
||||
ProductDemandEntity root = rootDao.findOne(TechnicalId.get(model.id));
|
||||
if (model.demands.getUpdates().size() > 0) {
|
||||
em.lock(root, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
|
||||
}
|
||||
for (DemandUpdated updated : model.demands.getUpdates()) {
|
||||
DemandEntity entity;
|
||||
if (TechnicalId.isPersisted(updated.getId())) {
|
||||
entity = demandDao.getOne(TechnicalId.get(updated.getId()));
|
||||
} else {
|
||||
entity = new DemandEntity(root, updated.getId().getDate());
|
||||
}
|
||||
entity.setDemand(updated.getDocumented());
|
||||
entity.setAdjustment(updated.getAdjustment());
|
||||
|
||||
if (!TechnicalId.isPersisted(updated.getId())) {
|
||||
demandDao.save(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package pl.com.bottega.factory.demand.forecasting;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class DemandService {
|
||||
|
||||
private DemandRepository repository;
|
||||
@@ -13,9 +9,9 @@ public class DemandService {
|
||||
public void process(Document document) {
|
||||
ProductDemand model = repository.get(document.getRefNo());
|
||||
model.process(document);
|
||||
repository.save(model);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void adjust(AdjustDemand adjustDemand) {
|
||||
ProductDemand model = repository.get(adjustDemand.getRefNo());
|
||||
model.adjust(adjustDemand);
|
||||
|
||||
@@ -2,7 +2,10 @@ package pl.com.bottega.factory.demand.forecasting;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import pl.com.bottega.factory.product.management.RefNoId;
|
||||
import pl.com.bottega.tools.TechnicalId;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@@ -20,4 +23,28 @@ public class ProductDemandEntity {
|
||||
@Column
|
||||
String refNo;
|
||||
|
||||
public ProductDemandEntity(String refNo) {
|
||||
this.refNo = refNo;
|
||||
}
|
||||
|
||||
ProductDemandEntityId createId() {
|
||||
return new ProductDemandEntityId(refNo, id, version);
|
||||
}
|
||||
|
||||
@Getter
|
||||
static class ProductDemandEntityId extends RefNoId implements TechnicalId {
|
||||
|
||||
Long id;
|
||||
Long version;
|
||||
|
||||
ProductDemandEntityId(String refNo) {
|
||||
super(refNo);
|
||||
}
|
||||
|
||||
ProductDemandEntityId(String refNo, long id, Long version) {
|
||||
super(refNo);
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package pl.com.bottega.factory.demand.forecasting.projection;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public interface CurrentDemandDao {
|
||||
|
||||
void save(CurrentDemandEntity entity);
|
||||
|
||||
List<CurrentDemandEntity> findRefNoFromDate(String refNo, Instant now);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package pl.com.bottega.factory.demand.forecasting.projection;
|
||||
|
||||
import pl.com.bottega.factory.demand.forecasting.Demand;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Data
|
||||
public class CurrentDemandEntity {
|
||||
private final String refNo;
|
||||
private final LocalDate date;
|
||||
private final long level;
|
||||
private final Demand.Schema schema;
|
||||
|
||||
public CurrentDemandEntity(String refNo, LocalDate date, long level, Demand.Schema schema) {
|
||||
this.refNo = refNo;
|
||||
this.date = date;
|
||||
this.level = level;
|
||||
this.schema = schema;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package pl.com.bottega.factory.demand.forecasting.projection;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import pl.com.bottega.factory.demand.forecasting.DemandEvents;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class CurrentDemandsProjection implements DemandEvents {
|
||||
|
||||
@Override
|
||||
public void emit(DemandedLevelsChanged event) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package pl.com.bottega.factory.demand.forecasting.projection;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public interface DeliveryForecastDao {
|
||||
|
||||
void save(DeliveryForecastEntity entity);
|
||||
|
||||
List<DeliveryForecastEntity> findRefNoFrom(String refNo, Instant instant, int daysAhead);
|
||||
|
||||
void delete(String refNo, LocalDate date);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package pl.com.bottega.factory.demand.forecasting.projection;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class DeliveryForecastEntity {
|
||||
private final String refNo;
|
||||
private final LocalDateTime time;
|
||||
private final long level;
|
||||
|
||||
public DeliveryForecastEntity(String refNo, LocalDateTime time, long level) {
|
||||
this.refNo = refNo;
|
||||
this.time = time;
|
||||
this.level = level;
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.test.annotation.Commit
|
||||
import pl.com.bottega.factory.demand.forecasting.persistence.DemandDao
|
||||
import pl.com.bottega.factory.demand.forecasting.persistence.ProductDemandDao
|
||||
import spock.lang.PendingFeature
|
||||
import spock.lang.Specification
|
||||
|
||||
import javax.persistence.EntityManager
|
||||
import javax.transaction.Transactional
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
@@ -22,6 +22,8 @@ class DemandRepositoryTest extends Specification {
|
||||
def clock = Clock.fixed(Instant.now(), ZoneId.systemDefault())
|
||||
def events = Mock(DemandEventsMapping)
|
||||
@Autowired
|
||||
EntityManager em
|
||||
@Autowired
|
||||
ProductDemandDao rootDao
|
||||
@Autowired
|
||||
DemandDao demandDao
|
||||
@@ -33,10 +35,9 @@ class DemandRepositoryTest extends Specification {
|
||||
def setup() {
|
||||
demandDao.deleteAllInBatch()
|
||||
rootDao.deleteAllInBatch()
|
||||
repository = new DemandRepository(clock, events, rootDao, demandDao)
|
||||
repository = new DemandRepository(clock, events, em, rootDao, demandDao)
|
||||
}
|
||||
|
||||
@PendingFeature
|
||||
def "persists new demand"() {
|
||||
given:
|
||||
rootDao.save(new ProductDemandEntity("3009000"))
|
||||
@@ -52,7 +53,6 @@ class DemandRepositoryTest extends Specification {
|
||||
demandDao.findAll().size() == 1
|
||||
}
|
||||
|
||||
@PendingFeature
|
||||
def "updates existing demand"() {
|
||||
given:
|
||||
def root = rootDao.save(new ProductDemandEntity("3009000"))
|
||||
@@ -73,7 +73,6 @@ class DemandRepositoryTest extends Specification {
|
||||
demand.every { it.getAdjustmentLevel() == 2000 }
|
||||
}
|
||||
|
||||
@PendingFeature
|
||||
def "doesn't fetch historical data"() {
|
||||
given:
|
||||
def root = rootDao.save(new ProductDemandEntity("3009000"))
|
||||
|
||||
Reference in New Issue
Block a user