瀏覽代碼

Now able to correctly predict customs handling and handling immediately after a reroute.

Fixed mapping of Cargo, adapted tests
peter_backlund 17 年之前
父節點
當前提交
fb2581614d
共有 15 個文件被更改,包括 241 次插入167 次删除
  1. 8
    8
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/application/util/SampleDataGenerator.java
  2. 18
    20
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Cargo.java
  3. 23
    33
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Delivery.java
  4. 3
    3
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Itinerary.java
  5. 4
    5
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Leg.java
  6. 1
    0
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/TransportStatus.java
  7. 2
    1
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/location/Location.java
  8. 7
    7
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/shared/HandlingActivity.java
  9. 11
    1
      dddsample/tracking/core/src/main/resources/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/Cargo.hbm.xml
  10. 34
    6
      dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/CargoTest.java
  11. 9
    20
      dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/DeliveryTest.java
  12. 13
    13
      dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/ItineraryTest.java
  13. 8
    8
      dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/LegTest.java
  14. 43
    33
      dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/scenario/CargoLifecycle.java
  15. 57
    9
      dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/scenario/CargoLifecycleScenarioTest.java

+ 8
- 8
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/application/util/SampleDataGenerator.java 查看文件

@@ -132,16 +132,16 @@ public class SampleDataGenerator implements ServletContextListener {
132 132
 
133 133
   private static void loadCargoData(JdbcTemplate jdbcTemplate) {
134 134
     String cargoSql =
135
-      "insert into Cargo (id, tracking_id, spec_origin_id, spec_destination_id, spec_arrival_deadline, calculated_at) " +
136
-        "values (?, ?, ?, ?, ?, ?)";
135
+      "insert into Cargo (id, tracking_id, spec_origin_id, spec_destination_id, spec_arrival_deadline, last_update, routed_after_handling) " +
136
+        "values (?, ?, ?, ?, ?, ?, ?)";
137 137
 
138 138
     Object[][] cargoArgs = {
139
-      {1, "XYZ", 1, 2, ts(10), ts(100)},
140
-      {2, "ABC", 1, 5, ts(20), ts(100)},
141
-      {3, "ZYX", 2, 1, ts(30), ts(100)},
142
-      {4, "CBA", 5, 1, ts(40), ts(100)},
143
-      {5, "FGH", 3, 5, ts(50), ts(100)},  // Cargo origin differs from spec origin
144
-      {6, "JKL", 6, 4, ts(60), ts(100)}
139
+      {1, "XYZ", 1, 2, ts(10), ts(100), false},
140
+      {2, "ABC", 1, 5, ts(20), ts(100), false},
141
+      {3, "ZYX", 2, 1, ts(30), ts(100), false},
142
+      {4, "CBA", 5, 1, ts(40), ts(100), false},
143
+      {5, "FGH", 3, 5, ts(50), ts(100), false},
144
+      {6, "JKL", 6, 4, ts(60), ts(100), false}
145 145
     };
146 146
     executeUpdate(jdbcTemplate, cargoSql, cargoArgs);
147 147
   }

+ 18
- 20
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Cargo.java 查看文件

@@ -3,9 +3,11 @@ package se.citerus.dddsample.tracking.core.domain.model.cargo;
3 3
 import org.apache.commons.lang.Validate;
4 4
 import static se.citerus.dddsample.tracking.core.domain.model.cargo.RoutingStatus.NOT_ROUTED;
5 5
 import se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent;
6
+import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent.Type.UNLOAD;
6 7
 import se.citerus.dddsample.tracking.core.domain.model.location.CustomsZone;
7 8
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
8 9
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
10
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.customsIn;
9 11
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
10 12
 import se.citerus.dddsample.tracking.core.domain.patterns.entity.EntitySupport;
11 13
 
@@ -94,7 +96,7 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
94 96
    * @return Estimated time of arrival.
95 97
    */
96 98
   public Date estimatedTimeOfArrival() {
97
-    if (onTrack()) {
99
+    if (delivery.isOnRoute(itinerary, routeSpecification)) {
98 100
       return itinerary.estimatedTimeOfArrival();
99 101
     } else {
100 102
       return null;
@@ -105,31 +107,32 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
105 107
    * @return Next expected activity.
106 108
    */
107 109
   public HandlingActivity nextExpectedActivity() {
108
-    if (onTrack()) {
109
-      if (delivery.isRoutedAfterHandling()) {
110
-        return itinerary.firstLeg().deriveLoadActivity();
110
+    if (!delivery.isOnRoute(itinerary, routeSpecification)) {
111
+      return null;
112
+    }
113
+
114
+    if (delivery.isRoutedAfterHandling()) {
115
+      return itinerary.firstLeg().deriveLoadActivity();
116
+    } else {
117
+      if (unloadedInCustomsClearancePoint()) {
118
+        return customsIn(customsClearancePoint());
111 119
       } else {
112
-        // Not yet able to determine when the next expected activity is non-pyhsical, i.e. customs
113 120
         return itinerary.activitySucceding(delivery.mostRecentPhysicalHandlingActivity());
114 121
       }
115
-    } else {
116
-      return null;
117 122
     }
118 123
   }
119 124
 
120
-  /**
121
-   * @return True if the cargo is assigned to a route and is following that route.
122
-   */
123
-  public boolean onTrack() {
124
-    return delivery.onTrack(itinerary, routeSpecification);
125
+  private boolean unloadedInCustomsClearancePoint() {
126
+    return mostRecentHandlingActivity() != null &&
127
+           mostRecentHandlingActivity().location().sameAs(customsClearancePoint()) &&
128
+           mostRecentHandlingActivity().type() == UNLOAD;
125 129
   }
126 130
 
127 131
   /**
128 132
    * @return True if cargo is misdirected.
129 133
    */
130 134
   public boolean isMisdirected() {
131
-    return delivery.hasBeenHandledAfterRouting() &&
132
-           !itinerary.isExpectedActivity(delivery.mostRecentPhysicalHandlingActivity());
135
+    return delivery.isMisdirected(itinerary);
133 136
   }
134 137
 
135 138
   /**
@@ -192,7 +195,6 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
192 195
     return routeSpecification.destination().customsZone();
193 196
   }
194 197
 
195
-
196 198
   /**
197 199
    * @return Customs clearance point.
198 200
    */
@@ -245,13 +247,9 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
245 247
     return succedsMostRecentActivity(newHandlingActivity);
246 248
   }
247 249
 
248
-  /*
249
-  * Problem: CUSTOMS går inte att matcha mot ett Leg, så varje annan händelse som
250
-  * är förväntad kommer att trilla ut som strictly prior.
251
-  */
252 250
   private boolean succedsMostRecentActivity(final HandlingActivity newHandlingActivity) {
253 251
     if (delivery.hasBeenHandledAfterRouting()) {
254
-      final HandlingActivity priorActivity = itinerary.strictlyPriorOf(mostRecentHandlingActivity(), newHandlingActivity);
252
+      final HandlingActivity priorActivity = itinerary.strictlyPriorOf(delivery.mostRecentPhysicalHandlingActivity(), newHandlingActivity);
255 253
       return !newHandlingActivity.sameValueAs(priorActivity);
256 254
     } else {
257 255
       return true;

+ 23
- 33
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Delivery.java 查看文件

@@ -3,7 +3,6 @@ package se.citerus.dddsample.tracking.core.domain.model.cargo;
3 3
 import org.apache.commons.lang.Validate;
4 4
 import static se.citerus.dddsample.tracking.core.domain.model.cargo.RoutingStatus.*;
5 5
 import static se.citerus.dddsample.tracking.core.domain.model.cargo.TransportStatus.ONBOARD_CARRIER;
6
-import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent.Type.CUSTOMS;
7 6
 import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent.Type.UNLOAD;
8 7
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
9 8
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
@@ -20,7 +19,7 @@ class Delivery extends ValueObjectSupport<Delivery> {
20 19
 
21 20
   private final HandlingActivity mostRecentHandlingActivity;
22 21
   private final HandlingActivity mostRecentPhysicalHandlingActivity;
23
-  private final Date calculatedAt;
22
+  private final Date lastUpdatedOn;
24 23
   private final boolean routedAfterHandling;
25 24
 
26 25
   /**
@@ -33,9 +32,9 @@ class Delivery extends ValueObjectSupport<Delivery> {
33 32
     Validate.notNull(newHandlingActivity, "Handling activity is required");
34 33
 
35 34
     if (newHandlingActivity.type().isPhysical()) {
36
-      return new Delivery(newHandlingActivity, newHandlingActivity, new Date(), false);
35
+      return new Delivery(newHandlingActivity, newHandlingActivity, false);
37 36
     } else {
38
-      return new Delivery(newHandlingActivity, mostRecentPhysicalHandlingActivity, new Date(), false);
37
+      return new Delivery(newHandlingActivity, mostRecentPhysicalHandlingActivity, false);
39 38
     }
40 39
   }
41 40
 
@@ -43,23 +42,23 @@ class Delivery extends ValueObjectSupport<Delivery> {
43 42
    * @return An up to date delivery
44 43
    */
45 44
   Delivery onRouting() {
46
-    return new Delivery(mostRecentHandlingActivity, mostRecentPhysicalHandlingActivity, calculatedAt, true);
45
+    return new Delivery(mostRecentHandlingActivity, mostRecentPhysicalHandlingActivity, true);
47 46
   }
48 47
 
49 48
   /**
50 49
    * @return Initial delivery, before any handling has taken place
51 50
    */
52 51
   static Delivery beforeHandling() {
53
-    return new Delivery(null, null, new Date(0L), false);
52
+    return new Delivery(null, null, false);
54 53
   }
55 54
 
56
-  Delivery(final HandlingActivity mostRecentHandlingActivity,
57
-           final HandlingActivity mostRecentPhysicalHandlingActivity,
58
-           final Date completionTime, boolean routedAfterHandling) {
55
+  private Delivery(final HandlingActivity mostRecentHandlingActivity,
56
+                   final HandlingActivity mostRecentPhysicalHandlingActivity,
57
+                   final boolean routedAfterHandling) {
59 58
     this.mostRecentHandlingActivity = mostRecentHandlingActivity;
60 59
     this.mostRecentPhysicalHandlingActivity = mostRecentPhysicalHandlingActivity;
61
-    this.calculatedAt = completionTime;
62 60
     this.routedAfterHandling = routedAfterHandling;
61
+    this.lastUpdatedOn = new Date();
63 62
   }
64 63
 
65 64
   HandlingActivity mostRecentHandlingActivity() {
@@ -81,7 +80,7 @@ class Delivery extends ValueObjectSupport<Delivery> {
81 80
    * @return Last known location of the cargo, or Location.UNKNOWN if the delivery history is empty.
82 81
    */
83 82
   Location lastKnownLocation() {
84
-    if (hasBeenHandledAfterRouting()) {
83
+    if (hasBeenHandled()) {
85 84
       return mostRecentHandlingActivity.location();
86 85
     } else {
87 86
       return Location.UNKNOWN;
@@ -100,7 +99,11 @@ class Delivery extends ValueObjectSupport<Delivery> {
100 99
   }
101 100
 
102 101
   boolean hasBeenHandledAfterRouting() {
103
-    return !routedAfterHandling && mostRecentHandlingActivity != null;
102
+    return hasBeenHandled() && !routedAfterHandling;
103
+  }
104
+
105
+  boolean hasBeenHandled() {
106
+    return mostRecentHandlingActivity != null;
104 107
   }
105 108
 
106 109
   /**
@@ -112,24 +115,11 @@ class Delivery extends ValueObjectSupport<Delivery> {
112 115
    * <li>A cargo that has received no handling events can not be misdirected.
113 116
    * </ul>
114 117
    *
115
-   * @return <code>true</code> if the cargo has been misdirected,
116 118
    * @param itinerary itinerary
117
-   * @param routeSpecification route specification
119
+   * @return <code>true</code> if the cargo has been misdirected,
118 120
    */
119
-  boolean isMisdirected(final Itinerary itinerary, final RouteSpecification routeSpecification) {
120
-    if (hasBeenHandledAfterRouting()) {
121
-      if (mostRecentHandlingActivity.type() == CUSTOMS) {
122
-        return !handledAtDestination(routeSpecification);
123
-      } else {
124
-        return !itinerary.isExpectedActivity(mostRecentHandlingActivity);
125
-      }
126
-    }
127
-
128
-    return false;
129
-  }
130
-
131
-  private boolean handledAtDestination(RouteSpecification routeSpecification) {
132
-    return routeSpecification.destination().sameAs(mostRecentHandlingActivity.location());
121
+  boolean isMisdirected(final Itinerary itinerary) {
122
+    return hasBeenHandledAfterRouting() && !itinerary.isExpectedActivity(mostRecentPhysicalHandlingActivity);
133 123
   }
134 124
 
135 125
   /**
@@ -162,8 +152,8 @@ class Delivery extends ValueObjectSupport<Delivery> {
162 152
   /**
163 153
    * @return When this delivery was calculated.
164 154
    */
165
-  Date lastTimestamp() {
166
-    return new Date(calculatedAt.getTime());
155
+  Date lastUpdatedOn() {
156
+    return new Date(lastUpdatedOn.getTime());
167 157
   }
168 158
 
169 159
   /**
@@ -171,8 +161,8 @@ class Delivery extends ValueObjectSupport<Delivery> {
171 161
    * @param itinerary itinerary
172 162
    * @param routeSpecification route specification
173 163
    */
174
-  boolean onTrack(final Itinerary itinerary, final RouteSpecification routeSpecification) {
175
-    return routingStatus(itinerary, routeSpecification) == ROUTED && !isMisdirected(itinerary, routeSpecification);
164
+  boolean isOnRoute(final Itinerary itinerary, final RouteSpecification routeSpecification) {
165
+    return routingStatus(itinerary, routeSpecification) == ROUTED && !isMisdirected(itinerary);
176 166
   }
177 167
 
178 168
   /**
@@ -184,7 +174,7 @@ class Delivery extends ValueObjectSupport<Delivery> {
184 174
 
185 175
   Delivery() {
186 176
     // Needed by Hibernate
187
-    calculatedAt = null;
177
+    lastUpdatedOn = null;
188 178
     mostRecentHandlingActivity = mostRecentPhysicalHandlingActivity = null;
189 179
     routedAfterHandling = false;
190 180
   }

+ 3
- 3
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Itinerary.java 查看文件

@@ -169,7 +169,7 @@ public class Itinerary extends ValueObjectSupport<Itinerary> {
169 169
    */
170 170
   HandlingActivity activitySucceding(final HandlingActivity previousActivity) {
171 171
     if (previousActivity == null) {
172
-      return receivedIn(firstLeg().loadLocation());
172
+      return receiveIn(firstLeg().loadLocation());
173 173
     } else {
174 174
       return deriveFromMatchingLeg(previousActivity, legMatchOf(previousActivity).leg());
175 175
     }
@@ -253,7 +253,7 @@ public class Itinerary extends ValueObjectSupport<Itinerary> {
253 253
       return null;
254 254
     } else {
255 255
       if (handlingActivity.type() == LOAD) {
256
-        return unloadedOff(handlingActivity.voyage()).in(matchingLeg.unloadLocation());
256
+        return unloadOff(handlingActivity.voyage()).in(matchingLeg.unloadLocation());
257 257
       } else if (handlingActivity.type() == UNLOAD) {
258 258
         return deriveFromNextLeg(nextLeg(matchingLeg));
259 259
       } else {
@@ -265,7 +265,7 @@ public class Itinerary extends ValueObjectSupport<Itinerary> {
265 265
 
266 266
   private HandlingActivity deriveFromNextLeg(final Leg nextLeg) {
267 267
     if (nextLeg == null) {
268
-      return claimedIn(lastLeg().unloadLocation());
268
+      return claimIn(lastLeg().unloadLocation());
269 269
     } else {
270 270
       return nextLeg.deriveLoadActivity();
271 271
     }

+ 4
- 5
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Leg.java 查看文件

@@ -5,8 +5,8 @@ import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingE
5 5
 import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent.Type.UNLOAD;
6 6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7 7
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
8
-import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.loadedOnto;
9
-import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.unloadedOff;
8
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.loadOnto;
9
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.unloadOff;
10 10
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
11 11
 import se.citerus.dddsample.tracking.core.domain.patterns.valueobject.ValueObjectSupport;
12 12
 
@@ -88,7 +88,6 @@ public class Leg extends ValueObjectSupport<Leg> {
88 88
     return Leg.deriveLeg(voyage, loadLocation, unloadLocation);
89 89
   }
90 90
 
91
-
92 91
   /**
93 92
    *
94 93
    * @param handlingActivity handling activity
@@ -124,11 +123,11 @@ public class Leg extends ValueObjectSupport<Leg> {
124 123
   }
125 124
 
126 125
   HandlingActivity deriveLoadActivity() {
127
-    return loadedOnto(voyage).in(loadLocation);
126
+    return loadOnto(voyage).in(loadLocation);
128 127
   }
129 128
 
130 129
   HandlingActivity deriveUnloadActivity() {
131
-    return unloadedOff(voyage).in(unloadLocation);
130
+    return unloadOff(voyage).in(unloadLocation);
132 131
   }
133 132
 
134 133
   @Override

+ 1
- 0
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/TransportStatus.java 查看文件

@@ -24,6 +24,7 @@ public enum TransportStatus implements ValueObject<TransportStatus> {
24 24
         return ONBOARD_CARRIER;
25 25
       case UNLOAD:
26 26
       case RECEIVE:
27
+      case CUSTOMS:
27 28
         return IN_PORT;
28 29
       case CLAIM:
29 30
         return CLAIMED;

+ 2
- 1
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/location/Location.java 查看文件

@@ -14,7 +14,7 @@ import java.util.TimeZone;
14 14
 public final class Location extends EntitySupport<Location, UnLocode> {
15 15
 
16 16
   private final UnLocode unLocode;
17
-  private String name;
17
+  private final String name;
18 18
   private TimeZone timeZone;
19 19
   private CustomsZone customsZone;
20 20
 
@@ -87,6 +87,7 @@ public final class Location extends EntitySupport<Location, UnLocode> {
87 87
   Location() {
88 88
     // Needed by Hibernate
89 89
     unLocode = null;
90
+    name = null;
90 91
   }
91 92
 
92 93
 }

+ 7
- 7
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/shared/HandlingActivity.java 查看文件

@@ -63,23 +63,23 @@ public class HandlingActivity extends ValueObjectSupport<HandlingActivity> {
63 63
 
64 64
   // DSL-like factory methods
65 65
 
66
-  public static InLocation loadedOnto(Voyage voyage) {
66
+  public static InLocation loadOnto(final Voyage voyage) {
67 67
     return new InLocation(LOAD, voyage);
68 68
   }
69 69
 
70
-  public static InLocation unloadedOff(Voyage voyage) {
70
+  public static InLocation unloadOff(final Voyage voyage) {
71 71
     return new InLocation(UNLOAD, voyage);
72 72
   }
73 73
 
74
-  public static HandlingActivity receivedIn(Location location) {
74
+  public static HandlingActivity receiveIn(final Location location) {
75 75
     return new HandlingActivity(RECEIVE, location);
76 76
   }
77 77
 
78
-  public static HandlingActivity claimedIn(Location location) {
78
+  public static HandlingActivity claimIn(final Location location) {
79 79
     return new HandlingActivity(CLAIM, location);
80 80
   }
81 81
 
82
-  public static HandlingActivity customsIn(Location location) {
82
+  public static HandlingActivity customsIn(final Location location) {
83 83
     return new HandlingActivity(CUSTOMS, location);
84 84
   }
85 85
 
@@ -87,12 +87,12 @@ public class HandlingActivity extends ValueObjectSupport<HandlingActivity> {
87 87
     private final HandlingEvent.Type type;
88 88
     private final Voyage voyage;
89 89
 
90
-    public InLocation(HandlingEvent.Type type, Voyage voyage) {
90
+    public InLocation(final HandlingEvent.Type type, final Voyage voyage) {
91 91
       this.type = type;
92 92
       this.voyage = voyage;
93 93
     }
94 94
 
95
-    public HandlingActivity in(Location location) {
95
+    public HandlingActivity in(final Location location) {
96 96
       return new HandlingActivity(type, location, voyage);
97 97
     }
98 98
   }

+ 11
- 1
dddsample/tracking/core/src/main/resources/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/Cargo.hbm.xml 查看文件

@@ -16,7 +16,8 @@
16 16
     </component>
17 17
 
18 18
     <component name="delivery" lazy="true">
19
-      <property name="calculatedAt" column="calculated_at" not-null="true"/>
19
+      <property name="routedAfterHandling" column="routed_after_handling" not-null="true"/>
20
+      <property name="lastUpdatedOn" column="last_update"/>
20 21
       <component name="mostRecentHandlingActivity">
21 22
         <many-to-one name="location" column="most_recent_location_id" foreign-key="most_recent_location_fk" cascade="none"/>
22 23
         <property name="type" column="most_recent_handling_event_type">
@@ -26,6 +27,15 @@
26 27
           </type>
27 28
         </property>
28 29
       </component>
30
+        <component name="mostRecentPhysicalHandlingActivity">
31
+          <many-to-one name="location" column="most_recent_physical_location_id" foreign-key="most_recent_physical_location_fk" cascade="none"/>
32
+          <property name="type" column="most_recent_physical_handling_event_type">
33
+            <type name="org.hibernate.type.EnumType">
34
+              <param name="enumClass">se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent$Type</param>
35
+              <param name="type">12</param><!-- 12 is java.sql.Types.VARCHAR -->
36
+            </type>
37
+          </property>
38
+        </component>
29 39
     </component>
30 40
 
31 41
     <component name="routeSpecification">

+ 34
- 6
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/CargoTest.java 查看文件

@@ -10,6 +10,8 @@ import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingE
10 10
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
11 11
 import static se.citerus.dddsample.tracking.core.domain.model.location.SampleLocations.*;
12 12
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
13
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.loadOnto;
14
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.unloadOff;
13 15
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
14 16
 import se.citerus.dddsample.tracking.core.domain.model.voyage.VoyageNumber;
15 17
 
@@ -58,6 +60,10 @@ public class CargoTest extends TestCase {
58 60
     assertEquals(Voyage.NONE, cargo.currentVoyage());
59 61
   }
60 62
 
63
+  public void testEmptyCtor() {
64
+    new Cargo();
65
+  }
66
+
61 67
   public void testRoutingStatus() throws Exception {
62 68
     final Cargo cargo = new Cargo(new TrackingId("XYZ"), new RouteSpecification(STOCKHOLM, MELBOURNE, new Date()));
63 69
     final Itinerary good = new Itinerary(Leg.deriveLeg(northernRail, SEATTLE, NEWYORK));
@@ -83,20 +89,20 @@ public class CargoTest extends TestCase {
83 89
   public void testOutOrderHandling() throws Exception {
84 90
     Cargo cargo = setUpCargoWithItinerary(STOCKHOLM, HAMBURG, MELBOURNE);
85 91
 
86
-    cargo.handled(HandlingActivity.loadedOnto(crazyVoyage).in(STOCKHOLM));
92
+    cargo.handled(loadOnto(crazyVoyage).in(STOCKHOLM));
87 93
     assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
88 94
     assertThat(cargo.lastKnownLocation(), is(STOCKHOLM));
89 95
 
90
-    cargo.handled(HandlingActivity.unloadedOff(crazyVoyage).in(HAMBURG));
96
+    cargo.handled(unloadOff(crazyVoyage).in(HAMBURG));
91 97
     assertThat(cargo.transportStatus(), is(IN_PORT));
92 98
     assertThat(cargo.lastKnownLocation(), is(HAMBURG));
93 99
 
94
-    cargo.handled(HandlingActivity.unloadedOff(crazyVoyage).in(MELBOURNE));
100
+    cargo.handled(unloadOff(crazyVoyage).in(MELBOURNE));
95 101
     assertThat(cargo.transportStatus(), is(IN_PORT));
96 102
     assertThat(cargo.lastKnownLocation(), is(MELBOURNE));
97 103
 
98 104
     // Out of order handling, does not affect state of cargo
99
-    cargo.handled(HandlingActivity.loadedOnto(crazyVoyage).in(HAMBURG));
105
+    cargo.handled(loadOnto(crazyVoyage).in(HAMBURG));
100 106
     assertThat(cargo.transportStatus(), is(IN_PORT));
101 107
     assertThat(cargo.lastKnownLocation(), is(MELBOURNE));
102 108
   }
@@ -171,6 +177,26 @@ public class CargoTest extends TestCase {
171 177
     assertTrue(cargo.isReadyToClaim());
172 178
   }
173 179
 
180
+  public void testCustomsInMelbourne() throws Exception {
181
+    Cargo cargo = setUpCargoWithItinerary(STOCKHOLM, HAMBURG, MELBOURNE);
182
+    
183
+    /*cargo.handled(unloadOff(crazyVoyage).in(HAMBURG));
184
+    assertThat(cargo.nextExpectedActivity(), is(loadOnto(crazyVoyage).in(HAMBURG)));
185
+    assertFalse(cargo.isMisdirected());
186
+
187
+    cargo.handled(loadOnto(crazyVoyage).in(HAMBURG));
188
+    assertThat(cargo.nextExpectedActivity(), is(unloadOff(crazyVoyage).in(MELBOURNE)));
189
+    assertFalse(cargo.isMisdirected());
190
+
191
+    cargo.handled(unloadOff(crazyVoyage).in(MELBOURNE));
192
+    assertThat(cargo.nextExpectedActivity(), is(customsIn(MELBOURNE)));
193
+    assertFalse(cargo.isMisdirected());
194
+
195
+    cargo.handled(customsIn(MELBOURNE));
196
+    assertThat(cargo.nextExpectedActivity(), is(claimIn(MELBOURNE)));
197
+    assertFalse(cargo.isMisdirected());*/
198
+  }
199
+
174 200
   private Cargo populateCargoReceivedStockholm() throws Exception {
175 201
     final Cargo cargo = setUpCargoWithItinerary(STOCKHOLM, HAMBURG, MELBOURNE);
176 202
     cargo.handled(new HandlingActivity(RECEIVE, STOCKHOLM));
@@ -257,6 +283,8 @@ public class CargoTest extends TestCase {
257 283
     final Cargo cargo = new Cargo(new TrackingId("XYZ"),
258 284
         new RouteSpecification(SHANGHAI, NEWYORK, new Date()));
259 285
 
286
+    assertThat(cargo.customsClearancePoint(), is(Location.UNKNOWN));
287
+
260 288
     //SHA-LGB-NYC
261 289
     cargo.assignToRoute(new Itinerary(
262 290
         Leg.deriveLeg(pacific, SHANGHAI, LONGBEACH),
@@ -301,8 +329,8 @@ public class CargoTest extends TestCase {
301 329
 
302 330
     Itinerary itinerary = new Itinerary(
303 331
         Arrays.asList(
304
-            new Leg(crazyVoyage, origin, midpoint, new Date(), new Date()),
305
-            new Leg(crazyVoyage, midpoint, destination, new Date(), new Date())
332
+            new Leg(crazyVoyage, origin, midpoint, new Date(1), new Date(2)),
333
+            new Leg(crazyVoyage, midpoint, destination, new Date(3), new Date(4))
306 334
         )
307 335
     );
308 336
 

+ 9
- 20
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/DeliveryTest.java 查看文件

@@ -12,7 +12,7 @@ import se.citerus.dddsample.tracking.core.domain.model.location.Location;
12 12
 import static se.citerus.dddsample.tracking.core.domain.model.location.SampleLocations.*;
13 13
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
14 14
 import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.customsIn;
15
-import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.loadedOnto;
15
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.loadOnto;
16 16
 import static se.citerus.dddsample.tracking.core.domain.model.voyage.SampleVoyages.*;
17 17
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
18 18
 
@@ -39,7 +39,7 @@ public class DeliveryTest extends TestCase {
39 39
   public void testOnHandling() {
40 40
     Delivery delivery = Delivery.beforeHandling();
41 41
 
42
-    HandlingActivity load = loadedOnto(HONGKONG_TO_NEW_YORK).in(HONGKONG);
42
+    HandlingActivity load = loadOnto(HONGKONG_TO_NEW_YORK).in(HONGKONG);
43 43
     delivery = delivery.onHandling(load);
44 44
 
45 45
     assertThat(delivery.mostRecentHandlingActivity(), is(load));
@@ -51,7 +51,7 @@ public class DeliveryTest extends TestCase {
51 51
     assertThat(delivery.mostRecentHandlingActivity(), is(customs));
52 52
     assertThat(delivery.mostRecentPhysicalHandlingActivity(), is(load));
53 53
 
54
-    HandlingActivity loadAgain = loadedOnto(NEW_YORK_TO_DALLAS).in(NEWYORK);
54
+    HandlingActivity loadAgain = loadOnto(NEW_YORK_TO_DALLAS).in(NEWYORK);
55 55
     delivery = delivery.onHandling(loadAgain);
56 56
 
57 57
     assertThat(delivery.mostRecentHandlingActivity(), is(loadAgain));
@@ -61,11 +61,10 @@ public class DeliveryTest extends TestCase {
61 61
   public void testDerivedFromRouteSpecificationAndItinerary() throws Exception {
62 62
     assertEquals(ROUTED, delivery.routingStatus(itinerary, routeSpecification));
63 63
     assertEquals(Voyage.NONE, delivery.currentVoyage());
64
-    assertFalse(delivery.isMisdirected(itinerary, routeSpecification));
65 64
     assertFalse(delivery.onTheGroundAtDestination(routeSpecification));
66 65
     assertEquals(Location.UNKNOWN, delivery.lastKnownLocation());
67 66
     assertEquals(NOT_RECEIVED, delivery.transportStatus());
68
-    assertTrue(delivery.lastTimestamp().before(new Date()));
67
+    assertTrue(delivery.lastUpdatedOn().before(new Date()));
69 68
   }
70 69
 
71 70
   public void testUpdateOnHandlingHappyPath() {
@@ -80,14 +79,13 @@ public class DeliveryTest extends TestCase {
80 79
     assertEquals(IN_PORT, newDelivery.transportStatus());
81 80
 
82 81
     // Changed on handling and/or (re-)routing
83
-    assertFalse(newDelivery.isMisdirected(itinerary, routeSpecification));
84 82
     assertFalse(newDelivery.onTheGroundAtDestination(routeSpecification));
85 83
 
86 84
     // Changed on (re-)routing
87 85
     assertEquals(ROUTED, newDelivery.routingStatus(itinerary, routeSpecification));
88 86
 
89 87
     // Updated on every calculation
90
-    assertTrue(delivery.lastTimestamp().before(newDelivery.lastTimestamp()));
88
+    assertTrue(delivery.lastUpdatedOn().before(newDelivery.lastUpdatedOn()));
91 89
 
92 90
     // 2. Load
93 91
 
@@ -98,12 +96,11 @@ public class DeliveryTest extends TestCase {
98 96
     assertEquals(HANGZOU, newDelivery.lastKnownLocation());
99 97
     assertEquals(ONBOARD_CARRIER, newDelivery.transportStatus());
100 98
 
101
-    assertFalse(newDelivery.isMisdirected(itinerary, routeSpecification));
102 99
     assertFalse(newDelivery.onTheGroundAtDestination(routeSpecification));
103 100
 
104 101
     assertEquals(ROUTED, newDelivery.routingStatus(itinerary, routeSpecification));
105 102
 
106
-    assertTrue(delivery.lastTimestamp().before(newDelivery.lastTimestamp()));
103
+    assertTrue(delivery.lastUpdatedOn().before(newDelivery.lastUpdatedOn()));
107 104
 
108 105
     // Skipping intermediate load/unloads
109 106
 
@@ -116,12 +113,11 @@ public class DeliveryTest extends TestCase {
116 113
     assertEquals(STOCKHOLM, newDelivery.lastKnownLocation());
117 114
     assertEquals(IN_PORT, newDelivery.transportStatus());
118 115
 
119
-    assertFalse(newDelivery.isMisdirected(itinerary, routeSpecification));
120 116
     assertTrue(newDelivery.onTheGroundAtDestination(routeSpecification));
121 117
 
122 118
     assertEquals(ROUTED, newDelivery.routingStatus(itinerary, routeSpecification));
123 119
 
124
-    assertTrue(delivery.lastTimestamp().before(newDelivery.lastTimestamp()));
120
+    assertTrue(delivery.lastUpdatedOn().before(newDelivery.lastUpdatedOn()));
125 121
 
126 122
     // 4. Claim
127 123
 
@@ -132,12 +128,11 @@ public class DeliveryTest extends TestCase {
132 128
     assertEquals(STOCKHOLM, newDelivery.lastKnownLocation());
133 129
     assertEquals(CLAIMED, newDelivery.transportStatus());
134 130
 
135
-    assertFalse(newDelivery.isMisdirected(itinerary, routeSpecification));
136 131
     assertFalse(newDelivery.onTheGroundAtDestination(routeSpecification));
137 132
 
138 133
     assertEquals(ROUTED, newDelivery.routingStatus(itinerary, routeSpecification));
139 134
 
140
-    assertTrue(delivery.lastTimestamp().before(newDelivery.lastTimestamp()));
135
+    assertTrue(delivery.lastUpdatedOn().before(newDelivery.lastUpdatedOn()));
141 136
   }
142 137
 
143 138
   public void testUpdateOnHandlingWhenMisdirected() {
@@ -151,27 +146,21 @@ public class DeliveryTest extends TestCase {
151 146
 
152 147
     // Next handling activity is undefined. Need a new itinerary to know what to do.
153 148
 
154
-    assertTrue(newDelivery.isMisdirected(itinerary, routeSpecification));
155 149
     assertFalse(newDelivery.onTheGroundAtDestination(routeSpecification));
156 150
 
157 151
     assertEquals(ROUTED, newDelivery.routingStatus(itinerary, routeSpecification));
158 152
 
159
-    assertTrue(delivery.lastTimestamp().before(newDelivery.lastTimestamp()));
153
+    assertTrue(delivery.lastUpdatedOn().before(newDelivery.lastUpdatedOn()));
160 154
 
161 155
     // New route specification, old itinerary
162 156
     RouteSpecification newRouteSpecification = routeSpecification.withOrigin(HAMBURG);
163 157
     assertEquals(MISROUTED, newDelivery.routingStatus(itinerary, newRouteSpecification));
164 158
 
165
-    // TODO is it really misdirected at this point?
166
-    assertTrue(newDelivery.isMisdirected(itinerary, newRouteSpecification));
167
-
168 159
     Itinerary newItinerary = new Itinerary(
169 160
       Leg.deriveLeg(DALLAS_TO_HELSINKI, HAMBURG, STOCKHOLM)
170 161
     );
171 162
 
172 163
     assertEquals(ROUTED, newDelivery.routingStatus(newItinerary, newRouteSpecification));
173
-    // TODO is it really misdirected here?
174
-    assertTrue(newDelivery.isMisdirected(newItinerary, newRouteSpecification));
175 164
     assertEquals(IN_PORT, newDelivery.transportStatus());
176 165
   }
177 166
 

+ 13
- 13
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/ItineraryTest.java 查看文件

@@ -105,19 +105,19 @@ public class ItineraryTest extends TestCase {
105 105
     Leg rotterdamToGothenburg = Leg.deriveLeg(voyage, ROTTERDAM, GOTHENBURG);
106 106
     Itinerary itinerary = new Itinerary(shanghaiToRotterdam, rotterdamToGothenburg);
107 107
 
108
-    assertThat(itinerary.legMatchOf(receivedIn(SHANGHAI)).leg(), is(shanghaiToRotterdam));
109
-    assertThat(itinerary.legMatchOf(loadedOnto(voyage).in(SHANGHAI)).leg(), is(shanghaiToRotterdam));
110
-    assertThat(itinerary.legMatchOf(unloadedOff(voyage).in(ROTTERDAM)).leg(), is(shanghaiToRotterdam));
111
-    assertThat(itinerary.legMatchOf(claimedIn(GOTHENBURG)).leg(), is(rotterdamToGothenburg));
108
+    assertThat(itinerary.legMatchOf(receiveIn(SHANGHAI)).leg(), is(shanghaiToRotterdam));
109
+    assertThat(itinerary.legMatchOf(loadOnto(voyage).in(SHANGHAI)).leg(), is(shanghaiToRotterdam));
110
+    assertThat(itinerary.legMatchOf(unloadOff(voyage).in(ROTTERDAM)).leg(), is(shanghaiToRotterdam));
111
+    assertThat(itinerary.legMatchOf(claimIn(GOTHENBURG)).leg(), is(rotterdamToGothenburg));
112 112
 
113
-    assertNull(itinerary.legMatchOf(loadedOnto(wrongVoyage).in(SHANGHAI)).leg());
114
-    assertNull(itinerary.legMatchOf(loadedOnto(wrongVoyage).in(NEWYORK)).leg());
113
+    assertNull(itinerary.legMatchOf(loadOnto(wrongVoyage).in(SHANGHAI)).leg());
114
+    assertNull(itinerary.legMatchOf(loadOnto(wrongVoyage).in(NEWYORK)).leg());
115 115
 
116
-    assertNull(itinerary.legMatchOf(unloadedOff(wrongVoyage).in(ROTTERDAM)).leg());
117
-    assertNull(itinerary.legMatchOf(unloadedOff(wrongVoyage).in(NEWYORK)).leg());
116
+    assertNull(itinerary.legMatchOf(unloadOff(wrongVoyage).in(ROTTERDAM)).leg());
117
+    assertNull(itinerary.legMatchOf(unloadOff(wrongVoyage).in(NEWYORK)).leg());
118 118
 
119
-    assertNull(itinerary.legMatchOf(receivedIn(NEWYORK)).leg());
120
-    assertNull(itinerary.legMatchOf(claimedIn(NEWYORK)).leg());
119
+    assertNull(itinerary.legMatchOf(receiveIn(NEWYORK)).leg());
120
+    assertNull(itinerary.legMatchOf(claimIn(NEWYORK)).leg());
121 121
   }
122 122
 
123 123
   public void testNextLeg() {
@@ -139,9 +139,9 @@ public class ItineraryTest extends TestCase {
139 139
 
140 140
     Itinerary itinerary = new Itinerary(shanghaiToLongBeach, longBeachToNewYork, newYorkToRotterdam);
141 141
 
142
-    HandlingActivity notOnRoute = HandlingActivity.loadedOnto(pacific).in(STOCKHOLM);
143
-    HandlingActivity loadInShanghai = HandlingActivity.loadedOnto(pacific).in(SHANGHAI);
144
-    HandlingActivity unloadInLongbeach = HandlingActivity.unloadedOff(pacific).in(LONGBEACH);
142
+    HandlingActivity notOnRoute = HandlingActivity.loadOnto(pacific).in(STOCKHOLM);
143
+    HandlingActivity loadInShanghai = HandlingActivity.loadOnto(pacific).in(SHANGHAI);
144
+    HandlingActivity unloadInLongbeach = HandlingActivity.unloadOff(pacific).in(LONGBEACH);
145 145
 
146 146
     assertThat(itinerary.strictlyPriorOf(loadInShanghai, unloadInLongbeach), is(loadInShanghai));
147 147
     assertThat(itinerary.strictlyPriorOf(unloadInLongbeach, loadInShanghai), is(loadInShanghai));

+ 8
- 8
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/LegTest.java 查看文件

@@ -6,8 +6,8 @@ import org.junit.Test;
6 6
 import static se.citerus.dddsample.tracking.core.application.util.DateTestUtil.toDate;
7 7
 import static se.citerus.dddsample.tracking.core.domain.model.location.SampleLocations.*;
8 8
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
9
-import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.loadedOnto;
10
-import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.unloadedOff;
9
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.loadOnto;
10
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.unloadOff;
11 11
 import static se.citerus.dddsample.tracking.core.domain.model.voyage.SampleVoyages.NEW_YORK_TO_DALLAS;
12 12
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
13 13
 
@@ -59,17 +59,17 @@ public class LegTest {
59 59
   public void matchActivity() {
60 60
     Leg newYorkToDallas = Leg.deriveLeg(voyage, NEWYORK, DALLAS);
61 61
 
62
-    assertTrue(newYorkToDallas.matchesActivity(loadedOnto(voyage).in(NEWYORK)));
63
-    assertTrue(newYorkToDallas.matchesActivity(unloadedOff(voyage).in(DALLAS)));
64
-    assertFalse(newYorkToDallas.matchesActivity(loadedOnto(voyage).in(DALLAS)));
65
-    assertFalse(newYorkToDallas.matchesActivity(unloadedOff(voyage).in(NEWYORK)));
62
+    assertTrue(newYorkToDallas.matchesActivity(loadOnto(voyage).in(NEWYORK)));
63
+    assertTrue(newYorkToDallas.matchesActivity(unloadOff(voyage).in(DALLAS)));
64
+    assertFalse(newYorkToDallas.matchesActivity(loadOnto(voyage).in(DALLAS)));
65
+    assertFalse(newYorkToDallas.matchesActivity(unloadOff(voyage).in(NEWYORK)));
66 66
   }
67 67
 
68 68
   @Test
69 69
   public void deriveActivities() {
70 70
     Leg newYorkToDallas = Leg.deriveLeg(voyage, NEWYORK, DALLAS);
71 71
     
72
-    assertThat(newYorkToDallas.deriveLoadActivity(), is(HandlingActivity.loadedOnto(voyage).in(NEWYORK)));
73
-    assertThat(newYorkToDallas.deriveUnloadActivity(), is(HandlingActivity.unloadedOff(voyage).in(DALLAS)));
72
+    assertThat(newYorkToDallas.deriveLoadActivity(), is(HandlingActivity.loadOnto(voyage).in(NEWYORK)));
73
+    assertThat(newYorkToDallas.deriveUnloadActivity(), is(HandlingActivity.unloadOff(voyage).in(DALLAS)));
74 74
   }
75 75
 }

+ 43
- 33
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/scenario/CargoLifecycle.java 查看文件

@@ -36,9 +36,6 @@ public class CargoLifecycle {
36 36
 
37 37
 
38 38
 
39
-
40
-
41
-
42 39
     // Route: Hongkong - Long Beach - New York - Stockholm
43 40
     List<Itinerary> itineraries =
44 41
       routingService.fetchRoutesForSpecification(cargo.routeSpecification());
@@ -48,7 +45,7 @@ public class CargoLifecycle {
48 45
     // Routed
49 46
     assertThat(cargo.transportStatus(), is(NOT_RECEIVED));
50 47
     assertThat(cargo.routingStatus(), is(ROUTED));
51
-    assertThat(cargo.nextExpectedActivity(), is(receivedIn(HONGKONG)));
48
+    assertThat(cargo.nextExpectedActivity(), is(receiveIn(HONGKONG)));
52 49
     assertNotNull(cargo.estimatedTimeOfArrival());
53 50
 
54 51
 
@@ -58,8 +55,11 @@ public class CargoLifecycle {
58 55
 
59 56
 
60 57
 
58
+
59
+
60
+
61 61
     // Received
62
-    cargo.handled(receivedIn(HONGKONG));
62
+    cargo.handled(receiveIn(HONGKONG));
63 63
 
64 64
     assertThat(cargo.transportStatus(), is(IN_PORT));
65 65
     assertThat(cargo.lastKnownLocation(), is(HONGKONG));
@@ -74,13 +74,19 @@ public class CargoLifecycle {
74 74
 
75 75
 
76 76
 
77
+
78
+
79
+
80
+
81
+
82
+
77 83
     // Loaded
78
-    cargo.handled(loadedOnto(v100).in(HONGKONG));
84
+    cargo.handled(loadOnto(v100).in(HONGKONG));
79 85
 
80 86
     assertThat(cargo.currentVoyage(), is(v100));
81 87
     assertThat(cargo.lastKnownLocation(), is(HONGKONG));
82 88
     assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
83
-    assertThat(cargo.nextExpectedActivity(), is(unloadedOff(v100).in(LONGBEACH)));
89
+    assertThat(cargo.nextExpectedActivity(), is(unloadOff(v100).in(LONGBEACH)));
84 90
     assertFalse(cargo.isMisdirected());
85 91
 
86 92
 
@@ -92,33 +98,24 @@ public class CargoLifecycle {
92 98
 
93 99
 
94 100
 
101
+
102
+
103
+
104
+
95 105
     // Unloaded
96
-    cargo.handled(unloadedOff(v100).in(LONGBEACH));
106
+    cargo.handled(unloadOff(v100).in(LONGBEACH));
97 107
 
98 108
     assertThat(cargo.currentVoyage(), is(NONE));
99 109
     assertThat(cargo.lastKnownLocation(), is(LONGBEACH));
100 110
     assertThat(cargo.transportStatus(), is(IN_PORT));
101 111
     assertFalse(cargo.isMisdirected());
102
-    assertThat(cargo.nextExpectedActivity(), is(loadedOnto(v250).in(LONGBEACH)));
103
-
104
-
105
-
106
-
112
+    assertThat(cargo.nextExpectedActivity(), is(loadOnto(v250).in(LONGBEACH)));
107 113
 
108 114
 
109 115
 
110 116
 
111 117
 
112
-    /*
113
-    loadInLongBeach();
114
-    checkDeliveryAfterLoadInLongBeach();
115 118
 
116
-    unloadInNewYork();
117
-    checkDeliveryAfterUnloadInNewYork();
118
-
119
-    loadInNewYork();
120
-    checkDeliveryAfterLoadInNewYork();
121
-    */
122 119
 
123 120
 
124 121
 
@@ -129,7 +126,7 @@ public class CargoLifecycle {
129 126
 
130 127
 
131 128
     // Unloaded in Rotterdam, wasn't supposed to happen
132
-    cargo.handled(unloadedOff(v200).in(ROTTERDAM));
129
+    cargo.handled(unloadOff(v200).in(ROTTERDAM));
133 130
 
134 131
     // Misdirected
135 132
     assertTrue(cargo.isMisdirected());
@@ -145,19 +142,35 @@ public class CargoLifecycle {
145 142
 
146 143
 
147 144
 
145
+
146
+
147
+
148 148
     // Reroute: specify new route
149
+    RouteSpecification currentRouteSpec = cargo.routeSpecification();
149 150
     RouteSpecification newRouteSpec =
150
-      cargo.routeSpecification().withOrigin(cargo.lastKnownLocation());
151
+        currentRouteSpec.withOrigin(cargo.lastKnownLocation());
151 152
     cargo.specifyNewRoute(newRouteSpec);
153
+
152 154
     assertThat(cargo.routingStatus(), is(MISROUTED));
153 155
 
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
164
+
154 165
     // Assign to new route
155
-    List<Itinerary> available =
156
-      routingService.fetchRoutesForSpecification(newRouteSpec);
166
+    List<Itinerary> available = routingService.fetchRoutesForSpecification(newRouteSpec);
157 167
     Itinerary newItinerary = selectAppropriateRoute(available);
158 168
     cargo.assignToRoute(newItinerary);
169
+
159 170
     assertThat(cargo.routingStatus(), is(ROUTED));
160
-    assertThat(cargo.nextExpectedActivity(), is(loadedOnto(v300).in(ROTTERDAM)));
171
+    assertThat(cargo.nextExpectedActivity(), is(loadOnto(v300).in(ROTTERDAM)));
172
+
173
+
161 174
 
162 175
 
163 176
 
@@ -169,7 +182,7 @@ public class CargoLifecycle {
169 182
 
170 183
 
171 184
     // Loaded, back on track
172
-    cargo.handled(loadedOnto(v300).in(ROTTERDAM));
185
+    cargo.handled(loadOnto(v300).in(ROTTERDAM));
173 186
     assertFalse(cargo.isMisdirected());
174 187
     assertThat(cargo.lastKnownLocation(), is(ROTTERDAM));
175 188
     assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
@@ -184,12 +197,9 @@ public class CargoLifecycle {
184 197
 
185 198
 
186 199
 
187
-    /*
188
-    checkDeliveryAfterUnloadInStockholm();
189 200
 
190
-    claimInStockholm();
191
-    checkDeliveryAfterClaimInStockholm();
192
-    */
201
+
202
+
193 203
   }
194 204
 
195 205
   private Itinerary selectAppropriateRoute(List<Itinerary> itineraries) {

+ 57
- 9
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/scenario/CargoLifecycleScenarioTest.java 查看文件

@@ -13,7 +13,7 @@ import se.citerus.dddsample.tracking.core.domain.model.location.Location;
13 13
 import se.citerus.dddsample.tracking.core.domain.model.location.LocationRepository;
14 14
 import static se.citerus.dddsample.tracking.core.domain.model.location.SampleLocations.*;
15 15
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
16
-import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.loadedOnto;
16
+import static se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity.*;
17 17
 import static se.citerus.dddsample.tracking.core.domain.model.voyage.SampleVoyages.*;
18 18
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
19 19
 import static se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage.NONE;
@@ -80,6 +80,9 @@ public class CargoLifecycleScenarioTest {
80 80
     unloadInStockholmOffOf(v200);
81 81
     checkDeliveryAfterUnloadInStockholm();
82 82
 
83
+    customsInStockholm();
84
+    checkDeliveryAfterCustomsInStockholm();
85
+    
83 86
     claimInStockholm();
84 87
     checkDeliveryAfterClaimInStockholm();
85 88
   }
@@ -174,11 +177,14 @@ public class CargoLifecycleScenarioTest {
174 177
     unloadInHamburg();
175 178
     checkDeliveryAfterUnloadInHamburg();
176 179
 
180
+    customsInHamburg();
181
+    checkDeliveryAfterCustomsInHamburg();
182
+
177 183
     loadInHamburg();
178 184
     checkDeliveryAfterLoadInHamburg();
179 185
 
180 186
     unloadInStockholmOffOf(v400);
181
-    checkDeliveryAfterUnloadInStockholm();
187
+    checkDeliveryAfterUnloadInStockholm2();
182 188
 
183 189
     claimInStockholm();
184 190
     checkDeliveryAfterClaimInStockholm();
@@ -297,7 +303,7 @@ public class CargoLifecycleScenarioTest {
297 303
     // New itinerary should satisfy new route
298 304
     assertThat(cargo.routingStatus(), is(ROUTED));
299 305
     assertFalse(cargo.isMisdirected());
300
-    assertThat(cargo.nextExpectedActivity(), is(loadedOnto(v300).in(TOKYO)));
306
+    assertThat(cargo.nextExpectedActivity(), is(loadOnto(v300).in(TOKYO)));
301 307
   }
302 308
 
303 309
   public void loadInTokyo() throws CannotCreateHandlingEventException {
@@ -325,10 +331,27 @@ public class CargoLifecycleScenarioTest {
325 331
     assertThat(cargo.currentVoyage(), is(NONE));
326 332
     assertThat(cargo.lastKnownLocation(), is(HAMBURG));
327 333
     assertThat(cargo.transportStatus(), is(IN_PORT));
334
+    assertThat(cargo.nextExpectedActivity(), is(customsIn(HAMBURG)));
335
+    assertFalse(cargo.isMisdirected());
336
+  }
337
+
338
+  public void customsInHamburg() throws CannotCreateHandlingEventException {
339
+    createHandlingEventAndUpdateAggregates(toDate("2009-03-12"), null, HAMBURG, CUSTOMS);
340
+  }
341
+
342
+  private void checkDeliveryAfterCustomsInHamburg() {
343
+    Cargo cargo = cargoRepository.find(trackingId);
344
+    // Check current state - should be ok
345
+
346
+    assertThat(cargo.currentVoyage(), is(NONE));
347
+    assertThat(cargo.lastKnownLocation(), is(HAMBURG));
348
+    assertThat(cargo.transportStatus(), is(IN_PORT));
349
+    assertThat(cargo.mostRecentHandlingActivity(), is(customsIn(HAMBURG)));
328 350
     assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(LOAD, HAMBURG, v400)));
329 351
     assertFalse(cargo.isMisdirected());
330 352
   }
331 353
 
354
+
332 355
   public void loadInHamburg() throws CannotCreateHandlingEventException {
333 356
     createHandlingEventAndUpdateAggregates(toDate("2009-03-14"), v400, HAMBURG, LOAD);
334 357
   }
@@ -354,7 +377,31 @@ public class CargoLifecycleScenarioTest {
354 377
     assertThat(cargo.currentVoyage(), is(NONE));
355 378
     assertThat(cargo.lastKnownLocation(), is(STOCKHOLM));
356 379
     assertThat(cargo.transportStatus(), is(IN_PORT));
357
-    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(CLAIM, STOCKHOLM)));
380
+    assertThat(cargo.nextExpectedActivity(), is(customsIn(STOCKHOLM)));
381
+    assertFalse(cargo.isMisdirected());
382
+  }
383
+
384
+  private void checkDeliveryAfterUnloadInStockholm2() {
385
+    Cargo cargo = cargoRepository.find(trackingId);
386
+    // Check current state - should be ok
387
+    assertThat(cargo.currentVoyage(), is(NONE));
388
+    assertThat(cargo.lastKnownLocation(), is(STOCKHOLM));
389
+    assertThat(cargo.transportStatus(), is(IN_PORT));
390
+    assertThat(cargo.nextExpectedActivity(), is(claimIn(STOCKHOLM)));
391
+    assertFalse(cargo.isMisdirected());
392
+  }
393
+  
394
+  private void customsInStockholm() {
395
+    createHandlingEventAndUpdateAggregates(toDate("2009-03-16"), null, STOCKHOLM, CUSTOMS);
396
+  }
397
+  
398
+  private void checkDeliveryAfterCustomsInStockholm() {
399
+    Cargo cargo = cargoRepository.find(trackingId);
400
+    // Check current state - should be ok
401
+    assertThat(cargo.currentVoyage(), is(NONE));
402
+    assertThat(cargo.lastKnownLocation(), is(STOCKHOLM));
403
+    assertThat(cargo.transportStatus(), is(IN_PORT));
404
+    assertThat(cargo.nextExpectedActivity(), is(claimIn(STOCKHOLM)));
358 405
     assertFalse(cargo.isMisdirected());
359 406
   }
360 407
 
@@ -373,19 +420,20 @@ public class CargoLifecycleScenarioTest {
373 420
   }
374 421
 
375 422
   private void createHandlingEventAndUpdateAggregates(Date completionTime, Voyage voyage, Location location, HandlingEvent.Type type) throws CannotCreateHandlingEventException {
376
-    updateHandlingEventAggregate(completionTime, voyage, location, type);
377
-    updateCargoAggregate();
423
+    EventSequenceNumber eventSequenceNumber = updateHandlingEventAggregate(completionTime, voyage, location, type);
424
+    updateCargoAggregate(eventSequenceNumber);
378 425
   }
379 426
 
380
-  private void updateHandlingEventAggregate(Date completionTime, Voyage voyage, Location location, HandlingEvent.Type type) throws CannotCreateHandlingEventException {
427
+  private EventSequenceNumber updateHandlingEventAggregate(Date completionTime, Voyage voyage, Location location, HandlingEvent.Type type) throws CannotCreateHandlingEventException {
381 428
     VoyageNumber voyageNumber = voyage != null ? voyage.voyageNumber() : null;
382 429
     HandlingEvent handlingEvent = handlingEventFactory.createHandlingEvent(completionTime, trackingId, voyageNumber, location.unLocode(), type, new OperatorCode("ABCDE"));
383 430
     handlingEventRepository.store(handlingEvent);
431
+    return handlingEvent.sequenceNumber();
384 432
   }
385 433
 
386
-  private void updateCargoAggregate() {
434
+  private void updateCargoAggregate(EventSequenceNumber eventSequenceNumber) {
387 435
     Cargo cargo = cargoRepository.find(trackingId);
388
-    HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(cargo);
436
+    HandlingEvent handlingEvent = handlingEventRepository.find(eventSequenceNumber);
389 437
     cargo.handled(handlingEvent.activity());
390 438
     cargoRepository.store(cargo);
391 439
   }