| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 | 
							- package model
 
- import (
 
- 	"time"
 
- )
 
- // Delivery is the actual transportation of the cargo, as opposed to the
 
- // customer requirement (RouteSpecification) and the plan (Itinerary).
 
- type Delivery struct {
 
- 	Itinerary               Itinerary
 
- 	RouteSpecification      RouteSpecification
 
- 	RoutingStatus           RoutingStatus
 
- 	TransportStatus         TransportStatus
 
- 	NextExpectedActivity    HandlingActivity
 
- 	LastEvent               HandlingEvent
 
- 	LastKnownLocation       UNLocode
 
- 	CurrentVoyage           VoyageNumber
 
- 	ETA                     time.Time
 
- 	IsMisdirected           bool
 
- 	IsUnloadedAtDestination bool
 
- }
 
- // UpdateOnRouting creates a new delivery snapshot to reflect changes in
 
- // routing, i.e. when the route specification or the itinerary has changed but
 
- // no additional handling of the cargo has been performed.
 
- func (d Delivery) UpdateOnRouting(rs RouteSpecification, itinerary Itinerary) Delivery {
 
- 	return newDelivery(d.LastEvent, itinerary, rs)
 
- }
 
- // IsOnTrack checks if the delivery is on track.
 
- func (d Delivery) IsOnTrack() bool {
 
- 	return d.RoutingStatus == Routed && !d.IsMisdirected
 
- }
 
- // DeriveDeliveryFrom creates a new delivery snapshot based on the complete
 
- // handling history of a cargo, as well as its route specification and
 
- // itinerary.
 
- func DeriveDeliveryFrom(rs RouteSpecification, itinerary Itinerary, history HandlingHistory) Delivery {
 
- 	lastEvent, _ := history.MostRecentlyCompletedEvent()
 
- 	return newDelivery(lastEvent, itinerary, rs)
 
- }
 
- // newDelivery creates a up-to-date delivery based on an handling event,
 
- // itinerary and a route specification.
 
- func newDelivery(lastEvent HandlingEvent, itinerary Itinerary, rs RouteSpecification) Delivery {
 
- 	var (
 
- 		routingStatus           = calculateRoutingStatus(itinerary, rs)
 
- 		transportStatus         = calculateTransportStatus(lastEvent)
 
- 		lastKnownLocation       = calculateLastKnownLocation(lastEvent)
 
- 		isMisdirected           = calculateMisdirectedStatus(lastEvent, itinerary)
 
- 		isUnloadedAtDestination = calculateUnloadedAtDestination(lastEvent, rs)
 
- 		currentVoyage           = calculateCurrentVoyage(transportStatus, lastEvent)
 
- 	)
 
- 	d := Delivery{
 
- 		LastEvent:               lastEvent,
 
- 		Itinerary:               itinerary,
 
- 		RouteSpecification:      rs,
 
- 		RoutingStatus:           routingStatus,
 
- 		TransportStatus:         transportStatus,
 
- 		LastKnownLocation:       lastKnownLocation,
 
- 		IsMisdirected:           isMisdirected,
 
- 		IsUnloadedAtDestination: isUnloadedAtDestination,
 
- 		CurrentVoyage:           currentVoyage,
 
- 	}
 
- 	d.NextExpectedActivity = calculateNextExpectedActivity(d)
 
- 	d.ETA = calculateETA(d)
 
- 	return d
 
- }
 
- // Below are internal functions used when creating a new delivery.
 
- func calculateRoutingStatus(itinerary Itinerary, rs RouteSpecification) RoutingStatus {
 
- 	if itinerary.Legs == nil {
 
- 		return NotRouted
 
- 	}
 
- 	if rs.IsSatisfiedBy(itinerary) {
 
- 		return Routed
 
- 	}
 
- 	return Misrouted
 
- }
 
- func calculateMisdirectedStatus(event HandlingEvent, itinerary Itinerary) bool {
 
- 	if event.Activity.Type == NotHandled {
 
- 		return false
 
- 	}
 
- 	return !itinerary.IsExpected(event)
 
- }
 
- func calculateUnloadedAtDestination(event HandlingEvent, rs RouteSpecification) bool {
 
- 	if event.Activity.Type == NotHandled {
 
- 		return false
 
- 	}
 
- 	return event.Activity.Type == Unload && rs.Destination == event.Activity.Location
 
- }
 
- func calculateTransportStatus(event HandlingEvent) TransportStatus {
 
- 	switch event.Activity.Type {
 
- 	case NotHandled:
 
- 		return NotReceived
 
- 	case Load:
 
- 		return OnboardCarrier
 
- 	case Unload:
 
- 		return InPort
 
- 	case Receive:
 
- 		return InPort
 
- 	case Customs:
 
- 		return InPort
 
- 	case Claim:
 
- 		return Claimed
 
- 	}
 
- 	return Unknown
 
- }
 
- func calculateLastKnownLocation(event HandlingEvent) UNLocode {
 
- 	return event.Activity.Location
 
- }
 
- func calculateNextExpectedActivity(d Delivery) HandlingActivity {
 
- 	if !d.IsOnTrack() {
 
- 		return HandlingActivity{}
 
- 	}
 
- 	switch d.LastEvent.Activity.Type {
 
- 	case NotHandled:
 
- 		return HandlingActivity{Type: Receive, Location: d.RouteSpecification.Origin}
 
- 	case Receive:
 
- 		l := d.Itinerary.Legs[0]
 
- 		return HandlingActivity{Type: Load, Location: l.LoadLocation, VoyageNumber: l.VoyageNumber}
 
- 	case Load:
 
- 		for _, l := range d.Itinerary.Legs {
 
- 			if l.LoadLocation == d.LastEvent.Activity.Location {
 
- 				return HandlingActivity{Type: Unload, Location: l.UnloadLocation, VoyageNumber: l.VoyageNumber}
 
- 			}
 
- 		}
 
- 	case Unload:
 
- 		for i, l := range d.Itinerary.Legs {
 
- 			if l.UnloadLocation == d.LastEvent.Activity.Location {
 
- 				if i < len(d.Itinerary.Legs)-1 {
 
- 					return HandlingActivity{Type: Load, Location: d.Itinerary.Legs[i+1].LoadLocation, VoyageNumber: d.Itinerary.Legs[i+1].VoyageNumber}
 
- 				}
 
- 				return HandlingActivity{Type: Claim, Location: l.UnloadLocation}
 
- 			}
 
- 		}
 
- 	}
 
- 	return HandlingActivity{}
 
- }
 
- func calculateCurrentVoyage(transportStatus TransportStatus, event HandlingEvent) VoyageNumber {
 
- 	if transportStatus == OnboardCarrier && event.Activity.Type != NotHandled {
 
- 		return event.Activity.VoyageNumber
 
- 	}
 
- 	return VoyageNumber("")
 
- }
 
- func calculateETA(d Delivery) time.Time {
 
- 	if !d.IsOnTrack() {
 
- 		return time.Time{}
 
- 	}
 
- 	return d.Itinerary.FinalArrivalTime()
 
- }
 
 
  |