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.
@@ -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
+