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,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
+