stock forecast query exposed

This commit is contained in:
Michał Michaluk
2017-12-08 08:40:48 +01:00
parent 33b27bfc6c
commit 2aafb8dba4
10 changed files with 194 additions and 10 deletions

View File

@@ -6,7 +6,6 @@ import java.util.List;
@Value
public class ProductDescription {
String refNo;
String matNum;
List<String> names;
}

View File

@@ -2,12 +2,21 @@ package pl.com.bottega.factory.product.management;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
@RepositoryRestResource(
path = "product/management/descriptions",
collectionResourceRel = "descriptions of products")
public interface ProductDescriptionDao extends JpaRepository<ProductDescriptionEntity, Long> {
public interface ProductDescriptionDao extends JpaRepository<ProductDescriptionEntity, String> {
@RestResource(exported = false)
default Optional<ProductDescription> description(String refNo) {
return Optional.ofNullable(findOne(refNo))
.map(ProductDescriptionEntity::getDescription);
}
}

View File

@@ -11,19 +11,19 @@ import javax.persistence.*;
@Entity(name = "ProductDescription")
@Data
@NoArgsConstructor
@EqualsAndHashCode(of = "id")
@EqualsAndHashCode(of = "refNo")
public class ProductDescriptionEntity {
@Id
@Column
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String refNo;
@Column
@Convert(converter = DescriptionAsJson.class)
ProductDescription description;
public ProductDescriptionEntity(ProductDescription description) {
public ProductDescriptionEntity(String refNo, ProductDescription description) {
this.refNo = refNo;
this.description = description;
}

View File

@@ -0,0 +1,18 @@
package pl.com.bottega.factory.production.planning.projection;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.stereotype.Repository;
import pl.com.bottega.tools.ProjectionDao;
import java.time.Instant;
import java.time.LocalDate;
import java.util.List;
@Repository
@RepositoryRestResource(
path = "production/planning/outputs/daily",
collectionResourceRel = "forecast of production daily outputs")
public interface ProductionDailyOutputDao extends ProjectionDao<ProductionDailyOutputEntity, Long> {
List<ProductionDailyOutputEntity> findByRefNoAndDateGreaterThanEqual(String refNo, LocalDate date);
}

View File

@@ -0,0 +1,31 @@
package pl.com.bottega.factory.production.planning.projection;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDate;
@Entity(name = "ProductionDailyOutput")
@Getter
@NoArgsConstructor
@EqualsAndHashCode(of = "id")
public class ProductionDailyOutputEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
private String refNo;
@Column
private LocalDate date;
@Column
private long output;
public ProductionDailyOutputEntity(String refNo, LocalDate date, long output) {
this.refNo = refNo;
this.date = date;
this.output = output;
}
}

View File

@@ -0,0 +1,28 @@
package pl.com.bottega.factory.stock.forecast;
import lombok.Builder;
import lombok.Singular;
import lombok.Value;
import pl.com.bottega.factory.product.management.ProductDescription;
import java.time.LocalDate;
import java.util.List;
@Value
@Builder
class StockForecast {
String refNo;
ProductDescription description;
@Singular
List<DailyForecast> forecasts;
@Value
static class DailyForecast {
LocalDate date;
long stock;
long withLocked;
long demand;
long output;
}
}

View File

@@ -0,0 +1,23 @@
package pl.com.bottega.factory.stock.forecast;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/stock/forecasts")
@AllArgsConstructor
class StockForecastController {
private final StockForecastQuery query;
@RequestMapping(value = "/{refNo}", method = RequestMethod.GET)
@Transactional(readOnly = true)
ResponseEntity<StockForecast> get(@PathVariable("refNo") String refNo) {
return ResponseEntity.ok(query.get(refNo));
}
}

View File

@@ -0,0 +1,76 @@
package pl.com.bottega.factory.stock.forecast;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import pl.com.bottega.factory.demand.forecasting.projection.CurrentDemandDao;
import pl.com.bottega.factory.demand.forecasting.projection.CurrentDemandEntity;
import pl.com.bottega.factory.product.management.ProductDescription;
import pl.com.bottega.factory.product.management.ProductDescriptionDao;
import pl.com.bottega.factory.product.management.ProductDescriptionEntity;
import pl.com.bottega.factory.production.planning.projection.ProductionDailyOutputDao;
import pl.com.bottega.factory.production.planning.projection.ProductionDailyOutputEntity;
import pl.com.bottega.factory.shortages.prediction.calculation.CurrentStock;
import pl.com.bottega.factory.stock.forecast.StockForecast.StockForecastBuilder;
import pl.com.bottega.factory.warehouse.WarehouseService;
import java.time.Clock;
import java.time.LocalDate;
import java.util.Map;
import java.util.Optional;
import static java.util.stream.Collectors.toMap;
@Component
@Transactional(readOnly = true)
@AllArgsConstructor
class StockForecastQuery {
private final WarehouseService stocks;
private final CurrentDemandDao demands;
private final ProductionDailyOutputDao outputs;
private final ProductDescriptionDao descriptions;
private final Clock clock;
StockForecast get(String refNo) {
CurrentStock stock = stocks.forRefNo(refNo);
LocalDate today = LocalDate.now(clock);
return build(refNo, today, Optional.ofNullable(descriptions.findOne(refNo))
.map(ProductDescriptionEntity::getDescription).orElse(null), stock,
this.demands
.findByRefNoAndDateGreaterThanEqual(refNo, today).stream()
.collect(toMap(
CurrentDemandEntity::getDate,
CurrentDemandEntity::getLevel
)),
this.outputs
.findByRefNoAndDateGreaterThanEqual(refNo, today).stream()
.collect(toMap(
ProductionDailyOutputEntity::getDate,
ProductionDailyOutputEntity::getOutput
)));
}
private StockForecast build(String refNo, LocalDate today,
ProductDescription description, CurrentStock stock,
Map<LocalDate, Long> demands,
Map<LocalDate, Long> outputs) {
LocalDate stopAtDay = today.plusDays(15);
long level = stock.getLevel();
StockForecastBuilder builder = StockForecast.builder();
for (LocalDate date = today; date.isBefore(stopAtDay); date = date.plusDays(1)) {
builder.forecast(
new StockForecast.DailyForecast(
date,
level,
level + stock.getLocked(),
demands.getOrDefault(date, 0L),
outputs.getOrDefault(date, 0L)
));
}
return builder
.refNo(refNo)
.description(description)
.build();
}
}

View File

@@ -18,8 +18,8 @@ class ProductDescriptionPersistenceTest extends Specification {
def "verify access to ProductDescription data"() {
given:
dao.save(new ProductDescriptionEntity(
new ProductDescription("3009000", "461952398951", singletonList("PROWAD.POJ.NA JARZ.ESSENT"))))
dao.save(new ProductDescriptionEntity("3009000",
new ProductDescription("461952398951", singletonList("PROWAD.POJ.NA JARZ.ESSENT"))))
when:
def entities = dao.findAll()
@@ -27,6 +27,6 @@ class ProductDescriptionPersistenceTest extends Specification {
then:
entities.size() == 1
entities.get(0).description ==
new ProductDescription("3009000", "461952398951", singletonList("PROWAD.POJ.NA JARZ.ESSENT"))
new ProductDescription("461952398951", singletonList("PROWAD.POJ.NA JARZ.ESSENT"))
}
}

View File

@@ -28,7 +28,7 @@ class ShortagePredictionProcessORMRepositoryTest extends Specification {
ShortagePredictionProcessORMRepository repository
def setup() {
dao.deleteAllInBatch()
dao.deleteAll()
repository = new ShortagePredictionProcessORMRepository(
dao, forecasts, notifications
)