1package cargo
2
3// TODO: It would make sense to have this in its own package. Unfortunately,
4// then there would be a circular dependency between the cargo and handling
5// packages since cargo.Delivery would use handling.HandlingEvent and
6// handling.HandlingEvent would use cargo.TrackingID. Also,
7// HandlingEventFactory depends on the cargo repository.
8//
9// It would make sense not having the cargo package depend on handling.
10
11import (
12	"errors"
13	"time"
14
15	"github.com/go-kit/kit/examples/shipping/location"
16	"github.com/go-kit/kit/examples/shipping/voyage"
17)
18
19// HandlingActivity represents how and where a cargo can be handled, and can
20// be used to express predictions about what is expected to happen to a cargo
21// in the future.
22type HandlingActivity struct {
23	Type         HandlingEventType
24	Location     location.UNLocode
25	VoyageNumber voyage.Number
26}
27
28// HandlingEvent is used to register the event when, for instance, a cargo is
29// unloaded from a carrier at a some location at a given time.
30type HandlingEvent struct {
31	TrackingID TrackingID
32	Activity   HandlingActivity
33}
34
35// HandlingEventType describes type of a handling event.
36type HandlingEventType int
37
38// Valid handling event types.
39const (
40	NotHandled HandlingEventType = iota
41	Load
42	Unload
43	Receive
44	Claim
45	Customs
46)
47
48func (t HandlingEventType) String() string {
49	switch t {
50	case NotHandled:
51		return "Not Handled"
52	case Load:
53		return "Load"
54	case Unload:
55		return "Unload"
56	case Receive:
57		return "Receive"
58	case Claim:
59		return "Claim"
60	case Customs:
61		return "Customs"
62	}
63
64	return ""
65}
66
67// HandlingHistory is the handling history of a cargo.
68type HandlingHistory struct {
69	HandlingEvents []HandlingEvent
70}
71
72// MostRecentlyCompletedEvent returns most recently completed handling event.
73func (h HandlingHistory) MostRecentlyCompletedEvent() (HandlingEvent, error) {
74	if len(h.HandlingEvents) == 0 {
75		return HandlingEvent{}, errors.New("delivery history is empty")
76	}
77
78	return h.HandlingEvents[len(h.HandlingEvents)-1], nil
79}
80
81// HandlingEventRepository provides access a handling event store.
82type HandlingEventRepository interface {
83	Store(e HandlingEvent)
84	QueryHandlingHistory(TrackingID) HandlingHistory
85}
86
87// HandlingEventFactory creates handling events.
88type HandlingEventFactory struct {
89	CargoRepository    Repository
90	VoyageRepository   voyage.Repository
91	LocationRepository location.Repository
92}
93
94// CreateHandlingEvent creates a validated handling event.
95func (f *HandlingEventFactory) CreateHandlingEvent(registered time.Time, completed time.Time, id TrackingID,
96	voyageNumber voyage.Number, unLocode location.UNLocode, eventType HandlingEventType) (HandlingEvent, error) {
97
98	if _, err := f.CargoRepository.Find(id); err != nil {
99		return HandlingEvent{}, err
100	}
101
102	if _, err := f.VoyageRepository.Find(voyageNumber); err != nil {
103		// TODO: This is pretty ugly, but when creating a Receive event, the voyage number is not known.
104		if len(voyageNumber) > 0 {
105			return HandlingEvent{}, err
106		}
107	}
108
109	if _, err := f.LocationRepository.Find(unLocode); err != nil {
110		return HandlingEvent{}, err
111	}
112
113	return HandlingEvent{
114		TrackingID: id,
115		Activity: HandlingActivity{
116			Type:         eventType,
117			Location:     unLocode,
118			VoyageNumber: voyageNumber,
119		},
120	}, nil
121}
122