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,113 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'savon'
|
4
|
+
require 'cargowise/order_search'
|
5
|
+
require 'cargowise/shipment_search'
|
6
|
+
|
7
|
+
module Cargowise
|
8
|
+
|
9
|
+
# The starting point for accessing data from your logistics provider. See
|
10
|
+
# the README for usage tips.
|
11
|
+
#
|
12
|
+
# You should create a new Client instance for each company API you plan
|
13
|
+
# to query.
|
14
|
+
#
|
15
|
+
class Client
|
16
|
+
|
17
|
+
def initialize(opts = {})
|
18
|
+
@order_uri = opts[:order_uri]
|
19
|
+
@shipment_uri = opts[:shipment_uri]
|
20
|
+
@code = opts[:company_code]
|
21
|
+
@username = opts[:username]
|
22
|
+
@password = opts[:password]
|
23
|
+
end
|
24
|
+
|
25
|
+
# begin an order search. See the docs for Cargowise::OrderSearch for info
|
26
|
+
# on what you can do with the object returned from this method.
|
27
|
+
def orders
|
28
|
+
OrderSearch.new(orders_client)
|
29
|
+
end
|
30
|
+
|
31
|
+
# begin a shipment search. See the docs for Cargowise::ShipmentSearch for
|
32
|
+
# info on what you can do with the object returned from this method.
|
33
|
+
def shipments
|
34
|
+
ShipmentSearch.new(shipments_client)
|
35
|
+
end
|
36
|
+
|
37
|
+
def orders_hello
|
38
|
+
response = orders_client.call(:hello)
|
39
|
+
response.xpath("//tns:HelloResponse/tns:HelloResult/text()", {"tns" => Cargowise::DEFAULT_NS}).to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def shipments_hello
|
43
|
+
response = shipments_client.call(:hello)
|
44
|
+
response.xpath("//tns:HelloResponse/tns:HelloResult/text()", {"tns" => Cargowise::DEFAULT_NS}).to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
# Find the base URI for the web interface at this client
|
48
|
+
#
|
49
|
+
def base_uri
|
50
|
+
uri = @shipment_uri || @order_uri || ""
|
51
|
+
uri.to_s[/(.+)\/WebService.+/,1]
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def orders_client
|
57
|
+
build_client(order_wsdl_path, @order_uri)
|
58
|
+
end
|
59
|
+
|
60
|
+
def order_wsdl_path
|
61
|
+
File.join(
|
62
|
+
File.dirname(__FILE__),
|
63
|
+
"order_wsdl.xml"
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def shipments_client
|
68
|
+
build_client(shipment_wsdl_path, @shipment_uri)
|
69
|
+
end
|
70
|
+
|
71
|
+
def shipment_wsdl_path
|
72
|
+
File.join(
|
73
|
+
File.dirname(__FILE__),
|
74
|
+
"shipment_wsdl.xml"
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: make some of these configurable via the initialize to Cargowise::Client. Will enable
|
79
|
+
# providers that need special treatment to work (like OHL) to be configured differently
|
80
|
+
# by the calling code
|
81
|
+
def build_client(wsdl_path, endpoint_uri)
|
82
|
+
Savon.client(
|
83
|
+
wsdl: wsdl_path,
|
84
|
+
endpoint: endpoint_uri,
|
85
|
+
|
86
|
+
# Cargowise servers can be super slow to respond, this gives them time
|
87
|
+
# to have a smoko before responding to our queries.
|
88
|
+
read_timeout: 240,
|
89
|
+
|
90
|
+
# OHL uses cargowise and has a load balancer that freaks out if we use
|
91
|
+
# the OpenSSL 1.0.1 default of TLS1.1.
|
92
|
+
ssl_version: :TLSv1,
|
93
|
+
|
94
|
+
# savon 2.2.0 ignores the above ssl_version unless this is set to
|
95
|
+
# false. Annoying.
|
96
|
+
ssl_verify_mode: :none,
|
97
|
+
|
98
|
+
# turn off logging to keep me sane. Change this to true when developing
|
99
|
+
log: false,
|
100
|
+
|
101
|
+
# the cargowsie API requires auth details in the SOAP header of every
|
102
|
+
# request
|
103
|
+
soap_header: {
|
104
|
+
"tns:WebTrackerSOAPHeader" => {
|
105
|
+
"tns:CompanyCode" => @code,
|
106
|
+
"tns:UserName" => @username,
|
107
|
+
"tns:Password" => @password
|
108
|
+
}
|
109
|
+
}
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'cargowise/abstract_result'
|
4
|
+
|
5
|
+
module Cargowise
|
6
|
+
|
7
|
+
# Extra shipping detail associated with a Shipment. Not built
|
8
|
+
# directly, but available via the consols() attribute
|
9
|
+
# of the Shipment model.
|
10
|
+
#
|
11
|
+
class Consol < AbstractResult
|
12
|
+
|
13
|
+
attr_reader :master_bill, :console_mode, :transport_mode
|
14
|
+
attr_reader :vessel_name, :voyage_flight
|
15
|
+
attr_reader :load_port, :discharge_port
|
16
|
+
|
17
|
+
def initialize(node)
|
18
|
+
@node = node
|
19
|
+
|
20
|
+
@master_bill = text_value("./MasterBill")
|
21
|
+
@console_mode = text_value("./ConsolMode")
|
22
|
+
@transport_mode = text_value("./TransportMode")
|
23
|
+
@vessel_name = text_value("./VesselName")
|
24
|
+
@voyage_flight = text_value("./VoyageFlight")
|
25
|
+
@load_port = text_value("./LoadPort")
|
26
|
+
@discharge_port = text_value("./DischargePort")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'cargowise/abstract_result'
|
4
|
+
|
5
|
+
module Cargowise
|
6
|
+
|
7
|
+
# A document that is associated with a Shipment. Not built
|
8
|
+
# directly, but available via the documents() attribute
|
9
|
+
# of the Shipment model.
|
10
|
+
#
|
11
|
+
class Document < AbstractResult
|
12
|
+
|
13
|
+
attr_reader :date, :description, :link
|
14
|
+
|
15
|
+
def initialize(node)
|
16
|
+
@node = node
|
17
|
+
|
18
|
+
@date = time_value("./Date")
|
19
|
+
@description = text_value("./Description")
|
20
|
+
@link = text_value("./Link")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'cargowise/abstract_result'
|
4
|
+
|
5
|
+
module Cargowise
|
6
|
+
|
7
|
+
# An invoice that is associated with a Shipment. Not built
|
8
|
+
# directly, but available via the invoices() attribute
|
9
|
+
# of the Shipment model.
|
10
|
+
#
|
11
|
+
class Invoice < AbstractResult
|
12
|
+
|
13
|
+
attr_reader :number, :issuer, :type, :date, :due_date
|
14
|
+
attr_reader :currency, :total, :outstanding
|
15
|
+
attr_reader :link
|
16
|
+
|
17
|
+
def initialize(node)
|
18
|
+
@node = node
|
19
|
+
|
20
|
+
@number = text_value("./InvoiceNumber")
|
21
|
+
@issuer = text_value("./IssuerName")
|
22
|
+
@date = time_value("./InvoiceDate")
|
23
|
+
@due_date = time_value("./DueDate")
|
24
|
+
@currency = text_value("./Currency")
|
25
|
+
@total = decimal_value("./TotalAmount")
|
26
|
+
@outstanding = decimal_value("./OutstandingAmount")
|
27
|
+
@link = text_value("./Link")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'cargowise/abstract_result'
|
4
|
+
require 'cargowise/shipment'
|
5
|
+
|
6
|
+
module Cargowise
|
7
|
+
|
8
|
+
# A purchase order that is being shipped to from a supplier to
|
9
|
+
# you via a logistics company.
|
10
|
+
#
|
11
|
+
# Typcially you will setup an arrangement with your account manager
|
12
|
+
# where they are sent copies of POs so they can be entered into the
|
13
|
+
# database and tracked.
|
14
|
+
#
|
15
|
+
# All order objects are read-only, see the object attributes to see
|
16
|
+
# what information is available.
|
17
|
+
#
|
18
|
+
# Use class find methods to retrieve order info from your logistics
|
19
|
+
# company.
|
20
|
+
#
|
21
|
+
# Order.find_by_order_number(...)
|
22
|
+
# Order.find_incomplete(...)
|
23
|
+
# etc
|
24
|
+
#
|
25
|
+
class Order < AbstractResult
|
26
|
+
|
27
|
+
attr_reader :order_number, :order_status, :description, :datetime
|
28
|
+
attr_reader :order_total, :transport_mode, :container_mode
|
29
|
+
attr_reader :invoice_number
|
30
|
+
|
31
|
+
attr_reader :buyer_name
|
32
|
+
|
33
|
+
attr_reader :supplier_name
|
34
|
+
|
35
|
+
attr_reader :shipments
|
36
|
+
|
37
|
+
def initialize(node)
|
38
|
+
@node = node
|
39
|
+
|
40
|
+
@order_number = text_value("./OrderIdentifier/OrderNumber")
|
41
|
+
@invoice_number = text_value("./OrderDetail/InvoiceNumber")
|
42
|
+
@order_status = text_value("./OrderDetail/OrderStatus")
|
43
|
+
@description = text_value("./OrderDetail/Description")
|
44
|
+
@datetime = time_value("./OrderDetail/OrderDateTime")
|
45
|
+
@order_total = decimal_value("./OrderDetail/OrderTotal")
|
46
|
+
@transport_mode = text_value("./OrderDetail/TransportMode")
|
47
|
+
@container_mode = text_value("./OrderDetail/ContainerMode")
|
48
|
+
|
49
|
+
@buyer_name = text_value("./OrderDetail/Buyer/OrganisationDetails/Name")
|
50
|
+
|
51
|
+
@supplier_name = text_value("./OrderDetail/Supplier/OrganisationDetails/Name")
|
52
|
+
|
53
|
+
@shipments = node_array("./Shipment").map { |node|
|
54
|
+
Shipment.new(node)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_xml
|
59
|
+
@node.to_xml
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'cargowise/order'
|
4
|
+
|
5
|
+
module Cargowise
|
6
|
+
|
7
|
+
class OrderSearch
|
8
|
+
|
9
|
+
def initialize(savon_client)
|
10
|
+
@savon_client = savon_client
|
11
|
+
end
|
12
|
+
|
13
|
+
# find all orders with an OrderNumber that matches ref
|
14
|
+
#
|
15
|
+
def by_order_number(ref)
|
16
|
+
filter_hash = {
|
17
|
+
"tns:Filter" => {
|
18
|
+
"tns:Number" => {
|
19
|
+
"tns:NumberSearchField" => "OrderNumber",
|
20
|
+
"tns:NumberValue" => ref
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
get_order_list(filter_hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
# find all orders with a ShipmentNumber that matches ref
|
28
|
+
#
|
29
|
+
def by_shipment_number(ref)
|
30
|
+
filter_hash = {
|
31
|
+
"tns:Filter" => {
|
32
|
+
"tns:Number" => {
|
33
|
+
"tns:NumberSearchField" => "ShipmentNumber",
|
34
|
+
"tns:NumberValue" => ref
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
get_order_list(filter_hash)
|
39
|
+
end
|
40
|
+
|
41
|
+
# find all orders still marked as incomplete.
|
42
|
+
#
|
43
|
+
def incomplete
|
44
|
+
filter_hash = {
|
45
|
+
"tns:Filter" => { "tns:OrderStatus" => "INC" }
|
46
|
+
}
|
47
|
+
get_order_list(filter_hash)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# return an array of orders. Each order *should* correspond to a buyer PO.
|
53
|
+
#
|
54
|
+
# filter_hash should be a hash that will be serialised into an
|
55
|
+
# XML fragment specifying the search criteria. See the WSDL documentation
|
56
|
+
# for samples
|
57
|
+
#
|
58
|
+
def get_order_list(filter_hash)
|
59
|
+
response = @savon_client.call(:get_order_list, message: filter_hash)
|
60
|
+
response.xpath("//tns:GetOrderListResult/tns:WebOrder", {"tns" => Cargowise::DEFAULT_NS}).map do |node|
|
61
|
+
Cargowise::Order.new(node)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|