domain and design in readme

This commit is contained in:
Michał Michaluk
2017-12-22 16:30:34 +01:00
parent 477149a716
commit 42689ab18d
40 changed files with 184 additions and 174 deletions

View File

@@ -1,4 +1,4 @@
# Missing complete example of Domain-Driven Design enterprise application # The missing, complete example of Domain-Driven Design enterprise application
## Command Query CRUD Responsibility Segregation ## Command Query CRUD Responsibility Segregation
Not every piece of software is equally important... Not every piece of software is equally important...
@@ -63,22 +63,28 @@ Making useful application from the Domain Model and the technology.
In most projects the biggest risk is lack of domain knowledge among developers. We all known Java, In most projects the biggest risk is lack of domain knowledge among developers. We all known Java,
databases and bunch of handy frameworks, but what about: Investment Banking, Automotive Manufacturing or even e-Commerce. databases and bunch of handy frameworks, but what about: Investment Banking, Automotive Manufacturing or even e-Commerce.
Lets face those risk at first, maintain and explore domain knowledge Let's face the risk at first, maintain and explore domain knowledge
with **Model Exploration Whirlpool** and build **Ubiquitous Language** with your executable **Domain Model**, with **Model Exploration Whirlpool** and build **Ubiquitous Language** with your executable **Domain Model**,
**Domain Stories** and **Specification by Examples** from day one. **Domain Stories** and **Specification by Examples** from day one.
Adding infrastructure and technology later is easy thanks to Hexagonal Architecture. Adding infrastructure and technology later is easy thanks to Hexagonal Architecture.
Starting from ZERO business knowledge through initial domain and opportunity exploration with **Big Picture Event Storming**: Simply starting from ZERO business knowledge through initial domain and opportunity exploration with **Big Picture Event Storming**:
<big-picture-es> ![Big Picture Event Storming](https://github.com/michal-michaluk/factory/raw/master/es-big-picture-original.jpg)
Looking for system boundaries, impacted and required actors and there interactions with system under design: after cleaning and trimming initial model to most valuable and needed areas:
<actors-and-boundaries> ![Big Picture Event Storming](https://github.com/michal-michaluk/factory/raw/master/es-big-picture-cleaned.jpg)
Estimating depth of domain model and Command Query CRUD segregation: Deep dive in **Demand Forecasting** sub-domain with **Design Level Event Storming**:
<command-query-crud> ![Design Level Event Storming - Demand Forecasting](https://github.com/michal-michaluk/factory/raw/master/es-design-demand-forecasting.jpg)
Design level Event Storming with Domain Stories and Specification by Examples: is excellent canvas to cooperative exploration of:
<demand-forecasting-design-es> - impacted and required actors,
<adjust-demand.feature> - initial / desired system boundaries,
- actors interactions with system under design.
<shortage-prediction-design-es> With use of **Domain Stories** and **Specification by Examples** it is easy to find:
- business rules and invariants,
- acceptance criteria,
- estimation of Domain Model depth,
- CRUD-suspected activities,
- missing parts.

View File

@@ -32,7 +32,7 @@ class DemandEventsMapping implements DemandEvents {
} }
@Override @Override
public void emit(ReviewRequested event) { public void emit(ReviewRequired event) {
Instant timestamp = Instant.now(clock); Instant timestamp = Instant.now(clock);
demandReviews.save(event.getReviews().stream() demandReviews.save(event.getReviews().stream()
.map(r -> new DemandReviewEntity(timestamp, r)) .map(r -> new DemandReviewEntity(timestamp, r))

View File

@@ -21,7 +21,7 @@ import static java.util.stream.Collectors.toMap;
@Component @Component
@AllArgsConstructor @AllArgsConstructor
class ForecastORMRepository implements Forecasts { class ForecastORMRepository implements ShortageForecasts {
private final WarehouseService stocks; private final WarehouseService stocks;
private final DeliveryForecastDao deliveries; private final DeliveryForecastDao deliveries;
@@ -29,7 +29,7 @@ class ForecastORMRepository implements Forecasts {
private final Clock clock; private final Clock clock;
@Override @Override
public Forecast get(RefNoId refNo, int daysAhead) { public ShortageForecast get(RefNoId refNo, int daysAhead) {
Stock stock = stocks.forRefNo(refNo); Stock stock = stocks.forRefNo(refNo);
LocalDateTime time = LocalDateTime.now(clock); LocalDateTime time = LocalDateTime.now(clock);
LocalDateTime max = time.plusDays(daysAhead).truncatedTo(ChronoUnit.DAYS); LocalDateTime max = time.plusDays(daysAhead).truncatedTo(ChronoUnit.DAYS);
@@ -53,6 +53,6 @@ class ForecastORMRepository implements Forecasts {
.collect(Collectors.toList()) .collect(Collectors.toList())
).outputsInTimes(time, deliveryTimes); ).outputsInTimes(time, deliveryTimes);
return new Forecast(refNo.getRefNo(), time, deliveryTimes, stock, outputs, demand); return new ShortageForecast(refNo.getRefNo(), time, deliveryTimes, stock, outputs, demand);
} }
} }

View File

@@ -2,7 +2,7 @@ package pl.com.bottega.factory.demand.forecasting;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import pl.com.bottega.factory.demand.forecasting.ReviewRequested.ReviewNeeded; import pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview;
import javax.transaction.Transactional; import javax.transaction.Transactional;
@@ -29,7 +29,7 @@ public class DemandService {
repository.save(model); repository.save(model);
} }
public void review(ReviewNeeded review, ReviewDecision decision) { public void review(ToReview review, ReviewDecision decision) {
ProductDemand model = repository.get(review.getRefNo()); ProductDemand model = repository.get(review.getRefNo());
model.review(review, decision); model.review(review, decision);
repository.save(model); repository.save(model);

View File

@@ -4,7 +4,7 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import pl.com.bottega.factory.demand.forecasting.ReviewDecision; import pl.com.bottega.factory.demand.forecasting.ReviewDecision;
import pl.com.bottega.factory.demand.forecasting.ReviewRequested.ReviewNeeded; import pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview;
import pl.com.bottega.tools.JsonConverter; import pl.com.bottega.tools.JsonConverter;
import javax.persistence.*; import javax.persistence.*;
@@ -25,14 +25,14 @@ public class DemandReviewEntity implements Serializable {
private LocalDate date; private LocalDate date;
private Instant timestamp; private Instant timestamp;
@Convert(converter = ReviewAsJson.class) @Convert(converter = ReviewAsJson.class)
private ReviewNeeded review; private ToReview review;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private ReviewDecision decision; private ReviewDecision decision;
@Setter @Setter
private LocalDate cleanAfter; private LocalDate cleanAfter;
public DemandReviewEntity(Instant timestamp, ReviewNeeded review) { public DemandReviewEntity(Instant timestamp, ToReview review) {
this.timestamp = timestamp; this.timestamp = timestamp;
this.refNo = review.getId().getRefNo(); this.refNo = review.getId().getRefNo();
this.date = review.getId().getDate(); this.date = review.getId().getDate();
@@ -43,9 +43,9 @@ public class DemandReviewEntity implements Serializable {
return decision != null; return decision != null;
} }
public static class ReviewAsJson extends JsonConverter<ReviewNeeded> { public static class ReviewAsJson extends JsonConverter<ToReview> {
public ReviewAsJson() { public ReviewAsJson() {
super(ReviewNeeded.class); super(ToReview.class);
} }
} }
} }

View File

@@ -7,7 +7,7 @@ import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import pl.com.bottega.factory.demand.forecasting.DemandEvents; import pl.com.bottega.factory.demand.forecasting.DemandEvents;
import pl.com.bottega.factory.demand.forecasting.DemandedLevelsChanged; import pl.com.bottega.factory.demand.forecasting.DemandedLevelsChanged;
import pl.com.bottega.factory.demand.forecasting.ReviewRequested; import pl.com.bottega.factory.demand.forecasting.ReviewRequired;
import java.time.Clock; import java.time.Clock;
@@ -34,7 +34,7 @@ public class Configuration {
} }
@Override @Override
public void emit(ReviewRequested event) { public void emit(ReviewRequired event) {
} }
} }

View File

@@ -2,7 +2,7 @@ package pl.com.bottega.factory.demand.forecasting;
import lombok.Value; import lombok.Value;
import pl.com.bottega.factory.demand.forecasting.DemandedLevelsChanged.Change; import pl.com.bottega.factory.demand.forecasting.DemandedLevelsChanged.Change;
import pl.com.bottega.factory.demand.forecasting.ReviewRequested.ReviewNeeded; import pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@@ -19,7 +19,7 @@ class DailyDemand {
interface Events { interface Events {
void emit(LevelChanged event); void emit(LevelChanged event);
void emit(ReviewNeeded event); void emit(ToReview event);
void emit(DemandUpdated event); void emit(DemandUpdated event);
} }
@@ -49,7 +49,7 @@ class DailyDemand {
void update(Demand documented) { void update(Demand documented) {
State state = state(); State state = state();
if (policy.reviewNeeded(this.documented, this.adjustment, documented)) { if (policy.reviewNeeded(this.documented, this.adjustment, documented)) {
events.emit(new ReviewNeeded(id, events.emit(new ToReview(id,
this.documented, this.documented,
this.adjustment.getDemand(), this.adjustment.getDemand(),
documented) documented)

View File

@@ -3,5 +3,5 @@ package pl.com.bottega.factory.demand.forecasting;
public interface DemandEvents { public interface DemandEvents {
void emit(DemandedLevelsChanged event); void emit(DemandedLevelsChanged event);
void emit(ReviewRequested event); void emit(ReviewRequired event);
} }

View File

@@ -1,7 +1,7 @@
package pl.com.bottega.factory.demand.forecasting; package pl.com.bottega.factory.demand.forecasting;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import pl.com.bottega.factory.demand.forecasting.ReviewRequested.ReviewNeeded; import pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview;
import pl.com.bottega.factory.product.management.RefNoId; import pl.com.bottega.factory.product.management.RefNoId;
import java.time.Clock; import java.time.Clock;
@@ -40,11 +40,11 @@ class ProductDemand {
events.emit(new DemandedLevelsChanged(id, unit.changes())); events.emit(new DemandedLevelsChanged(id, unit.changes()));
} }
if (unit.anyReviews()) { if (unit.anyReviews()) {
events.emit(new ReviewRequested(id, unit.reviews())); events.emit(new ReviewRequired(id, unit.reviews()));
} }
} }
void review(ReviewNeeded review, ReviewDecision decision) { void review(ToReview review, ReviewDecision decision) {
if (decision.requireAdjustment()) { if (decision.requireAdjustment()) {
adjust(decision.toAdjustment(review)); adjust(decision.toAdjustment(review));
} }

View File

@@ -1,7 +1,7 @@
package pl.com.bottega.factory.demand.forecasting; package pl.com.bottega.factory.demand.forecasting;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import pl.com.bottega.factory.demand.forecasting.ReviewRequested.ReviewNeeded; import pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview;
import java.util.Collections; import java.util.Collections;
import java.util.function.Function; import java.util.function.Function;
@@ -9,13 +9,13 @@ import java.util.function.Function;
@AllArgsConstructor @AllArgsConstructor
public enum ReviewDecision { public enum ReviewDecision {
IGNORE(r -> null), IGNORE(r -> null),
PICK_PREVIOUS(ReviewNeeded::getPreviousDocumented), PICK_PREVIOUS(ToReview::getPreviousDocumented),
MAKE_ADJUSTMENT_WEAK(ReviewNeeded::getAdjustment), MAKE_ADJUSTMENT_WEAK(ToReview::getAdjustment),
PICK_NEW(ReviewNeeded::getNewDocumented); PICK_NEW(ToReview::getNewDocumented);
private final Function<ReviewNeeded, Demand> pick; private final Function<ToReview, Demand> pick;
public AdjustDemand toAdjustment(ReviewNeeded review) { public AdjustDemand toAdjustment(ToReview review) {
if (this == IGNORE) { if (this == IGNORE) {
throw new IllegalStateException("can't convert " + this + " to adjustment"); throw new IllegalStateException("can't convert " + this + " to adjustment");
} }

View File

@@ -1,6 +1,6 @@
package pl.com.bottega.factory.demand.forecasting; package pl.com.bottega.factory.demand.forecasting;
import pl.com.bottega.factory.demand.forecasting.ReviewRequested.ReviewNeeded; import pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview;
import java.util.*; import java.util.*;
@@ -9,7 +9,7 @@ import static java.util.Collections.unmodifiableList;
class UnitOfWork implements DailyDemand.Events { class UnitOfWork implements DailyDemand.Events {
Map<DailyId, DemandedLevelsChanged.Change> changes = new HashMap<>(); Map<DailyId, DemandedLevelsChanged.Change> changes = new HashMap<>();
List<ReviewNeeded> reviews = new LinkedList<>(); List<ToReview> reviews = new LinkedList<>();
List<DailyDemand.DemandUpdated> updates = new LinkedList<>(); List<DailyDemand.DemandUpdated> updates = new LinkedList<>();
boolean anyChanges() { boolean anyChanges() {
@@ -24,7 +24,7 @@ class UnitOfWork implements DailyDemand.Events {
return !reviews.isEmpty(); return !reviews.isEmpty();
} }
List<ReviewNeeded> reviews() { List<ToReview> reviews() {
return Collections.unmodifiableList(reviews); return Collections.unmodifiableList(reviews);
} }
@@ -38,7 +38,7 @@ class UnitOfWork implements DailyDemand.Events {
} }
@Override @Override
public void emit(ReviewNeeded event) { public void emit(ToReview event) {
reviews.add(event); reviews.add(event);
} }

View File

@@ -6,7 +6,7 @@ import java.time.LocalDate
import java.time.ZoneId import java.time.ZoneId
import static DemandedLevelsChanged.Change import static DemandedLevelsChanged.Change
import static ReviewRequested.ReviewNeeded import static pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview
class DailyDemandBuilder { class DailyDemandBuilder {
@@ -92,8 +92,8 @@ class DailyDemandBuilder {
) )
} }
ReviewNeeded reviewRequest(long previousDocumented, long adjustment, long newDocumented) { ToReview reviewRequest(long previousDocumented, long adjustment, long newDocumented) {
new ReviewNeeded( new ToReview(
new DailyId(refNo, date), new DailyId(refNo, date),
Demand.of(previousDocumented), Demand.of(previousDocumented),
Demand.of(adjustment), Demand.of(adjustment),

View File

@@ -5,7 +5,7 @@ import pl.com.bottega.factory.product.management.RefNoId
import java.time.* import java.time.*
import static DemandedLevelsChanged.Change import static DemandedLevelsChanged.Change
import static ReviewRequested.ReviewNeeded import static pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview
class ProductDemandBuilder { class ProductDemandBuilder {
@@ -74,15 +74,15 @@ class ProductDemandBuilder {
new DemandedLevelsChanged(new RefNoId(refNo), results) new DemandedLevelsChanged(new RefNoId(refNo), results)
} }
ReviewRequested reviewRequest(ReviewNeeded... reviews) { ReviewRequired reviewRequest(ToReview... reviews) {
new ReviewRequested(new RefNoId(refNo), reviews as List) new ReviewRequired(new RefNoId(refNo), reviews as List)
} }
ReviewNeeded review(LocalDate date, ToReview review(LocalDate date,
long previousDocumented, long previousDocumented,
long strongAdjustment, long strongAdjustment,
long newDocumented) { long newDocumented) {
new ReviewNeeded( new ToReview(
new DailyId(refNo, date), new DailyId(refNo, date),
Demand.of(previousDocumented), Demand.of(previousDocumented),
Demand.of(strongAdjustment), Demand.of(strongAdjustment),

View File

@@ -2,7 +2,7 @@ package pl.com.bottega.factory.demand.forecasting
import java.time.LocalDate import java.time.LocalDate
import static ReviewRequested.ReviewNeeded import static pl.com.bottega.factory.demand.forecasting.ReviewRequired.ToReview
trait ProductDemandTrait { trait ProductDemandTrait {
@@ -32,11 +32,11 @@ trait ProductDemandTrait {
[] []
} }
ReviewRequested reviewRequest(ReviewNeeded... reviews) { ReviewRequired reviewRequest(ToReview... reviews) {
builder.reviewRequest(reviews) builder.reviewRequest(reviews)
} }
ReviewNeeded review( ToReview review(
LocalDate date, LocalDate date,
long previousDocumented, long previousDocumented,
long strongAdjustment, long strongAdjustment,

View File

@@ -1,25 +1,29 @@
Feature: manual adjustments of demand Feature: manual adjustments of demand
sub domain: demand forecasting Sub Domain: demand forecasting
keeps track of current and future customer needs for our products keeps track of current and future customer needs for our products
Domain story: Domain story:
Adjust demand at day to amount, delivered. Adjust demand at day to amount, delivered.
>> demand.adjust(productRefNo, atDay, amount)
We can change only Demands for today and future. We can change only Demands for today and future.
New demand is stored for further reference New demand is stored for further reference
Data from call-off document should be preserved (DONT OVERRIDE THEM). Data from call-off document should be preserved.
Adjust demand should be possible even Adjust demand should be possible even
if there was no call-off document for that product. if there was no document for that product.
In standard case future call-off documents should be stronger (overrides) adjustment, In standard case future call-off documents should override adjustment,
but if customer warn us about opposite case import of call-off document should not remove previous adjustments. but if customer warn us about opposite case
import of document should not remove previous adjustments.
Logistician note should be kept with adjustment.
emit domain event demand changed Domain event: demanded levels changed
[context boundary]
Logistician note should be kept along with adjustment. Sub Domain: shortage prediction
continuously monitors demands, production plan and stock levels
predicts potential shortage based on forecasts
notifies personal about potential shortages
outside of context boundary:
If new demand is not fulfilled by If new demand is not fulfilled by
current product stock and production forecast current product stock and production forecast
there is a shortage in particular days and we need to rise an alert. there is a shortage in particular days and we need to rise an alert.

BIN
es-big-picture-cleaned.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

BIN
es-big-picture-original.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

View File

@@ -8,17 +8,17 @@ import java.util.Collections;
import java.util.List; import java.util.List;
@Value @Value
public class ReviewRequested { public class ReviewRequired {
RefNoId refNo; RefNoId refNo;
List<ReviewNeeded> reviews; List<ToReview> reviews;
public ReviewRequested(RefNoId refNo, List<ReviewNeeded> reviews) { public ReviewRequired(RefNoId refNo, List<ToReview> reviews) {
this.refNo = refNo; this.refNo = refNo;
this.reviews = Collections.unmodifiableList(reviews); this.reviews = Collections.unmodifiableList(reviews);
} }
@Value @Value
public static class ReviewNeeded { public static class ToReview {
DailyId id; DailyId id;
Demand previousDocumented; Demand previousDocumented;
Demand adjustment; Demand adjustment;

View File

@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import pl.com.bottega.factory.product.management.RefNoId; import pl.com.bottega.factory.product.management.RefNoId;
import pl.com.bottega.factory.shortages.prediction.Configuration; import pl.com.bottega.factory.shortages.prediction.Configuration;
import pl.com.bottega.factory.shortages.prediction.calculation.Forecasts; import pl.com.bottega.factory.shortages.prediction.calculation.ShortageForecasts;
import pl.com.bottega.factory.shortages.prediction.monitoring.persistence.ShortagesDao; import pl.com.bottega.factory.shortages.prediction.monitoring.persistence.ShortagesDao;
import pl.com.bottega.factory.shortages.prediction.monitoring.persistence.ShortagesEntity; import pl.com.bottega.factory.shortages.prediction.monitoring.persistence.ShortagesEntity;
import pl.com.bottega.tools.TechnicalId; import pl.com.bottega.tools.TechnicalId;
@@ -17,7 +17,7 @@ class ShortagePredictionProcessORMRepository {
private final ShortagesDao dao; private final ShortagesDao dao;
private final ShortageDiffPolicy policy = ShortageDiffPolicy.ValuesAreNotSame; private final ShortageDiffPolicy policy = ShortageDiffPolicy.ValuesAreNotSame;
private final Forecasts forecasts; private final ShortageForecasts forecasts;
private final Configuration configuration = () -> 14; private final Configuration configuration = () -> 14;
private final ShortageEvents events; private final ShortageEvents events;
@@ -40,7 +40,7 @@ class ShortagePredictionProcessORMRepository {
ShortagesEntity entity = TechnicalId.findOrDefault( ShortagesEntity entity = TechnicalId.findOrDefault(
refNo, dao::findOne, refNo, dao::findOne,
() -> dao.save(new ShortagesEntity(refNo.getRefNo()))); () -> dao.save(new ShortagesEntity(refNo.getRefNo())));
entity.setShortages(event.getShortages()); entity.setShortages(event.getShortage());
events.emit(event); events.emit(event);
} }

View File

@@ -4,7 +4,7 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import pl.com.bottega.factory.product.management.RefNoId; import pl.com.bottega.factory.product.management.RefNoId;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
import pl.com.bottega.tools.JsonConverter; import pl.com.bottega.tools.JsonConverter;
import pl.com.bottega.tools.TechnicalId; import pl.com.bottega.tools.TechnicalId;
@@ -25,7 +25,7 @@ public class ShortagesEntity implements Serializable {
private String refNo; private String refNo;
@Setter @Setter
@Convert(converter = ShortagesAsJson.class) @Convert(converter = ShortagesAsJson.class)
private Shortages shortages; private Shortage shortages;
public ShortagesEntity(String refNo) { public ShortagesEntity(String refNo) {
this.refNo = refNo; this.refNo = refNo;
@@ -39,9 +39,9 @@ public class ShortagesEntity implements Serializable {
return id instanceof ShortagesEntityId ? id : new ShortagesEntityId(id.getRefNo()); return id instanceof ShortagesEntityId ? id : new ShortagesEntityId(id.getRefNo());
} }
public static class ShortagesAsJson extends JsonConverter<Shortages> { public static class ShortagesAsJson extends JsonConverter<Shortage> {
public ShortagesAsJson() { public ShortagesAsJson() {
super(Shortages.class); super(Shortage.class);
} }
} }

View File

@@ -3,7 +3,7 @@ package pl.com.bottega.factory.shortages.prediction.notification;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
import java.time.Clock; import java.time.Clock;
@@ -27,17 +27,17 @@ public class NotificationConfiguration {
private static class MockedPlannerPushNotifications implements Notifications { private static class MockedPlannerPushNotifications implements Notifications {
@Override @Override
public void alertPlanner(Shortages shortage) { public void alertPlanner(Shortage shortage) {
} }
@Override @Override
public void softNotifyPlanner(Shortages shortage) { public void softNotifyPlanner(Shortage shortage) {
} }
@Override @Override
public void markOnPlan(Shortages shortage) { public void markOnPlan(Shortage shortage) {
} }
} }

View File

@@ -6,8 +6,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters; import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import pl.com.bottega.factory.product.management.RefNoId; import pl.com.bottega.factory.product.management.RefNoId;
import pl.com.bottega.factory.shortages.prediction.calculation.Forecast; import pl.com.bottega.factory.shortages.prediction.calculation.ShortageForecast;
import pl.com.bottega.factory.shortages.prediction.calculation.Forecasts; import pl.com.bottega.factory.shortages.prediction.calculation.ShortageForecasts;
import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage; import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage;
import pl.com.bottega.factory.shortages.prediction.monitoring.ShortageEvents; import pl.com.bottega.factory.shortages.prediction.monitoring.ShortageEvents;
import pl.com.bottega.factory.shortages.prediction.monitoring.ShortageSolved; import pl.com.bottega.factory.shortages.prediction.monitoring.ShortageSolved;
@@ -26,8 +26,8 @@ public class Configuration {
} }
@Bean @Bean
public Forecasts forecasts() { public ShortageForecasts forecasts() {
return new ForecastsFake(); return new ShortageForecastsFake();
} }
@Bean @Bean
@@ -47,9 +47,9 @@ public class Configuration {
} }
} }
private class ForecastsFake implements Forecasts { private class ShortageForecastsFake implements ShortageForecasts {
@Override @Override
public Forecast get(RefNoId refNo, int daysAhead) { public ShortageForecast get(RefNoId refNo, int daysAhead) {
return null; return null;
} }
} }

View File

@@ -4,8 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.annotation.Commit import org.springframework.test.annotation.Commit
import pl.com.bottega.factory.product.management.RefNoId import pl.com.bottega.factory.product.management.RefNoId
import pl.com.bottega.factory.shortages.prediction.Shortages import pl.com.bottega.factory.shortages.prediction.Shortage
import pl.com.bottega.factory.shortages.prediction.calculation.Forecasts import pl.com.bottega.factory.shortages.prediction.calculation.ShortageForecasts
import pl.com.bottega.factory.shortages.prediction.monitoring.persistence.ShortagesDao import pl.com.bottega.factory.shortages.prediction.monitoring.persistence.ShortagesDao
import pl.com.bottega.factory.shortages.prediction.monitoring.persistence.ShortagesEntity import pl.com.bottega.factory.shortages.prediction.monitoring.persistence.ShortagesEntity
import spock.lang.Specification import spock.lang.Specification
@@ -25,7 +25,7 @@ class ShortagePredictionProcessORMRepositoryTest extends Specification {
@Autowired @Autowired
ShortagesDao dao ShortagesDao dao
def forecasts = Mock(Forecasts) def forecasts = Mock(ShortageForecasts)
def notifications = Mock(ShortageEvents) def notifications = Mock(ShortageEvents)
ShortagePredictionProcessORMRepository repository ShortagePredictionProcessORMRepository repository
@@ -88,13 +88,13 @@ class ShortagePredictionProcessORMRepositoryTest extends Specification {
noShortagesPersisted() noShortagesPersisted()
} }
def persistedShortage(Shortages shortages) { def persistedShortage(Shortage shortages) {
def entity = new ShortagesEntity(refNo) def entity = new ShortagesEntity(refNo)
entity.setShortages(shortages) entity.setShortages(shortages)
dao.save(entity) dao.save(entity)
} }
Shortages shortagesCurrentlyPersisted() { Shortage shortagesCurrentlyPersisted() {
dao.findByRefNo(refNo).get().shortages dao.findByRefNo(refNo).get().shortages
} }
@@ -106,29 +106,29 @@ class ShortagePredictionProcessORMRepositoryTest extends Specification {
repository.get(new RefNoId(refNo)) repository.get(new RefNoId(refNo))
} }
Shortages noShortages() { Shortage noShortages() {
null null
} }
Shortages someShortages() { Shortage someShortages() {
Shortages.builder(refNo, 0, now) Shortage.builder(refNo, 0, now)
.missing(now.plusDays(1), 500) .missing(now.plusDays(1), 500)
.build() .build()
.orElse(null) .orElse(null)
} }
Shortages someOldShortages() { Shortage someOldShortages() {
Shortages.builder(refNo, 0, now.minusDays(1)) Shortage.builder(refNo, 0, now.minusDays(1))
.missing(now.plusDays(2), 2500) .missing(now.plusDays(2), 2500)
.build() .build()
.orElse(null) .orElse(null)
} }
Shortages shortagesCurrentlyKnownBy(ShortagePredictionProcess process) { Shortage shortagesCurrentlyKnownBy(ShortagePredictionProcess process) {
process.known process.known
} }
void processEmitsNewShortage(ShortagePredictionProcess process, Shortages shortages) { void processEmitsNewShortage(ShortagePredictionProcess process, Shortage shortages) {
process.events.emit(new NewShortage(process.refNo, DemandChanged, shortages)) process.events.emit(new NewShortage(process.refNo, DemandChanged, shortages))
} }

View File

@@ -15,22 +15,22 @@ import java.util.TreeMap;
* Created by michal on 22.10.2015. * Created by michal on 22.10.2015.
*/ */
@Value @Value
public class Shortages { public class Shortage {
private final String refNo; private final String refNo;
private final long lockedParts; private final long lockedParts;
private final LocalDateTime found; private final LocalDateTime found;
private final SortedMap<LocalDateTime, Long> shortages; private final SortedMap<LocalDateTime, Long> shortages;
public static Shortages.Builder builder(String refNo, long locked, LocalDateTime found) { public static Shortage.Builder builder(String refNo, long locked, LocalDateTime found) {
return new Builder(refNo, locked, found); return new Builder(refNo, locked, found);
} }
public static boolean areNotSame(Shortages first, Shortages second) { public static boolean areNotSame(Shortage first, Shortage second) {
return !areSame(first, second); return !areSame(first, second);
} }
public static boolean areSame(Shortages first, Shortages second) { public static boolean areSame(Shortage first, Shortage second) {
boolean noShortages = first == null && second == null; boolean noShortages = first == null && second == null;
boolean onlyOne = first == null && second != null || first != null && second == null; boolean onlyOne = first == null && second != null || first != null && second == null;
if (noShortages || onlyOne) return false; if (noShortages || onlyOne) return false;
@@ -55,11 +55,11 @@ public class Shortages {
return this; return this;
} }
public Optional<Shortages> build() { public Optional<Shortage> build() {
if (gaps.isEmpty()) { if (gaps.isEmpty()) {
return Optional.empty(); return Optional.empty();
} else { } else {
return Optional.of(new Shortages(refNo, locked, found, return Optional.of(new Shortage(refNo, locked, found,
Collections.unmodifiableSortedMap(gaps))); Collections.unmodifiableSortedMap(gaps)));
} }
} }

View File

@@ -1,14 +1,14 @@
package pl.com.bottega.factory.shortages.prediction.calculation; package pl.com.bottega.factory.shortages.prediction.calculation;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Optional; import java.util.Optional;
import java.util.SortedSet; import java.util.SortedSet;
@AllArgsConstructor @AllArgsConstructor
public class Forecast { public class ShortageForecast {
private final String refNo; private final String refNo;
private final LocalDateTime created; private final LocalDateTime created;
@@ -17,10 +17,10 @@ public class Forecast {
private final ProductionOutputs outputs; private final ProductionOutputs outputs;
private final DeliveriesForecast deliveries; private final DeliveriesForecast deliveries;
public Optional<Shortages> findShortages() { public Optional<Shortage> findShortages() {
long level = stock.getLevel(); long level = stock.getLevel();
Shortages.Builder found = Shortages.builder(refNo, stock.getLocked(), created); Shortage.Builder found = Shortage.builder(refNo, stock.getLocked(), created);
LocalDateTime lastTime = created; LocalDateTime lastTime = created;
for (LocalDateTime time : deliveryTimes) { for (LocalDateTime time : deliveryTimes) {
long demand = deliveries.get(time); long demand = deliveries.get(time);

View File

@@ -2,6 +2,6 @@ package pl.com.bottega.factory.shortages.prediction.calculation;
import pl.com.bottega.factory.product.management.RefNoId; import pl.com.bottega.factory.product.management.RefNoId;
public interface Forecasts { public interface ShortageForecasts {
Forecast get(RefNoId refNo, int daysAhead); ShortageForecast get(RefNoId refNo, int daysAhead);
} }

View File

@@ -2,7 +2,7 @@ package pl.com.bottega.factory.shortages.prediction.monitoring;
import lombok.Value; import lombok.Value;
import pl.com.bottega.factory.product.management.RefNoId; import pl.com.bottega.factory.product.management.RefNoId;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
/** /**
* Created by michal on 03.02.2017. * Created by michal on 03.02.2017.
@@ -14,5 +14,5 @@ public class NewShortage {
RefNoId refNo; RefNoId refNo;
After trigger; After trigger;
Shortages shortages; Shortage shortage;
} }

View File

@@ -1,10 +1,10 @@
package pl.com.bottega.factory.shortages.prediction.monitoring; package pl.com.bottega.factory.shortages.prediction.monitoring;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
interface ShortageDiffPolicy { interface ShortageDiffPolicy {
ShortageDiffPolicy ValuesAreNotSame = Shortages::areNotSame; ShortageDiffPolicy ValuesAreNotSame = Shortage::areNotSame;
boolean areDifferent(Shortages previous, Shortages found); boolean areDifferent(Shortage previous, Shortage found);
} }

View File

@@ -3,9 +3,9 @@ package pl.com.bottega.factory.shortages.prediction.monitoring;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import pl.com.bottega.factory.product.management.RefNoId; import pl.com.bottega.factory.product.management.RefNoId;
import pl.com.bottega.factory.shortages.prediction.Configuration; import pl.com.bottega.factory.shortages.prediction.Configuration;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
import pl.com.bottega.factory.shortages.prediction.calculation.Forecast; import pl.com.bottega.factory.shortages.prediction.calculation.ShortageForecast;
import pl.com.bottega.factory.shortages.prediction.calculation.Forecasts; import pl.com.bottega.factory.shortages.prediction.calculation.ShortageForecasts;
import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage.After; import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage.After;
import java.util.Optional; import java.util.Optional;
@@ -17,10 +17,10 @@ import java.util.Optional;
class ShortagePredictionProcess { class ShortagePredictionProcess {
private final RefNoId refNo; private final RefNoId refNo;
private Shortages known; private Shortage known;
private final ShortageDiffPolicy diffPolicy; private final ShortageDiffPolicy diffPolicy;
private final Forecasts forecasts; private final ShortageForecasts forecasts;
private final Configuration configuration; private final Configuration configuration;
private final ShortageEvents events; private final ShortageEvents events;
@@ -41,10 +41,10 @@ class ShortagePredictionProcess {
} }
private void predict(After event) { private void predict(After event) {
Forecast forecast = forecasts.get(refNo, ShortageForecast forecast = forecasts.get(refNo,
configuration.shortagePredictionDaysAhead()); configuration.shortagePredictionDaysAhead());
Optional<Shortages> newlyFound = forecast.findShortages(); Optional<Shortage> newlyFound = forecast.findShortages();
boolean areDifferent = diffPolicy.areDifferent(this.known, newlyFound.orElse(null)); boolean areDifferent = diffPolicy.areDifferent(this.known, newlyFound.orElse(null));
if (areDifferent && newlyFound.isPresent()) { if (areDifferent && newlyFound.isPresent()) {

View File

@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Singular; import lombok.Singular;
import lombok.Value; import lombok.Value;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage; import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage;
import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage.After; import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage.After;
@@ -32,9 +32,9 @@ public class NotificationOfShortage {
} }
public void notifyAbout(NewShortage event) { public void notifyAbout(NewShortage event) {
Shortages shortage = event.getShortages(); Shortage shortage = event.getShortage();
rules.wayOfNotificationAfter(event.getTrigger()) rules.wayOfNotificationAfter(event.getTrigger())
.notifyAbout(event.getShortages()); .notifyAbout(event.getShortage());
if (policy.shouldIncreasePriority(LocalDateTime.now(clock), shortage)) { if (policy.shouldIncreasePriority(LocalDateTime.now(clock), shortage)) {
qualityTasks.increasePriorityFor(shortage.getRefNo()); qualityTasks.increasePriorityFor(shortage.getRefNo());
@@ -54,6 +54,6 @@ public class NotificationOfShortage {
} }
interface Notificator { interface Notificator {
void notifyAbout(Shortages shortages); void notifyAbout(Shortage shortage);
} }
} }

View File

@@ -1,14 +1,14 @@
package pl.com.bottega.factory.shortages.prediction.notification; package pl.com.bottega.factory.shortages.prediction.notification;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
/** /**
* Created by michal on 02.02.2017. * Created by michal on 02.02.2017.
*/ */
public interface Notifications { public interface Notifications {
void alertPlanner(Shortages shortage); void alertPlanner(Shortage shortage);
void softNotifyPlanner(Shortages shortage); void softNotifyPlanner(Shortage shortage);
void markOnPlan(Shortages shortage); void markOnPlan(Shortage shortage);
} }

View File

@@ -1,6 +1,6 @@
package pl.com.bottega.factory.shortages.prediction.notification; package pl.com.bottega.factory.shortages.prediction.notification;
import pl.com.bottega.factory.shortages.prediction.Shortages; import pl.com.bottega.factory.shortages.prediction.Shortage;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -10,7 +10,7 @@ import java.time.LocalDateTime;
public interface RecoveryTaskPriorityChangePolicy { public interface RecoveryTaskPriorityChangePolicy {
static RecoveryTaskPriorityChangePolicy never() { static RecoveryTaskPriorityChangePolicy never() {
return (LocalDateTime now, Shortages shortage) -> false; return (LocalDateTime now, Shortage shortage) -> false;
} }
static RecoveryTaskPriorityChangePolicy onlyIn1DaysAhead() { static RecoveryTaskPriorityChangePolicy onlyIn1DaysAhead() {
@@ -18,10 +18,10 @@ public interface RecoveryTaskPriorityChangePolicy {
} }
static RecoveryTaskPriorityChangePolicy shortageInDays(long shortageInXDays) { static RecoveryTaskPriorityChangePolicy shortageInDays(long shortageInXDays) {
return (LocalDateTime now, Shortages shortage) -> return (LocalDateTime now, Shortage shortage) ->
shortage.getLockedParts() > 0 && shortage.anyBefore( shortage.getLockedParts() > 0 && shortage.anyBefore(
now.plusDays(shortageInXDays)); now.plusDays(shortageInXDays));
} }
boolean shouldIncreasePriority(LocalDateTime now, Shortages shortage); boolean shouldIncreasePriority(LocalDateTime now, Shortage shortage);
} }

View File

@@ -2,7 +2,7 @@ package pl.com.bottega.factory.shortages.prediction.calculation
import spock.lang.Specification import spock.lang.Specification
class ShortagesCalculationAlgorithmSpec extends Specification class ShortageCalculationAlgorithmSpec extends Specification
implements ShortagesCalculationAssemblerTrait { implements ShortagesCalculationAssemblerTrait {
void setup() { void setup() {

View File

@@ -2,7 +2,7 @@ package pl.com.bottega.factory.shortages.prediction.calculation
import spock.lang.Specification import spock.lang.Specification
class ShortagesCalculationExamplesSpec extends Specification class ShortageCalculationExamplesSpec extends Specification
implements ShortagesCalculationAssemblerTrait { implements ShortagesCalculationAssemblerTrait {
void setup() { void setup() {

View File

@@ -1,7 +1,7 @@
package pl.com.bottega.factory.shortages.prediction.calculation package pl.com.bottega.factory.shortages.prediction.calculation
import pl.com.bottega.factory.product.management.RefNoId import pl.com.bottega.factory.product.management.RefNoId
import pl.com.bottega.factory.shortages.prediction.Shortages import pl.com.bottega.factory.shortages.prediction.Shortage
import java.time.Duration import java.time.Duration
import java.time.LocalDateTime import java.time.LocalDateTime
@@ -12,13 +12,13 @@ trait ShortagesCalculationAssemblerTrait {
String refNo = "3009000" String refNo = "3009000"
SortedSet<LocalDateTime> times SortedSet<LocalDateTime> times
Forecasts forecastProvider(Stock stock, DeliveriesForecast demands, ProductionOutputs outputs) { ShortageForecasts forecastProvider(Stock stock, DeliveriesForecast demands, ProductionOutputs outputs) {
def forecast = forecast(stock, demands, outputs) def forecast = forecast(stock, demands, outputs)
return { RefNoId refNo, int daysAhead -> forecast } as Forecasts return { RefNoId refNo, int daysAhead -> forecast } as ShortageForecasts
} }
Forecast forecast(Stock stock, DeliveriesForecast demands, ProductionOutputs outputs) { ShortageForecast forecast(Stock stock, DeliveriesForecast demands, ProductionOutputs outputs) {
new Forecast(refNo, now, times, stock, outputs, demands) new ShortageForecast(refNo, now, times, stock, outputs, demands)
} }
ProductionOutputs noProductions() { ProductionOutputs noProductions() {
@@ -53,12 +53,12 @@ trait ShortagesCalculationAssemblerTrait {
new Stock(level, locked) new Stock(level, locked)
} }
Optional<Shortages> noShortages() { Optional<Shortage> noShortages() {
Optional.empty() Optional.empty()
} }
Optional<Shortages> shortage(Map<LocalDateTime, Long> missing, long locked = 0) { Optional<Shortage> shortage(Map<LocalDateTime, Long> missing, long locked = 0) {
def shortages = Shortages.builder(refNo, locked, now) def shortages = Shortage.builder(refNo, locked, now)
missing.each { time, level -> shortages.missing(time, level) } missing.each { time, level -> shortages.missing(time, level) }

View File

@@ -1,6 +1,6 @@
package pl.com.bottega.factory.shortages.prediction.monitoring package pl.com.bottega.factory.shortages.prediction.monitoring
import pl.com.bottega.factory.shortages.prediction.Shortages import pl.com.bottega.factory.shortages.prediction.Shortage
import pl.com.bottega.factory.shortages.prediction.calculation.TimeGrammar import pl.com.bottega.factory.shortages.prediction.calculation.TimeGrammar
import spock.lang.Specification import spock.lang.Specification
@@ -18,11 +18,11 @@ class ShortageDiffPolicySpec extends Specification {
given: given:
def policy = ShortageDiffPolicy.ValuesAreNotSame def policy = ShortageDiffPolicy.ValuesAreNotSame
Shortages one = Shortages.builder("3009000", 0, now) Shortage one = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
Shortages another = Shortages.builder("3009000", 0, now + 5.min) Shortage another = Shortage.builder("3009000", 0, now + 5.min)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
@@ -34,11 +34,11 @@ class ShortageDiffPolicySpec extends Specification {
given: given:
def policy = ShortageDiffPolicy.ValuesAreNotSame def policy = ShortageDiffPolicy.ValuesAreNotSame
Shortages one = Shortages.builder("3009000", 0, now) Shortage one = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
Shortages another = Shortages.builder("3009000", 1000, now) Shortage another = Shortage.builder("3009000", 1000, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
@@ -50,11 +50,11 @@ class ShortageDiffPolicySpec extends Specification {
given: given:
def policy = ShortageDiffPolicy.ValuesAreNotSame def policy = ShortageDiffPolicy.ValuesAreNotSame
Shortages one = Shortages.builder("3009000XXX", 0, now) Shortage one = Shortage.builder("3009000XXX", 0, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
Shortages another = Shortages.builder("3009000", 0, now) Shortage another = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
@@ -66,11 +66,11 @@ class ShortageDiffPolicySpec extends Specification {
given: given:
def policy = ShortageDiffPolicy.ValuesAreNotSame def policy = ShortageDiffPolicy.ValuesAreNotSame
Shortages one = Shortages.builder("3009000", 0, now) Shortage one = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
Shortages another = Shortages.builder("3009000", 0, now) Shortage another = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 1500L) .missing(now + 1.day, 1500L)
.build().orElse(null) .build().orElse(null)
@@ -82,11 +82,11 @@ class ShortageDiffPolicySpec extends Specification {
given: given:
def policy = ShortageDiffPolicy.ValuesAreNotSame def policy = ShortageDiffPolicy.ValuesAreNotSame
Shortages one = Shortages.builder("3009000", 0, now) Shortage one = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
Shortages another = Shortages.builder("3009000", 0, now) Shortage another = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 499L) .missing(now + 1.day, 499L)
.build().orElse(null) .build().orElse(null)
@@ -98,11 +98,11 @@ class ShortageDiffPolicySpec extends Specification {
given: given:
def policy = ShortageDiffPolicy.ValuesAreNotSame def policy = ShortageDiffPolicy.ValuesAreNotSame
Shortages one = Shortages.builder("3009000", 0, now) Shortage one = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
Shortages another = Shortages.builder("3009000", 0, now) Shortage another = Shortage.builder("3009000", 0, now)
.missing(now + 2.day, 500L) .missing(now + 2.day, 500L)
.build().orElse(null) .build().orElse(null)
@@ -114,11 +114,11 @@ class ShortageDiffPolicySpec extends Specification {
given: given:
def policy = ShortageDiffPolicy.ValuesAreNotSame def policy = ShortageDiffPolicy.ValuesAreNotSame
Shortages one = Shortages.builder("3009000", 0, now) Shortage one = Shortage.builder("3009000", 0, now)
.missing(now + 1.day, 500L) .missing(now + 1.day, 500L)
.build().orElse(null) .build().orElse(null)
Shortages another = Shortages.builder("3009000", 0, now) Shortage another = Shortage.builder("3009000", 0, now)
.missing(now + 1.day + 1.min, 500L) .missing(now + 1.day + 1.min, 500L)
.build().orElse(null) .build().orElse(null)

View File

@@ -2,8 +2,8 @@ package pl.com.bottega.factory.shortages.prediction.monitoring
import pl.com.bottega.factory.product.management.RefNoId import pl.com.bottega.factory.product.management.RefNoId
import pl.com.bottega.factory.shortages.prediction.Configuration import pl.com.bottega.factory.shortages.prediction.Configuration
import pl.com.bottega.factory.shortages.prediction.Shortages import pl.com.bottega.factory.shortages.prediction.Shortage
import pl.com.bottega.factory.shortages.prediction.calculation.Forecasts import pl.com.bottega.factory.shortages.prediction.calculation.ShortageForecasts
import pl.com.bottega.factory.shortages.prediction.calculation.ShortagesCalculationAssembler import pl.com.bottega.factory.shortages.prediction.calculation.ShortagesCalculationAssembler
import spock.lang.Specification import spock.lang.Specification
@@ -119,8 +119,8 @@ class ShortagePredictionProcessSpec extends Specification {
} }
ShortagePredictionProcess predictionProcess( ShortagePredictionProcess predictionProcess(
Shortages previouslyFound, Shortage previouslyFound,
Forecasts forecastThatWillFindShortages) { ShortageForecasts forecastThatWillFindShortages) {
new ShortagePredictionProcess( new ShortagePredictionProcess(
refNo, refNo,
@@ -142,15 +142,15 @@ class ShortagePredictionProcessSpec extends Specification {
(now.plusDays(1)) : 900L] (now.plusDays(1)) : 900L]
} }
Shortages noShortagesWasPreviouslyFound() { Shortage noShortagesWasPreviouslyFound() {
null null
} }
Shortages wasPreviouslyFound(Map<LocalDateTime, Long> shortages) { Shortage wasPreviouslyFound(Map<LocalDateTime, Long> shortages) {
forecastAssembler.shortage(shortages).orElse(null) forecastAssembler.shortage(shortages).orElse(null)
} }
Forecasts noShortagesWillBeFound() { ShortageForecasts noShortagesWillBeFound() {
forecastAssembler.forecastProvider( forecastAssembler.forecastProvider(
forecastAssembler.stock(1000), forecastAssembler.stock(1000),
forecastAssembler.noDeliveries(), forecastAssembler.noDeliveries(),
@@ -158,7 +158,7 @@ class ShortagePredictionProcessSpec extends Specification {
) )
} }
Forecasts willFindShortages(Map<LocalDateTime, Long> shortages) { ShortageForecasts willFindShortages(Map<LocalDateTime, Long> shortages) {
forecastAssembler.forecastProvider( forecastAssembler.forecastProvider(
forecastAssembler.stock(0), forecastAssembler.stock(0),
forecastAssembler.deliveries(shortages), forecastAssembler.deliveries(shortages),

View File

@@ -1,7 +1,7 @@
package pl.com.bottega.factory.shortages.prediction.notification package pl.com.bottega.factory.shortages.prediction.notification
import pl.com.bottega.factory.product.management.RefNoId import pl.com.bottega.factory.product.management.RefNoId
import pl.com.bottega.factory.shortages.prediction.Shortages import pl.com.bottega.factory.shortages.prediction.Shortage
import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage import pl.com.bottega.factory.shortages.prediction.monitoring.NewShortage
import spock.lang.Specification import spock.lang.Specification
@@ -109,14 +109,14 @@ class NotificationOfShortageSpec extends Specification {
) )
} }
NewShortage newShortage(After after, Shortages shortages) { NewShortage newShortage(After after, Shortage shortages) {
new NewShortage(new RefNoId(refNo), after, shortages) new NewShortage(new RefNoId(refNo), after, shortages)
} }
Shortages withShortage( Shortage withShortage(
Duration firstShortageIn = Duration.ofDays(4), Duration firstShortageIn = Duration.ofDays(4),
long lockedStock = 0) { long lockedStock = 0) {
Shortages.builder(refNo, lockedStock, now) Shortage.builder(refNo, lockedStock, now)
.missing(now.plus(firstShortageIn), 500L) .missing(now.plus(firstShortageIn), 500L)
.build().get() .build().get()
} }

View File

@@ -1,6 +1,6 @@
package pl.com.bottega.factory.shortages.prediction.notification package pl.com.bottega.factory.shortages.prediction.notification
import pl.com.bottega.factory.shortages.prediction.Shortages import pl.com.bottega.factory.shortages.prediction.Shortage
import spock.lang.Specification import spock.lang.Specification
import java.time.Duration import java.time.Duration
@@ -10,8 +10,8 @@ class RecoveryTaskPriorityChangePolicySpec extends Specification {
def now = LocalDateTime.now() def now = LocalDateTime.now()
Shortages foundShortage(Duration firstShortageIn, long lockedStock) { Shortage foundShortage(Duration firstShortageIn, long lockedStock) {
Shortages.builder("3009000", lockedStock, now) Shortage.builder("3009000", lockedStock, now)
.missing(now.plus(firstShortageIn), 500L) .missing(now.plus(firstShortageIn), 500L)
.build().get() .build().get()
} }