manual testing of api

This commit is contained in:
Michał Michaluk
2017-12-10 21:47:02 +01:00
parent 04970f2caa
commit 537b83da43
43 changed files with 445 additions and 139 deletions

View File

@@ -5,12 +5,14 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;
import pl.com.bottega.factory.shortages.prediction.calculation.CurrentStock;
import org.springframework.scheduling.annotation.EnableScheduling;
import pl.com.bottega.factory.shortages.prediction.calculation.Stock;
import pl.com.bottega.factory.warehouse.WarehouseService;
import java.time.Clock;
@SpringBootApplication
@EnableScheduling
@EntityScan(
basePackageClasses = {AppConfiguration.class, Jsr310JpaConverters.class}
)
@@ -23,7 +25,7 @@ public class AppConfiguration {
@Bean
public WarehouseService warehouseService() {
// mocked facade for external service
return refNo -> new CurrentStock(0, 0);
return refNo -> new Stock(1200, 700);
}
@Bean

View File

@@ -4,7 +4,6 @@ import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import pl.com.bottega.factory.delivery.planning.definition.DeliveryPlannerDefinitionDao;
import pl.com.bottega.factory.delivery.planning.definition.DeliveryPlannerDefinitionEntity;
import pl.com.bottega.factory.product.management.RefNoId;
import java.util.Collections;
@@ -12,7 +11,7 @@ import static java.util.Optional.ofNullable;
@Component
@AllArgsConstructor
public class DeliveryAutoPlannerRepository {
public class DeliveryAutoPlannerORMRepository {
DeliveryPlannerDefinitionDao dao;

View File

@@ -1,7 +1,6 @@
package pl.com.bottega.factory.delivery.planning.definition;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
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;
@@ -9,7 +8,7 @@ import org.springframework.stereotype.Repository;
@Repository
@RepositoryRestResource(
path = "delivery-definitions", collectionResourceRel = "delivery-definitions")
public interface DeliveryPlannerDefinitionDao extends CrudRepository<DeliveryPlannerDefinitionEntity, Long> {
public interface DeliveryPlannerDefinitionDao extends JpaRepository<DeliveryPlannerDefinitionEntity, Long> {
@RestResource(path = "refNos", rel = "refNos")
DeliveryPlannerDefinitionEntity findByRefNo(@Param("refNo") String refNo);
DeliveryPlannerDefinitionEntity findByRefNo(String refNo);
}

View File

@@ -1,6 +1,7 @@
package pl.com.bottega.factory.delivery.planning.definition;
import lombok.AllArgsConstructor;
import org.springframework.data.rest.core.annotation.HandleAfterCreate;
import org.springframework.data.rest.core.annotation.HandleAfterSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.stereotype.Component;
@@ -14,6 +15,7 @@ public class DeliveryPlannerDefinitionEventsMapping {
private DeliveryForecastProjection projection;
@HandleAfterSave
@HandleAfterCreate
public void handle(DeliveryPlannerDefinitionEntity entity) {
projection.handleDeliveryPlannerDefinitionChange(entity.getRefNo());
}

View File

@@ -3,22 +3,23 @@ package pl.com.bottega.factory.delivery.planning.projection;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository;
import pl.com.bottega.tools.ProjectionDao;
import pl.com.bottega.tools.ProjectionRepository;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Repository
@RepositoryRestResource(path = "delivery-forecasts", collectionResourceRel = "delivery-forecasts")
public interface DeliveryForecastDao extends ProjectionDao<DeliveryForecastEntity, Long> {
public interface DeliveryForecastDao extends ProjectionRepository<DeliveryForecastEntity, Long> {
@RestResource(exported = false)
List<DeliveryForecastEntity> findByRefNoAndDateGreaterThanEqual(String refNo, Instant instant);
@RestResource(path = "refNos", rel = "refNos")
List<DeliveryForecastEntity> findByRefNoAndTimeGreaterThanEqual(String refNo, LocalDateTime from);
@RestResource(exported = false)
void deleteByRefNoAndDate(String refNo, LocalDate date);
@RestResource(exported = false)
void deleteByRefNo(String refNo);
}

View File

@@ -3,7 +3,7 @@ package pl.com.bottega.factory.delivery.planning.projection;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import pl.com.bottega.factory.delivery.planning.DeliveryAutoPlanner;
import pl.com.bottega.factory.delivery.planning.DeliveryAutoPlannerRepository;
import pl.com.bottega.factory.delivery.planning.DeliveryAutoPlannerORMRepository;
import pl.com.bottega.factory.demand.forecasting.Demand;
import pl.com.bottega.factory.demand.forecasting.DemandEvents;
import pl.com.bottega.factory.demand.forecasting.projection.CurrentDemandDao;
@@ -20,7 +20,7 @@ public class DeliveryForecastProjection implements DemandEvents {
private final Clock clock;
private final DeliveryForecastDao forecastDao;
private final CurrentDemandDao demandDao;
private final DeliveryAutoPlannerRepository planners;
private final DeliveryAutoPlannerORMRepository planners;
@Override
public void emit(DemandedLevelsChanged event) {

View File

@@ -1,5 +1,6 @@
package pl.com.bottega.factory.demand.forecasting;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import pl.com.bottega.factory.delivery.planning.projection.DeliveryForecastProjection;
@@ -8,11 +9,12 @@ import pl.com.bottega.factory.shortages.prediction.ShortagePredictionEventsMappi
@Lazy
@Component
@AllArgsConstructor
class DemandEventsMapping implements DemandEvents {
CurrentDemandProjection demands;
DeliveryForecastProjection deliveries;
ShortagePredictionEventsMapping predictions;
private final CurrentDemandProjection demands;
private final DeliveryForecastProjection deliveries;
private final ShortagePredictionEventsMapping predictions;
@Override
public void emit(DemandedLevelsChanged event) {

View File

@@ -15,6 +15,7 @@ public class DemandService {
public void process(Document document) {
ProductDemand model = repository.get(document.getRefNo());
model.process(document);
repository.save(model);
}
public void adjust(AdjustDemand adjustDemand) {

View File

@@ -0,0 +1,15 @@
package pl.com.bottega.factory.demand.forecasting.command;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository;
import pl.com.bottega.tools.CommandRepository;
import java.time.LocalDate;
@Repository
@RepositoryRestResource(path = "demand-adjustments", collectionResourceRel = "demand-adjustments")
public interface DemandAdjustmentDao extends CommandRepository<DemandAdjustmentEntity, Long> {
@RestResource(exported = false)
void deleteByCleanAfterGreaterThanEqual(LocalDate date);
}

View File

@@ -0,0 +1,41 @@
package pl.com.bottega.factory.demand.forecasting.command;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import pl.com.bottega.factory.demand.forecasting.AdjustDemand;
import pl.com.bottega.tools.JsonConverter;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDate;
@Entity(name = "DemandAdjustment")
@Getter
@NoArgsConstructor
@EqualsAndHashCode(of = "id")
public class DemandAdjustmentEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
private String note;
@Column
private String customerRepresentative;
@Column
@Setter
private LocalDate cleanAfter;
@Column(length = 4096)
@Convert(converter = AdjustDemandAsJson.class)
private AdjustDemand adjustment;
public static class AdjustDemandAsJson extends JsonConverter<AdjustDemand> {
public AdjustDemandAsJson() {
super(AdjustDemand.class);
}
}
}

View File

@@ -0,0 +1,42 @@
package pl.com.bottega.factory.demand.forecasting.command;
import lombok.AllArgsConstructor;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import pl.com.bottega.factory.demand.forecasting.DemandService;
import java.time.Clock;
import java.time.LocalDate;
@Component
@AllArgsConstructor
@RepositoryEventHandler
public class Handler {
private final DemandService service;
private final DemandAdjustmentDao adjustments;
private final Clock clock;
@HandleBeforeCreate
@HandleBeforeSave
public void adjustDemand(DemandAdjustmentEntity resource) {
LocalDate latest = resource.getAdjustment()
.latestAdjustment()
.orElse(LocalDate.now(clock));
resource.setCleanAfter(latest.plusDays(7));
service.adjust(resource.getAdjustment());
}
@Scheduled(cron = "0 0 12 * * ?")
@EventListener(ApplicationReadyEvent.class)
@Transactional
public void clean() {
adjustments.deleteByCleanAfterGreaterThanEqual(LocalDate.now(clock));
}
}

View File

@@ -11,6 +11,5 @@ import java.util.List;
@Repository
@RestResource(exported = false)
public interface DemandDao extends JpaRepository<DemandEntity, Long> {
List<DemandEntity> findByProductRefNoAndDateGreaterThanEqual(String refNo, LocalDate now);
}

View File

@@ -8,8 +8,5 @@ import pl.com.bottega.factory.demand.forecasting.ProductDemandEntity;
@Repository
@RestResource(exported = false)
public interface ProductDemandDao extends JpaRepository<ProductDemandEntity, Long> {
ProductDemandEntity findById(Long id);
ProductDemandEntity findByRefNo(String refNo);
}

View File

@@ -3,16 +3,15 @@ package pl.com.bottega.factory.demand.forecasting.projection;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository;
import pl.com.bottega.tools.ProjectionDao;
import pl.com.bottega.tools.ProjectionRepository;
import java.time.LocalDate;
import java.util.List;
@Repository
@RepositoryRestResource(path = "demand-forecasts", collectionResourceRel = "demand-forecasts")
public interface CurrentDemandDao extends ProjectionDao<CurrentDemandEntity, Long> {
@RestResource(exported = false)
public interface CurrentDemandDao extends ProjectionRepository<CurrentDemandEntity, Long> {
@RestResource(path = "refNos", rel = "refNos")
List<CurrentDemandEntity> findByRefNoAndDateGreaterThanEqual(String refNo, LocalDate date);
@RestResource(exported = false)

View File

@@ -1,14 +1,18 @@
package pl.com.bottega.factory.product.management;
import lombok.AllArgsConstructor;
import org.springframework.data.rest.core.annotation.HandleAfterCreate;
import org.springframework.data.rest.core.annotation.HandleAfterSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.stereotype.Component;
@Component
@AllArgsConstructor
@RepositoryEventHandler
public class ProductDescriptionEventsMapping {
@HandleAfterSave
@HandleAfterCreate
public void handle(ProductDescriptionEntity entity) {
}
}

View File

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

View File

@@ -1,15 +1,16 @@
package pl.com.bottega.factory.production.planning.projection;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository;
import pl.com.bottega.tools.ProjectionDao;
import pl.com.bottega.tools.ProjectionRepository;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List;
@Repository
@RepositoryRestResource(path = "production-outputs", collectionResourceRel = "production-outputs")
public interface ProductionOutputDao extends ProjectionDao<ProductionOutputEntity, Long> {
List<ProductionOutputEntity> findByRefNoAndStartGreaterThanEqual(String refNo, Instant instant);
public interface ProductionOutputDao extends ProjectionRepository<ProductionOutputEntity, Long> {
@RestResource(path = "refNos", rel = "refNos")
List<ProductionOutputEntity> findByRefNoAndStartGreaterThanEqual(String refNo, LocalDateTime from);
}

View File

@@ -7,14 +7,12 @@ import pl.com.bottega.factory.demand.forecasting.DemandEvents;
import pl.com.bottega.factory.shortages.prediction.monitoring.ShortagePredictionProcess;
import pl.com.bottega.factory.shortages.prediction.monitoring.ShortagePredictionProcessRepository;
import java.util.Set;
@Lazy
@Component
@AllArgsConstructor
public class ShortagePredictionEventsMapping implements DemandEvents {
private ShortagePredictionProcessRepository repository;
private final ShortagePredictionProcessRepository repository;
@Override
public void emit(DemandedLevelsChanged event) {

View File

@@ -12,9 +12,9 @@ import pl.com.bottega.factory.warehouse.WarehouseService;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toMap;
@@ -23,36 +23,36 @@ import static java.util.stream.Collectors.toMap;
@AllArgsConstructor
class ForecastORMProvider implements Forecasts {
private WarehouseService stocks;
private DeliveryForecastDao demands;
private ProductionOutputDao outputs;
private Clock clock;
private final WarehouseService stocks;
private final DeliveryForecastDao demands;
private final ProductionOutputDao outputs;
private final Clock clock;
@Override
public Forecast get(RefNoId refNo, int daysAhead) {
CurrentStock stock = stocks.forRefNo(refNo);
Stock stock = stocks.forRefNo(refNo);
Instant now = Instant.now(clock);
LocalDateTime time = now.atZone(clock.getZone()).toLocalDateTime();
Map<LocalDateTime, Long> demands = this.demands
.findByRefNoAndDateGreaterThanEqual(refNo.getRefNo(), now).stream()
.findByRefNoAndTimeGreaterThanEqual(refNo.getRefNo(), time).stream()
.collect(toMap(
DeliveryForecastEntity::getTime,
DeliveryForecastEntity::getLevel
));
List<LocalDateTime> times = new ArrayList<>(demands.keySet());
SortedSet<LocalDateTime> deliveryTimes = new TreeSet<>(demands.keySet());
Demands demand = new Demands(demands);
ProductionOutputs outputs = new ProductionForecast(
this.outputs.findByRefNoAndStartGreaterThanEqual(refNo.getRefNo(), now).stream()
this.outputs.findByRefNoAndStartGreaterThanEqual(refNo.getRefNo(), time).stream()
.map(e -> new Item(
e.getStart(),
e.getDuration(),
e.getPartsPerMinute()))
.collect(Collectors.toList())
).outputsInTimes(time, demands.keySet());
).outputsInTimes(time, deliveryTimes);
return new Forecast(refNo.getRefNo(), time, times, stock, outputs, demand);
return new Forecast(refNo.getRefNo(), time, deliveryTimes, stock, outputs, demand);
}
}

View File

@@ -1,16 +1,15 @@
package pl.com.bottega.factory.shortages.prediction.monitoring;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository;
import pl.com.bottega.tools.ProjectionDao;
import pl.com.bottega.tools.ProjectionRepository;
import java.util.Optional;
@Repository
@RepositoryRestResource(path = "shortages", collectionResourceRel = "shortages")
public interface ShortagesDao extends ProjectionDao<ShortagesEntity, Long> {
public interface ShortagesDao extends ProjectionRepository<ShortagesEntity, Long> {
@RestResource(path = "refNos", rel = "refNos")
Optional<ShortagesEntity> findByRefNo(@Param("refNo") String refNo);
Optional<ShortagesEntity> findByRefNo(String refNo);
}

View File

@@ -1,5 +1,6 @@
package pl.com.bottega.factory.stock.forecast;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Builder;
import lombok.Singular;
import lombok.Value;
@@ -8,6 +9,8 @@ import pl.com.bottega.factory.product.management.ProductDescription;
import java.time.LocalDate;
import java.util.List;
import static com.fasterxml.jackson.annotation.JsonFormat.Shape.STRING;
@Value
@Builder
class StockForecast {
@@ -19,6 +22,7 @@ class StockForecast {
@Value
static class DailyForecast {
@JsonFormat(shape = STRING, pattern = "yyyy-MM-dd")
LocalDate date;
long stock;
long withLocked;

View File

@@ -4,21 +4,21 @@ 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;
import org.springframework.web.bind.annotation.RequestParam;
import pl.com.bottega.factory.product.management.RefNoId;
//@Controller
//@RequestMapping("/stock/forecasts")
@Controller
@RequestMapping("stock-forecasts")
@AllArgsConstructor
class StockForecastController {
private final StockForecastQuery query;
@RequestMapping(value = "/{refNo}", method = RequestMethod.GET)
@RequestMapping(value = "search/refNos", method = RequestMethod.GET)
@Transactional(readOnly = true)
ResponseEntity<StockForecast> get(@PathVariable("refNo") String refNo) {
ResponseEntity<StockForecast> get(@RequestParam String refNo) {
return ResponseEntity.ok(query.get(new RefNoId(refNo)));
}
}

View File

@@ -11,7 +11,7 @@ import pl.com.bottega.factory.product.management.ProductDescriptionEntity;
import pl.com.bottega.factory.product.management.RefNoId;
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.shortages.prediction.calculation.Stock;
import pl.com.bottega.factory.stock.forecast.StockForecast.StockForecastBuilder;
import pl.com.bottega.factory.warehouse.WarehouseService;
@@ -34,10 +34,12 @@ class StockForecastQuery {
private final Clock clock;
StockForecast get(RefNoId refNo) {
CurrentStock stock = stocks.forRefNo(refNo);
Stock stock = stocks.forRefNo(refNo);
LocalDate today = LocalDate.now(clock);
return build(refNo, today, Optional.ofNullable(descriptions.findByRefNo(refNo.getRefNo()))
.map(ProductDescriptionEntity::getDescription).orElse(null), stock,
return build(refNo, today,
Optional.ofNullable(descriptions.findByRefNo(refNo.getRefNo()))
.map(ProductDescriptionEntity::getDescription).orElse(null),
stock,
this.demands
.findByRefNoAndDateGreaterThanEqual(refNo.getRefNo(), today).stream()
.collect(toMap(
@@ -49,25 +51,31 @@ class StockForecastQuery {
.collect(toMap(
ProductionDailyOutputEntity::getDate,
ProductionDailyOutputEntity::getOutput
)));
))
);
}
private StockForecast build(RefNoId refNo, LocalDate today,
ProductDescription description, CurrentStock stock,
ProductDescription description, Stock 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)) {
long demand = demands.getOrDefault(date, 0L);
long output = outputs.getOrDefault(date, 0L);
long withLocked = level + stock.getLocked();
builder.forecast(
new StockForecast.DailyForecast(
date,
level,
level + stock.getLocked(),
demands.getOrDefault(date, 0L),
outputs.getOrDefault(date, 0L)
));
withLocked,
demand,
output
)
);
level = level - demand + output;
}
return builder
.refNo(refNo.getRefNo())

View File

@@ -1,8 +1,8 @@
package pl.com.bottega.factory.warehouse;
import pl.com.bottega.factory.product.management.RefNoId;
import pl.com.bottega.factory.shortages.prediction.calculation.CurrentStock;
import pl.com.bottega.factory.shortages.prediction.calculation.Stock;
public interface WarehouseService {
CurrentStock forRefNo(RefNoId refNo);
Stock forRefNo(RefNoId refNo);
}

View File

@@ -0,0 +1,27 @@
package pl.com.bottega.tools;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository;
import java.io.Serializable;
@Repository
public interface CommandRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
@Override
@RestResource(exported = false)
void delete(ID id);
@Override
@RestResource(exported = false)
void delete(T entity);
@Override
@RestResource(exported = false)
void delete(Iterable<? extends T> entities);
@Override
@RestResource(exported = false)
void deleteAll();
}

View File

@@ -7,7 +7,7 @@ import org.springframework.stereotype.Repository;
import java.io.Serializable;
@Repository
public interface ProjectionDao<T, ID extends Serializable> extends CrudRepository<T, ID> {
public interface ProjectionRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
@Override
@RestResource(exported = false)

View File

@@ -1,4 +1,11 @@
Aggregate Persistence:
1) normalisation of aggregate entities (Daily Demand)
2) optimistic force increment
3) mapper (Daily Demand)
4) wrapper (Product Demand)
5) event sourcing (Product Demand)
Rest:
1) crud for json document
2) query calculated
@@ -9,29 +16,3 @@ Rest:
6) Eventual consistency
Read your own writes (E-tag, Expect, Retry-After)
Aggregate Persistence:
1) optimistic force increment
2) normalisation of aggregate entities (Daily Demand)
3) mapper (Daily Demand)
4) wrapper (Product Demand)
5) event sourcing (Product Demand)
http://pillopl.github.io/eventual-consistency-and-rest/
https://github.com/pilloPl/orders-manager
http://groovy-lang.org/json.html
vattenfall:
why status is active when card is EXPIRED?
rfid: 04938C9AF74D80, point: EVB-P1552169 2 (EVB-P1552169_2) EXPIRED Revoked(authId=04938C9AF74D80, status=ACTIVE)
exceptions:
null pointer
more results for query single card

View File

@@ -1,15 +0,0 @@
{
"refNo": "3009000",
"definitions": {
"AtDayStart": {
"06:00": 1
},
"TillDayEnd": {
"22:00": 1
},
"Twice": {
"16:00": 0.5,
"20:00": 0.5
}
}
}

View File

@@ -0,0 +1,17 @@
{
"refNo": "3009000",
"definition": {
"definitions": {
"AtDayStart": {
"06:00": 1.0
},
"TillDayEnd": {
"22:00": 1.0
},
"Twice": {
"16:00": 0.5,
"20:00": 0.5
}
}
}
}

View File

@@ -0,0 +1,6 @@
{
"refNo": "3009000",
"date": "2017-12-10",
"time": "2017-12-10T18:00:00.000",
"level": "3000"
}

View File

@@ -0,0 +1,23 @@
{
"note": "Customer moved demand for day earlier",
"customerRepresentative": "Krysia z logistyki",
"adjustment": {
"refNo": "3009000",
"adjustments": {
"2017-12-11": {
"demand": {
"level": 2800,
"schema": "AtDayStart"
},
"strong": true
},
"2017-12-12": {
"demand": {
"level": 0,
"schema": "AtDayStart"
},
"strong": false
}
}
}
}

View File

@@ -0,0 +1,6 @@
{
"refNo": "3009000",
"date": "2017-12-10",
"level": "2800",
"schema": "TillDayEnd"
}

View File

@@ -0,0 +1,9 @@
{
"refNo": "3009000",
"description": {
"matNum": "461952398951",
"names": [
"PROWAD.POJ.NA JARZ.ESSENT"
]
}
}

View File

@@ -0,0 +1,5 @@
{
"refNo": "3009000",
"date": "2017-12-10",
"output": "1200"
}

View File

@@ -0,0 +1,7 @@
{
"refNo": "3009000",
"start": "2017-12-10T06:15:00.000",
"duration": "DT120M",
"partsPerMinute": "10",
"total": "1200"
}

View File

@@ -0,0 +1,11 @@
{
"refNo": "3009000",
"shortages": {
"refNo": "3009000",
"lockedParts": 0,
"found": "2017-12-10T18:21:07.293",
"shortages": {
"2018-12-11T00:00": 2800
}
}
}

View File

@@ -0,0 +1,116 @@
{
"refNo": "3009000",
"description": {
"matNum": "461952398951",
"names": [
"PROWAD.POJ.NA JARZ.ESSENT"
]
},
"forecasts": [
{
"date": "2017-12-10",
"stock": 1200,
"withLocked": 1900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-11",
"stock": 1200,
"withLocked": 1900,
"demand": 2800,
"output": 0
},
{
"date": "2017-12-12",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-13",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-14",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-15",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-16",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-17",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-18",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-19",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-20",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-21",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-22",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-23",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
},
{
"date": "2017-12-24",
"stock": -1600,
"withLocked": -900,
"demand": 0,
"output": 0
}
]
}

View File

@@ -15,10 +15,10 @@ interface DeliveriesSuggestion {
static DeliveriesSuggestion timesAndFractions(Map<LocalTime, Double> timesAndFractions) {
return (refNo, date, demand) ->
timesAndFractions.entrySet().stream()
.map(e -> new Delivery(
refNo,
date.atTime(e.getKey()), ((long)((double)demand.getLevel() / e.getValue())))
);
.map(e -> new Delivery(
refNo,
date.atTime(e.getKey()), ((long) ((double) demand.getLevel() / e.getValue())))
);
}
Stream<Delivery> deliveriesFor(String refNo, LocalDate date, Demand demand);

View File

@@ -1,7 +1,7 @@
package pl.com.bottega.factory.delivery.planning;
import pl.com.bottega.factory.demand.forecasting.Demand;
import lombok.AllArgsConstructor;
import pl.com.bottega.factory.demand.forecasting.Demand;
import java.time.LocalDate;
import java.util.Map;

View File

@@ -3,8 +3,9 @@ package pl.com.bottega.factory.demand.forecasting;
import lombok.Value;
import java.time.LocalDate;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
@Value
@@ -12,15 +13,13 @@ public class AdjustDemand {
String refNo;
Map<LocalDate, Adjustment> adjustments;
public AdjustDemand(String refNo,
Map<LocalDate, Adjustment> adjustments) {
this.refNo = refNo;
this.adjustments = Collections.unmodifiableMap(adjustments);
}
public void forEachStartingFrom(LocalDate date, BiConsumer<LocalDate, Adjustment> f) {
adjustments.entrySet().stream()
.filter(e -> !e.getKey().isBefore(date))
.forEach(e -> f.accept(e.getKey(), e.getValue()));
}
public Optional<LocalDate> latestAdjustment() {
return adjustments.keySet().stream().max(Comparator.naturalOrder());
}
}

View File

@@ -4,25 +4,25 @@ import lombok.AllArgsConstructor;
import pl.com.bottega.factory.shortages.prediction.Shortages;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.SortedSet;
@AllArgsConstructor
public class Forecast {
String refNo;
LocalDateTime created;
List<LocalDateTime> times;
CurrentStock stock;
ProductionOutputs outputs;
Demands demands;
private final String refNo;
private final LocalDateTime created;
private final SortedSet<LocalDateTime> deliveryTimes;
private final Stock stock;
private final ProductionOutputs outputs;
private final Demands demands;
public Optional<Shortages> findShortages() {
long level = stock.getLevel();
Shortages.Builder found = Shortages.builder(refNo, stock.getLocked(), created);
LocalDateTime lastTime = created;
for (LocalDateTime time : times) {
for (LocalDateTime time : deliveryTimes) {
long demand = demands.get(time);
long produced = outputs.getOutput(lastTime, time);

View File

@@ -3,7 +3,7 @@ package pl.com.bottega.factory.shortages.prediction.calculation;
import lombok.Value;
@Value
public class CurrentStock {
public class Stock {
long level;
long locked;
}

View File

@@ -12,12 +12,12 @@ trait ShortagesCalculationAssemblerTrait {
String refNo = "3009000"
Set<LocalDateTime> times
Forecasts forecastProvider(CurrentStock stock, Demands demands, ProductionOutputs outputs) {
Forecasts forecastProvider(Stock stock, Demands demands, ProductionOutputs outputs) {
def forecast = forecast(stock, demands, outputs)
return { RefNoId refNo, int daysAhead -> forecast } as Forecasts
}
Forecast forecast(CurrentStock stock, Demands demands, ProductionOutputs outputs) {
Forecast forecast(Stock stock, Demands demands, ProductionOutputs outputs) {
new Forecast(refNo, now, times as List, stock, outputs, demands)
}
@@ -45,12 +45,12 @@ trait ShortagesCalculationAssemblerTrait {
new Demands(demands)
}
CurrentStock stock(long levels) {
new CurrentStock(levels, 0)
Stock stock(long levels) {
new Stock(levels, 0)
}
CurrentStock stock(long level, long locked) {
new CurrentStock(level, locked)
Stock stock(long level, long locked) {
new Stock(level, locked)
}
Optional<Shortages> noShortages() {