reactive_freight 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +5 -0
- data/MIT-LICENSE +20 -0
- data/README.md +166 -0
- data/Rakefile +8 -0
- data/accessorial_symbols.txt +95 -0
- data/lib/reactive_freight.rb +21 -0
- data/lib/reactive_freight/carrier.rb +62 -0
- data/lib/reactive_freight/carriers.rb +18 -0
- data/lib/reactive_freight/carriers/btvp.rb +384 -0
- data/lib/reactive_freight/carriers/clni.rb +59 -0
- data/lib/reactive_freight/carriers/ctbv.rb +35 -0
- data/lib/reactive_freight/carriers/dphe.rb +296 -0
- data/lib/reactive_freight/carriers/drrq.rb +303 -0
- data/lib/reactive_freight/carriers/fcsy.rb +24 -0
- data/lib/reactive_freight/carriers/fwda.rb +243 -0
- data/lib/reactive_freight/carriers/jfj_transportation.rb +11 -0
- data/lib/reactive_freight/carriers/pens.rb +135 -0
- data/lib/reactive_freight/carriers/rdfs.rb +320 -0
- data/lib/reactive_freight/carriers/saia.rb +336 -0
- data/lib/reactive_freight/carriers/sefl.rb +234 -0
- data/lib/reactive_freight/carriers/totl.rb +96 -0
- data/lib/reactive_freight/carriers/wrds.rb +218 -0
- data/lib/reactive_freight/configuration/carriers/btvp.yml +139 -0
- data/lib/reactive_freight/configuration/carriers/clni.yml +107 -0
- data/lib/reactive_freight/configuration/carriers/ctbv.yml +117 -0
- data/lib/reactive_freight/configuration/carriers/dphe.yml +124 -0
- data/lib/reactive_freight/configuration/carriers/drrq.yml +115 -0
- data/lib/reactive_freight/configuration/carriers/fcsy.yml +104 -0
- data/lib/reactive_freight/configuration/carriers/fwda.yml +117 -0
- data/lib/reactive_freight/configuration/carriers/jfj_transportation.yml +2 -0
- data/lib/reactive_freight/configuration/carriers/pens.yml +22 -0
- data/lib/reactive_freight/configuration/carriers/rdfs.yml +135 -0
- data/lib/reactive_freight/configuration/carriers/saia.yml +117 -0
- data/lib/reactive_freight/configuration/carriers/sefl.yml +115 -0
- data/lib/reactive_freight/configuration/carriers/totl.yml +107 -0
- data/lib/reactive_freight/configuration/carriers/wrds.yml +19 -0
- data/lib/reactive_freight/configuration/platforms/carrier_logistics.yml +25 -0
- data/lib/reactive_freight/configuration/platforms/liftoff.yml +12 -0
- data/lib/reactive_freight/package.rb +137 -0
- data/lib/reactive_freight/platform.rb +36 -0
- data/lib/reactive_freight/platforms.rb +4 -0
- data/lib/reactive_freight/platforms/carrier_logistics.rb +317 -0
- data/lib/reactive_freight/platforms/liftoff.rb +102 -0
- data/lib/reactive_freight/rate_estimate.rb +113 -0
- data/lib/reactive_freight/shipment_event.rb +10 -0
- data/reactive_freight.gemspec +39 -0
- data/service_type_symbols.txt +4 -0
- metadata +198 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
:accessorials:
|
2
|
+
:mappable:
|
3
|
+
:airport_delivery: chkAPD
|
4
|
+
:airport_pickup: chkAPU
|
5
|
+
:amusement_park_delivery: chkLAD
|
6
|
+
:amusement_park_pickup: chkLAP
|
7
|
+
:appointment_delivery: chkAN
|
8
|
+
:athletic_facility_delivery: chkLAD
|
9
|
+
:athletic_facility_pickup: chkLAP
|
10
|
+
:cemetery_delivery: chkLAD
|
11
|
+
:cemetery_pickup: chkLAP
|
12
|
+
:church_delivery: chkLAD
|
13
|
+
:church_pickup: chkLAP
|
14
|
+
:construction_delivery: chkLAD
|
15
|
+
:construction_pickup: chkLAP
|
16
|
+
:convention_delivery: chkLAD
|
17
|
+
:convention_pickup: chkLAP
|
18
|
+
:dock_dropoff: chkSHC
|
19
|
+
:fair_delivery: chkLAD
|
20
|
+
:fair_pickup: chkLAP
|
21
|
+
:farm_delivery: chkLAD
|
22
|
+
:farm_pickup: chkLAP
|
23
|
+
:fitness_center_delivery: chkLAD
|
24
|
+
:fitness_center_pickup: chkLAP
|
25
|
+
:golf_course_delivery: chkLAD
|
26
|
+
:golf_course_pickup: chkLAP
|
27
|
+
:government_delivery: chkLAD
|
28
|
+
:government_pickup: chkLAP
|
29
|
+
:grocery_store_delivery: chkLAD
|
30
|
+
:grocery_store_pickup: chkLAP
|
31
|
+
:guaranteed_delivery: chkDG
|
32
|
+
:guaranteed_delivery_am: chkNG
|
33
|
+
:guaranteed_delivery_pm: chkDG
|
34
|
+
:hospital_delivery: chkLAD
|
35
|
+
:hospital_pickup: chkLAP
|
36
|
+
:hotel_delivery: chkLAD
|
37
|
+
:hotel_pickup: chkLAP
|
38
|
+
:inside_delivery: chkID
|
39
|
+
:inside_pickup: chkIP
|
40
|
+
:inspection_site_delivery: chkLAD
|
41
|
+
:inspection_site_pickup: chkLAP
|
42
|
+
:jobsite_delivery: chkLAD
|
43
|
+
:jobsite_pickup: chkLAP
|
44
|
+
:liftgate_delivery: chkLGD
|
45
|
+
:liftgate_pickup: chkLGP
|
46
|
+
:mall_delivery: chkLAD
|
47
|
+
:mall_pickup: chkLAP
|
48
|
+
:marina_delivery: chkLAD
|
49
|
+
:marina_pickup: chkLAP
|
50
|
+
:military_site_delivery: chkLAD
|
51
|
+
:military_site_pickup: chkLAP
|
52
|
+
:mine_site_delivery: chkLAD
|
53
|
+
:mine_site_pickup: chkLAP
|
54
|
+
:motel_delivery: chkLAD
|
55
|
+
:motel_pickup: chkLAP
|
56
|
+
:nursing_home_delivery: chkLAD
|
57
|
+
:nursing_home_pickup: chkLAP
|
58
|
+
:park_delivery: chkLAD
|
59
|
+
:park_pickup: chkLAP
|
60
|
+
:prison_delivery: chkLAD
|
61
|
+
:prison_pickup: chkLAP
|
62
|
+
:ranch_delivery: chkLAD
|
63
|
+
:ranch_pickup: chkLAP
|
64
|
+
:reservation_delivery: chkLAD
|
65
|
+
:reservation_pickup: chkLAP
|
66
|
+
:residential_delivery: chkPR
|
67
|
+
:residential_pickup: chkPR
|
68
|
+
:resort_delivery: chkLAD
|
69
|
+
:resort_pickup: chkLAP
|
70
|
+
:restaurant_delivery: chkLAD
|
71
|
+
:restaurant_pickup: chkLAP
|
72
|
+
:school_delivery: chkLAD
|
73
|
+
:school_pickup: chkLAP
|
74
|
+
:steel_mill_delivery: chkLAD
|
75
|
+
:steel_mill_pickup: chkLAP
|
76
|
+
:storage_facility_delivery: chkLAD
|
77
|
+
:storage_facility_pickup: chkLAP
|
78
|
+
:university_delivery: chkLAD
|
79
|
+
:university_pickup: chkLAP
|
80
|
+
:utility_site_delivery: chkLAD
|
81
|
+
:utility_site_pickup: chkLAP
|
82
|
+
# May be inaccurate
|
83
|
+
:unquotable:
|
84
|
+
- :dock_pickup
|
85
|
+
# May be inaccurate
|
86
|
+
:unserviceable:
|
87
|
+
- :afterhours_delivery
|
88
|
+
- :afterhours_pickup
|
89
|
+
- :appointment_pickup
|
90
|
+
- :brewery_delivery
|
91
|
+
- :brewery_pickup
|
92
|
+
- :customs_brokerage
|
93
|
+
- :driver_assist
|
94
|
+
- :early_morning_delivery
|
95
|
+
- :early_morning_pickup
|
96
|
+
- :flatbed_delivery
|
97
|
+
- :flatbed_pickup
|
98
|
+
- :grocery_warehouse_delivery
|
99
|
+
- :grocery_warehouse_pickup
|
100
|
+
- :winery_delivery
|
101
|
+
- :winery_pickup
|
102
|
+
:api:
|
103
|
+
:domain: www.sefl.com
|
104
|
+
:endpoints:
|
105
|
+
:rates: /webconnect/ratequotes/rest/submitQuote
|
106
|
+
:methods:
|
107
|
+
:get_rate: :get
|
108
|
+
:rates: :post
|
109
|
+
:use_ssl: false
|
110
|
+
:attributes:
|
111
|
+
:rates:
|
112
|
+
:with_excessive_length_fees: true
|
113
|
+
:services:
|
114
|
+
:types:
|
115
|
+
- :standard
|
@@ -0,0 +1,107 @@
|
|
1
|
+
:accessorials:
|
2
|
+
:mappable:
|
3
|
+
:appointment_delivery: '&APPT=yes'
|
4
|
+
:inside_delivery: '&IDL=yes'
|
5
|
+
:inside_pickup: '&IDL=yes'
|
6
|
+
:liftgate_delivery: '&LIFT=yes'
|
7
|
+
:liftgate_pickup: '&LIFTP=yes'
|
8
|
+
:residential_delivery: '&RESI=yes'
|
9
|
+
:residential_pickup: '&RESP=yes'
|
10
|
+
# May be inaccurate
|
11
|
+
:unquotable:
|
12
|
+
- :amusement_park_delivery
|
13
|
+
- :amusement_park_pickup
|
14
|
+
- :athletic_facility_delivery
|
15
|
+
- :athletic_facility_delivery
|
16
|
+
- :athletic_facility_pickup
|
17
|
+
- :cemetery_delivery
|
18
|
+
- :cemetery_pickup
|
19
|
+
- :church_delivery
|
20
|
+
- :church_pickup
|
21
|
+
- :construction_delivery
|
22
|
+
- :construction_pickup
|
23
|
+
- :dock_dropoff
|
24
|
+
- :dock_pickup
|
25
|
+
- :farm_delivery
|
26
|
+
- :farm_pickup
|
27
|
+
- :fitness_center_delivery
|
28
|
+
- :fitness_center_pickup
|
29
|
+
- :golf_course_delivery
|
30
|
+
- :golf_course_pickup
|
31
|
+
- :government_delivery
|
32
|
+
- :government_pickup
|
33
|
+
- :grocery_store_delivery
|
34
|
+
- :grocery_store_pickup
|
35
|
+
- :hospital_delivery
|
36
|
+
- :hospital_pickup
|
37
|
+
- :hotel_delivery
|
38
|
+
- :hotel_pickup
|
39
|
+
- :inspection_site_delivery
|
40
|
+
- :inspection_site_pickup
|
41
|
+
- :jobsite_delivery
|
42
|
+
- :jobsite_pickup
|
43
|
+
- :mall_delivery
|
44
|
+
- :mall_pickup
|
45
|
+
- :marina_delivery
|
46
|
+
- :marina_pickup
|
47
|
+
- :mine_site_delivery
|
48
|
+
- :mine_site_pickup
|
49
|
+
- :motel_delivery
|
50
|
+
- :motel_pickup
|
51
|
+
- :nursing_home_delivery
|
52
|
+
- :nursing_home_pickup
|
53
|
+
- :park_delivery
|
54
|
+
- :park_pickup
|
55
|
+
- :prison_delivery
|
56
|
+
- :prison_pickup
|
57
|
+
- :ranch_delivery
|
58
|
+
- :ranch_pickup
|
59
|
+
- :reservation_delivery
|
60
|
+
- :reservation_pickup
|
61
|
+
- :resort_delivery
|
62
|
+
- :resort_pickup
|
63
|
+
- :restaurant_delivery
|
64
|
+
- :restaurant_pickup
|
65
|
+
- :school_delivery
|
66
|
+
- :school_pickup
|
67
|
+
- :steel_mill_delivery
|
68
|
+
- :steel_mill_pickup
|
69
|
+
- :storage_facility_delivery
|
70
|
+
- :storage_facility_pickup
|
71
|
+
- :university_delivery
|
72
|
+
- :university_pickup
|
73
|
+
- :utility_site_delivery
|
74
|
+
- :utility_site_pickup
|
75
|
+
# May be inaccurate
|
76
|
+
:unserviceable:
|
77
|
+
- :airport_delivery
|
78
|
+
- :airport_pickup
|
79
|
+
- :appointment_pickup
|
80
|
+
- :brewery_delivery
|
81
|
+
- :brewery_pickup
|
82
|
+
- :convention_delivery
|
83
|
+
- :convention_pickup
|
84
|
+
- :customs_brokerage
|
85
|
+
- :driver_assist
|
86
|
+
- :early_morning_delivery
|
87
|
+
- :fair_delivery
|
88
|
+
- :fair_pickup
|
89
|
+
- :flatbed_delivery
|
90
|
+
- :flatbed_pickup
|
91
|
+
- :grocery_warehouse_delivery
|
92
|
+
- :grocery_warehouse_pickup
|
93
|
+
- :guaranteed_delivery
|
94
|
+
- :guaranteed_delivery_am
|
95
|
+
- :guaranteed_delivery_pm
|
96
|
+
- :military_site_delivery
|
97
|
+
- :military_site_pickup
|
98
|
+
- :winery_delivery
|
99
|
+
- :winery_pickup
|
100
|
+
:api:
|
101
|
+
:carrier_code: tot
|
102
|
+
:attributes:
|
103
|
+
:rates:
|
104
|
+
:with_excessive_length_fees: true
|
105
|
+
:services:
|
106
|
+
:types:
|
107
|
+
- :standard
|
@@ -0,0 +1,19 @@
|
|
1
|
+
:api:
|
2
|
+
:domain: tracking.wrds.com
|
3
|
+
:endpoints:
|
4
|
+
:pod: /zLogin.aspx
|
5
|
+
:track: /zTrack2.aspx
|
6
|
+
:methods:
|
7
|
+
:track: :post
|
8
|
+
:use_ssl:
|
9
|
+
:bol: false
|
10
|
+
:pod: false
|
11
|
+
:track: true
|
12
|
+
:events:
|
13
|
+
:types:
|
14
|
+
:arrived_at_terminal: arrived
|
15
|
+
:delivered: delivered
|
16
|
+
:departed: departed
|
17
|
+
:services:
|
18
|
+
:types:
|
19
|
+
- :standard
|
@@ -0,0 +1,25 @@
|
|
1
|
+
:api:
|
2
|
+
:domain: tracking.carrierlogistics.com
|
3
|
+
:endpoints:
|
4
|
+
:bol: /scripts/@CARRIER_CODE@.pol/web-login.htm?
|
5
|
+
:pod: /scripts/@CARRIER_CODE@.pol/web-login.htm?
|
6
|
+
:rates: /scripts/@CARRIER_CODE@.pol/ratequote.xml?
|
7
|
+
:track: /scripts/@CARRIER_CODE@.pol/boldetail.htm?
|
8
|
+
:use_ssl:
|
9
|
+
:bol: true
|
10
|
+
:pod: true
|
11
|
+
:rates: true
|
12
|
+
:track: true
|
13
|
+
:attributes:
|
14
|
+
:rates:
|
15
|
+
:with_excessive_length_fees: false
|
16
|
+
:events:
|
17
|
+
:types:
|
18
|
+
:arrived_at_terminal: trailer arrived
|
19
|
+
:delivered: delivered
|
20
|
+
:departed: trailer departed
|
21
|
+
:out_for_delivery: out for delivery
|
22
|
+
:picked_up: picked up
|
23
|
+
:pickup_scheduled: pickup request received
|
24
|
+
:trailer_closed: loaded on trailer
|
25
|
+
:trailer_unloaded: undisp
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReactiveShipping
|
4
|
+
class Package
|
5
|
+
VALID_FREIGHT_CLASSES = [55, 60, 65, 70, 77.5, 85, 92.5, 100, 110, 125, 150, 175, 200, 250, 300, 400].freeze
|
6
|
+
|
7
|
+
attr_writer :declared_freight_class
|
8
|
+
|
9
|
+
def initialize(grams_or_ounces, dimensions, options = {})
|
10
|
+
options = @@default_options.update(options) if @@default_options
|
11
|
+
options.symbolize_keys!
|
12
|
+
@options = options
|
13
|
+
|
14
|
+
# For backward compatibility
|
15
|
+
if dimensions.is_a?(Array)
|
16
|
+
@dimensions = [dimensions].flatten.reject(&:nil?)
|
17
|
+
else
|
18
|
+
@dimensions = [dimensions.dig(:height), dimensions.dig(:width), dimensions.dig(:length)]
|
19
|
+
@dimensions = [@dimensions].flatten.reject(&:nil?)
|
20
|
+
end
|
21
|
+
|
22
|
+
imperial = (options[:units] == :imperial)
|
23
|
+
|
24
|
+
weight_imperial = dimensions_imperial = imperial if options.include?(:units)
|
25
|
+
|
26
|
+
if options.include?(:weight_units)
|
27
|
+
weight_imperial = (options[:weight_units] == :imperial)
|
28
|
+
end
|
29
|
+
|
30
|
+
if options.include?(:dim_units)
|
31
|
+
dimensions_imperial = (options[:dim_units] == :imperial)
|
32
|
+
end
|
33
|
+
|
34
|
+
@weight_unit_system = weight_imperial ? :imperial : :metric
|
35
|
+
@dimensions_unit_system = dimensions_imperial ? :imperial : :metric
|
36
|
+
|
37
|
+
@weight = attribute_from_metric_or_imperial(grams_or_ounces, Measured::Weight, @weight_unit_system, :grams, :ounces)
|
38
|
+
|
39
|
+
if @dimensions.blank?
|
40
|
+
zero_length = Measured::Length.new(0, (dimensions_imperial ? :inches : :centimetres))
|
41
|
+
@dimensions = [zero_length] * 3
|
42
|
+
else
|
43
|
+
# Overriding ReactiveShipping's protected process_dimensions which sorts
|
44
|
+
# them making it confusing for ReactiveFreight carrier API's that expect
|
45
|
+
# the H x W x L order. Since H x W x L is nonstandard in the freight
|
46
|
+
# industry ReactiveFreight introduces explicit functions for each
|
47
|
+
@dimensions = @dimensions.map do |l|
|
48
|
+
attribute_from_metric_or_imperial(l, Measured::Length, @dimensions_unit_system, :centimetres, :inches)
|
49
|
+
end
|
50
|
+
2.downto(@dimensions.length) do |_n|
|
51
|
+
@dimensions.unshift(@dimensions[0])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@value = Package.cents_from(options[:value])
|
56
|
+
@currency = options[:currency] || (options[:value].currency if options[:value].respond_to?(:currency))
|
57
|
+
@cylinder = (options[:cylinder] || options[:tube]) ? true : false
|
58
|
+
@gift = options[:gift] ? true : false
|
59
|
+
@oversized = options[:oversized] ? true : false
|
60
|
+
@unpackaged = options[:unpackaged] ? true : false
|
61
|
+
end
|
62
|
+
|
63
|
+
def cubic_ft
|
64
|
+
if !inches[0].blank? && !inches[1].blank? && !inches[2].blank?
|
65
|
+
cubic_ft = (inches[0] * inches[1] * inches[2]).to_f / 1728
|
66
|
+
return ('%0.2f' % cubic_ft).to_f
|
67
|
+
end
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def density
|
72
|
+
if !inches[0].blank? && !inches[1].blank? && !inches[2].blank? && pounds
|
73
|
+
density = pounds.to_f / cubic_ft
|
74
|
+
return ('%0.2f' % density).to_f
|
75
|
+
end
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def calculated_freight_class
|
80
|
+
sanitized_freight_class(density_to_freight_class(density))
|
81
|
+
end
|
82
|
+
|
83
|
+
def declared_freight_class
|
84
|
+
@declared_freight_class || @options[:declared_freight_class]
|
85
|
+
end
|
86
|
+
|
87
|
+
def freight_class
|
88
|
+
declared_freight_class.blank? ? calculated_freight_class : declared_freight_class
|
89
|
+
end
|
90
|
+
|
91
|
+
def length(unit)
|
92
|
+
@dimensions[2].convert_to(unit).value.to_f
|
93
|
+
end
|
94
|
+
|
95
|
+
def width(unit)
|
96
|
+
@dimensions[1].convert_to(unit).value.to_f
|
97
|
+
end
|
98
|
+
|
99
|
+
def height(unit)
|
100
|
+
@dimensions[0].convert_to(unit).value.to_f
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
def density_to_freight_class(density)
|
106
|
+
return nil unless density
|
107
|
+
return 400 if density < 1
|
108
|
+
return 60 if density > 30
|
109
|
+
|
110
|
+
density_table = [
|
111
|
+
[1, 2, 300],
|
112
|
+
[2, 4, 250],
|
113
|
+
[4, 6, 175],
|
114
|
+
[6, 8, 125],
|
115
|
+
[8, 10, 100],
|
116
|
+
[10, 12, 92.5],
|
117
|
+
[12, 15, 85],
|
118
|
+
[15, 22.5, 70],
|
119
|
+
[22.5, 30, 65],
|
120
|
+
[30, 35, 60]
|
121
|
+
]
|
122
|
+
density_table.each do |density_row|
|
123
|
+
return density_row[2] if (density >= density_row[0]) && (density < density_row[1])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def sanitized_freight_class(freight_class)
|
128
|
+
return nil if freight_class.blank?
|
129
|
+
|
130
|
+
if VALID_FREIGHT_CLASSES.include?(freight_class)
|
131
|
+
return freight_class.to_i == freight_class ? freight_class.to_i : freight_class
|
132
|
+
end
|
133
|
+
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ReactiveShipping
|
4
|
+
class Platform < ReactiveShipping::Carrier
|
5
|
+
attr_accessor :conf
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
requirements.each { |key| requires!(options, key) }
|
9
|
+
@conf = nil
|
10
|
+
@debug = options[:debug].blank? ? false : true
|
11
|
+
@options = options
|
12
|
+
@last_request = nil
|
13
|
+
@test_mode = @options[:test]
|
14
|
+
|
15
|
+
conf_path = File.join(__dir__, 'configuration', 'platforms', "#{self.class.ancestors[1].name.split('::')[1].underscore}.yml")
|
16
|
+
@conf = YAML.safe_load(File.read(conf_path), permitted_classes: [Symbol])
|
17
|
+
|
18
|
+
conf_path = File.join(__dir__, 'configuration', 'carriers', "#{self.class.to_s.split('::')[1].underscore}.yml")
|
19
|
+
@conf = @conf.deep_merge(YAML.safe_load(File.read(conf_path), permitted_classes: [Symbol]))
|
20
|
+
|
21
|
+
@rates_with_excessive_length_fees = @conf.dig(:attributes, :rates, :with_excessive_length_fees)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_bol(*)
|
25
|
+
raise NotImplementedError, "#{self.class.name}: #find_bol not supported"
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_estimate(*)
|
29
|
+
raise NotImplementedError, "#{self.class.name}: #find_estimate not supported"
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_pod(*)
|
33
|
+
raise NotImplementedError, "#{self.class.name}: #find_pod not supported"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|