cargowise-ts 1.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+