cargowise-ts 1.0.0.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG +64 -0
- data/COPYING +340 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +107 -0
- data/TODO +2 -0
- data/lib/cargowise/abstract_result.rb +89 -0
- data/lib/cargowise/client.rb +113 -0
- data/lib/cargowise/consol.rb +29 -0
- data/lib/cargowise/document.rb +23 -0
- data/lib/cargowise/invoice.rb +30 -0
- data/lib/cargowise/order.rb +64 -0
- data/lib/cargowise/order_search.rb +67 -0
- data/lib/cargowise/order_wsdl.xml +2047 -0
- data/lib/cargowise/packing.rb +25 -0
- data/lib/cargowise/shipment.rb +158 -0
- data/lib/cargowise/shipment_search.rb +100 -0
- data/lib/cargowise/shipment_wsdl.xml +1446 -0
- data/lib/cargowise.rb +20 -0
- metadata +148 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'cargowise/abstract_result'
|
4
|
+
|
5
|
+
module Cargowise
|
6
|
+
|
7
|
+
# Extra packing detail associated with a Shipment. Not built
|
8
|
+
# directly, but available via the packings attribute
|
9
|
+
# of the Shipment model.
|
10
|
+
#
|
11
|
+
class Packing < AbstractResult
|
12
|
+
|
13
|
+
attr_reader :pack_type, :line_price, :weight, :volume, :description
|
14
|
+
|
15
|
+
def initialize(node)
|
16
|
+
@node = node
|
17
|
+
|
18
|
+
@pack_type = text_value("./PackType")
|
19
|
+
@line_price = decimal_value("./LinePrice")
|
20
|
+
@weight = kg_value("./Weight")
|
21
|
+
@volume = cubic_value("./Volume")
|
22
|
+
@description = text_value("./Description")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'cargowise/abstract_result'
|
4
|
+
require 'cargowise/packing'
|
5
|
+
require 'cargowise/consol'
|
6
|
+
require 'cargowise/document'
|
7
|
+
require 'cargowise/invoice'
|
8
|
+
|
9
|
+
module Cargowise
|
10
|
+
|
11
|
+
# A shipment that is currently on its way to you. Could take on a
|
12
|
+
# variety of forms - carton, palet, truck? Could be travelling via
|
13
|
+
# air, sea, road, rail, donkey?
|
14
|
+
#
|
15
|
+
# Typcially you will lookup the status of a shipment you're aware of
|
16
|
+
# using the shipment number:
|
17
|
+
#
|
18
|
+
# Shipment.find_by_shipment_number(...)
|
19
|
+
#
|
20
|
+
# If you want all recent shipments (delivered and undelivered) to
|
21
|
+
# ensure you know what's coming:
|
22
|
+
#
|
23
|
+
# Shipment.find_with_recent_activity(...)
|
24
|
+
#
|
25
|
+
# All shipment objects are read-only, see the object attributes to see
|
26
|
+
# what information is available.
|
27
|
+
#
|
28
|
+
class Shipment < AbstractResult
|
29
|
+
|
30
|
+
attr_reader :number, :housebill, :goods_description, :service_level
|
31
|
+
attr_reader :client_reference
|
32
|
+
attr_reader :origin, :destination, :etd, :eta, :delivered_date
|
33
|
+
attr_reader :kg, :cubic_meters
|
34
|
+
|
35
|
+
attr_reader :shipper_name
|
36
|
+
|
37
|
+
attr_reader :consignee_name
|
38
|
+
|
39
|
+
attr_reader :consols, :packings, :documents, :invoices
|
40
|
+
|
41
|
+
def initialize(node)
|
42
|
+
@node = node
|
43
|
+
|
44
|
+
@number = text_value("./Number")
|
45
|
+
@housebill = text_value("./HouseBill")
|
46
|
+
@goods_description = text_value("./GoodsDescription")
|
47
|
+
@service_level = text_value("./ServiceLevel")
|
48
|
+
@client_reference = text_value("./ClientReference")
|
49
|
+
@origin = text_value("./Origin")
|
50
|
+
@destination = text_value("./Destination")
|
51
|
+
@etd = time_value("./ETD")
|
52
|
+
@eta = time_value("./ETA")
|
53
|
+
@delivered_date = time_value("./DeliveredDate")
|
54
|
+
@kg = kg_value("./Weight")
|
55
|
+
@cubic_meters = cubic_value("./Size")
|
56
|
+
|
57
|
+
@shipper_name = text_value("./Shipper/OrganisationDetails/Name")
|
58
|
+
|
59
|
+
@consignee_name = text_value("./Consignee/OrganisationDetails/Name")
|
60
|
+
|
61
|
+
@consols = node_array("./Consols/Consol").map { |node|
|
62
|
+
Consol.new(node)
|
63
|
+
}
|
64
|
+
|
65
|
+
@packings = node_array("./Packings/Packing").map { |node|
|
66
|
+
Packing.new(node)
|
67
|
+
}
|
68
|
+
|
69
|
+
@documents = node_array("./DocumentLinks/DocumentLink").map { |node|
|
70
|
+
Document.new(node)
|
71
|
+
}.sort_by { |doc|
|
72
|
+
doc.date
|
73
|
+
}
|
74
|
+
|
75
|
+
@invoices = node_array("./RelatedInvoiceLinks/InvoiceLink").map { |node|
|
76
|
+
Invoice.new(node)
|
77
|
+
}.sort_by { |inv|
|
78
|
+
inv.due_date
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
# returns the raw XML string this shipment is based on
|
83
|
+
#
|
84
|
+
def to_xml
|
85
|
+
@node.to_xml
|
86
|
+
end
|
87
|
+
|
88
|
+
# returns a space separated string with all transport modes being used
|
89
|
+
# to move this shipment
|
90
|
+
#
|
91
|
+
def transport_mode
|
92
|
+
@consols.map { |con| con.transport_mode }.uniq.sort.join(" ")
|
93
|
+
end
|
94
|
+
|
95
|
+
# lookup full Cargowise::Order objects for each order on this shipment.
|
96
|
+
#
|
97
|
+
# client is a Cargowise::Client instance to look for the related shipments on
|
98
|
+
#
|
99
|
+
def orders(client)
|
100
|
+
@orders ||= client.orders.by_shipment_number(self.number)
|
101
|
+
end
|
102
|
+
|
103
|
+
# lookup related Cargowise::Shipment objects. These are usually "child" shipments
|
104
|
+
# grouped under a parent. Think a consolidated pallet (the parent) with cartons from
|
105
|
+
# multiple suppliers (the children).
|
106
|
+
#
|
107
|
+
# client is a Cargowise::Client instance to look for the related shipments on
|
108
|
+
#
|
109
|
+
def related_shipments(client)
|
110
|
+
@related ||= @consols.map { |consol|
|
111
|
+
consol.master_bill
|
112
|
+
}.compact.map { |master_bill|
|
113
|
+
client.shipments.by_masterbill_number(master_bill)
|
114
|
+
}.flatten.select { |shipment|
|
115
|
+
shipment.number != self.number
|
116
|
+
}.compact
|
117
|
+
end
|
118
|
+
|
119
|
+
# if this shipment has an order ref associated with it, find it.
|
120
|
+
#
|
121
|
+
# This data isn't available via the API, so we need to screen scrape the
|
122
|
+
# website to get it.
|
123
|
+
#
|
124
|
+
# client is a Cargowise::Client instance to look for the related shipments on
|
125
|
+
#
|
126
|
+
def order_ref(client)
|
127
|
+
if client.base_uri
|
128
|
+
@order_ref ||= html_page(client).search(".//span[@id='Ztextlabel1']/text()").to_s.strip || ""
|
129
|
+
else
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# retrieve a Mechanize::Page object that containts info on this shipment
|
137
|
+
#
|
138
|
+
def html_page(client)
|
139
|
+
return nil unless client.base_uri
|
140
|
+
|
141
|
+
@html_page ||= begin
|
142
|
+
login_uri = client.base_uri + "/Login/Login.aspx"
|
143
|
+
agent = Mechanize.new
|
144
|
+
agent.agent.http.ssl_version = :TLSv1
|
145
|
+
if File.file?(Cargowise::CA_CERT_FILE)
|
146
|
+
agent.agent.http.ca_file = CA_CERT_FILE
|
147
|
+
end
|
148
|
+
page = agent.get(login_uri)
|
149
|
+
form = page.forms.first
|
150
|
+
input_name = form.fields.detect { |field| field.name.to_s.downcase.include?("number")}.andand.name
|
151
|
+
form.__send__("#{input_name}=", self.number) if input_name
|
152
|
+
form.add_field!("ViewShipmentBtn","View Shipment")
|
153
|
+
agent.submit(form)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'cargowise/shipment'
|
4
|
+
|
5
|
+
module Cargowise
|
6
|
+
|
7
|
+
class ShipmentSearch
|
8
|
+
|
9
|
+
def initialize(savon_client)
|
10
|
+
@savon_client = savon_client
|
11
|
+
end
|
12
|
+
|
13
|
+
# find all shipments with a MasterBillNumber that matches ref
|
14
|
+
#
|
15
|
+
def by_masterbill_number(ref)
|
16
|
+
by_number("MasterBillNumber", ref)
|
17
|
+
end
|
18
|
+
|
19
|
+
# find all shipments with a ShipmentNumber that matches ref
|
20
|
+
#
|
21
|
+
def by_shipment_number(ref)
|
22
|
+
by_number("ShipmentNumber", ref)
|
23
|
+
end
|
24
|
+
|
25
|
+
# find all shipments that haven't been delivered yet.
|
26
|
+
#
|
27
|
+
# This times out on some systems, possibly because the logistics company
|
28
|
+
# isn't correctly marking shipments as delivered, so the result is too
|
29
|
+
# large to transfer in a timely manner.
|
30
|
+
#
|
31
|
+
def undelivered
|
32
|
+
filter_hash = {
|
33
|
+
"tns:Filter" => { "tns:Status" => "Undelivered" }
|
34
|
+
}
|
35
|
+
get_shipments_list(filter_hash)
|
36
|
+
end
|
37
|
+
|
38
|
+
# find all shipments that had some activity in the past fourteen days. This could
|
39
|
+
# include leaving port, being delivered or passing a milestone.
|
40
|
+
#
|
41
|
+
def with_recent_activity
|
42
|
+
filter_hash = {
|
43
|
+
"tns:Filter" => {
|
44
|
+
"tns:Date" => {
|
45
|
+
"tns:DateSearchField" => "ALL",
|
46
|
+
"tns:FromDate" => (Date.today - 14).strftime("%Y-%m-%d"),
|
47
|
+
"tns:ToDate" => (Date.today + 14).strftime("%Y-%m-%d")
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
get_shipments_list(filter_hash)
|
52
|
+
end
|
53
|
+
|
54
|
+
# find all shipments that had were shipped in the past 14 days or will ship in
|
55
|
+
# the next 14 days
|
56
|
+
#
|
57
|
+
def recently_shipped
|
58
|
+
filter_hash = {
|
59
|
+
"tns:Filter" => {
|
60
|
+
"tns:Date" => {
|
61
|
+
"tns:DateSearchField" => "ETD",
|
62
|
+
"tns:FromDate" => (Date.today - 14).strftime("%Y-%m-%d"),
|
63
|
+
"tns:ToDate" => (Date.today + 14).strftime("%Y-%m-%d")
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
get_shipments_list(filter_hash)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def by_number(field, ref)
|
73
|
+
filter_hash = {
|
74
|
+
"tns:Filter" => {
|
75
|
+
"tns:Number" => {
|
76
|
+
"tns:NumberSearchField" => field,
|
77
|
+
"tns:NumberValue" => ref
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
get_shipments_list(filter_hash)
|
82
|
+
end
|
83
|
+
|
84
|
+
# return an array of shipments. Each shipment should correspond to
|
85
|
+
# a consolidated shipment from the freight company.
|
86
|
+
#
|
87
|
+
# filter_hash should be a hash that will be serialised into an
|
88
|
+
# XML fragment specifying the search criteria. See the WSDL documentation
|
89
|
+
# for samples
|
90
|
+
#
|
91
|
+
def get_shipments_list(filter_hash)
|
92
|
+
response = @savon_client.call(:get_shipments_list, message: filter_hash)
|
93
|
+
response.xpath("//tns:GetShipmentsListResult/tns:WebShipment", {"tns" => Cargowise::DEFAULT_NS}).map do |node|
|
94
|
+
Cargowise::Shipment.new(node)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|