friendly_shipping 0.6.2 → 0.6.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c625d1ee6c38d9d51bbcadd7280f6b0b415cecb5256d5d884c1da53b6a2afd38
4
- data.tar.gz: 4ce888754298c7a7b5bddb724bb518fb2a0e8993f497188756a70cf72965f5cb
3
+ metadata.gz: ff4df4b78d4b922cd7f2c08ad8256c828783be946fdbd1ba551a5ec562f3176f
4
+ data.tar.gz: 472c8ef33c9e0b581fe582f630daf04033719c5c90ba2f7ac0a2194a6aed1f20
5
5
  SHA512:
6
- metadata.gz: c20df19636bdc7ace8658ada396f7d094da5f7ea776e6b555af0aa875e893a0d5d6bb5143e1ee9ac89922c8924478caf08afd6527859d539318d7971c054112a
7
- data.tar.gz: b5ba5a4fc4b5c66861c4d04743f30ce0a3d5ad3452dad0646bc9a8683a666d76ef41b4eec9bb583713d9a500ae5632341cc92e331acdd71c89d42c21c221b190
6
+ metadata.gz: 89fd85041a4800344354421548d197e642a4812e0e9af04d9bcc45f663e704506bff9b9901708a1ff452309e991a83134b84d88e945e5f8627dc5e776bdd5910
7
+ data.tar.gz: 872f43aea4c769fde901b47fa67e39e90b089cd5d10e3fccf28317b81dab234bb56c10590b00c930e7c50de358baa903c7ce3e383f52c4dbe867af54a42c18ae
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  /coverage/
5
5
  /doc/
6
6
  /.idea/
7
+ /.vscode/
7
8
  /pkg/
8
9
  /spec/reports/
9
10
  /tmp/
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.3] - 2020-10-30
8
+
9
+ ### Added
10
+
11
+ - USPS Service: Append HFP (Hold For Pickup) to service code when necessary (#110)
12
+ - USPS Service: Add Priority Cubic shipping method (#113)
13
+
14
+ ### Changed
15
+
16
+ - USPS Service: Refactor to use explicit service codes (#111)
17
+ - USPS Service: Match Priority Express by CLASSID instead of service name (#112)
18
+ - UPS Service: Rename peak surcharge keys to match UPS docs (#114)
19
+
7
20
  ## [0.6.2] - 2020-08-12
8
21
 
9
22
  - UPS Service: Be more resilient when UPS does not send a PickupTime element
@@ -75,8 +75,8 @@ module FriendlyShipping
75
75
  "407" => "EXTENDED AREA PICKUP",
76
76
  "410" => "RETURN OF DOCUMENT",
77
77
  "430" => "PEAK SEASON",
78
- "431" => "PEAK SEASON SURCHARGE - LARGE PACK",
79
- "432" => "PEAK SEASON SURCHARGE - ADDITIONAL HANDLING",
78
+ "431" => "LARGE PACKAGE SEASONAL SURCHARGE",
79
+ "432" => "ADDITIONAL HANDLING SEASONAL SURCHARGE",
80
80
  "440" => "SHIP LARGE PACKAGE",
81
81
  "441" => "CARBON NEUTRAL",
82
82
  "442" => "PKG QV IN TRANSIT NOTIFICATION",
@@ -54,6 +54,7 @@ module FriendlyShipping
54
54
  SERVICE_NAME_TAG = 'MailService'
55
55
  RATE_TAG = 'Rate'
56
56
  COMMERCIAL_RATE_TAG = 'CommercialRate'
57
+ COMMERCIAL_PLUS_RATE_TAG = 'CommercialPlusRate'
57
58
  CURRENCY = Money::Currency.new('USD').freeze
58
59
 
59
60
  class << self
@@ -82,27 +83,36 @@ module FriendlyShipping
82
83
 
83
84
  # Some USPS services only offer commercial pricing. Unfortunately, USPS then returns a retail rate of 0.
84
85
  # In these cases, return the commercial rate instead of the normal rate.
86
+ #
85
87
  # Some rates are available in both commercial and retail pricing - if we want the commercial pricing here,
86
88
  # we need to specify the commercial_pricing property on the `Physical::Package`.
87
- rate_value = if (package_options.commercial_pricing || rate_node.at(RATE_TAG).text.to_d.zero?) && rate_node.at(COMMERCIAL_RATE_TAG)
88
- rate_node.at(COMMERCIAL_RATE_TAG).text.to_d
89
- else
90
- rate_node.at(RATE_TAG).text.to_d
91
- end
89
+ #
90
+ commercial_rate_requested_or_rate_is_zero = package_options.commercial_pricing || rate_node.at(RATE_TAG).text.to_d.zero?
91
+ commercial_rate_available = rate_node.at(COMMERCIAL_RATE_TAG) || rate_node.at(COMMERCIAL_PLUS_RATE_TAG)
92
+
93
+ rate_value =
94
+ if commercial_rate_requested_or_rate_is_zero && commercial_rate_available
95
+ rate_node.at(COMMERCIAL_RATE_TAG)&.text&.to_d || rate_node.at(COMMERCIAL_PLUS_RATE_TAG).text.to_d
96
+ else
97
+ rate_node.at(RATE_TAG).text.to_d
98
+ end
92
99
 
93
100
  # The rate expressed as a RubyMoney objext
94
101
  rate = Money.new(rate_value * CURRENCY.subunit_to_unit, CURRENCY)
95
102
 
96
- # Which shipping method does this rate belong to? This is trickier than it sounds, because we match
97
- # strings here, and we have a `Priority Mail` and `Priority Mail Express` shipping method.
98
- # If we have multiple matches, we take the longest matching shipping method name so `Express` rates
99
- # do not accidentally get marked as `Priority` only.
100
- possible_shipping_methods = SHIPPING_METHODS.select do |sm|
101
- service_name.tr('-', ' ').upcase.starts_with?(sm.service_code)
102
- end.sort_by do |shipping_method|
103
- shipping_method.name.length
104
- end
105
- shipping_method = possible_shipping_methods.last
103
+ # Which shipping method does this rate belong to? We first try to match a rate to a shipping method
104
+ # by class ID (the CLASSID attribute in the USPS API rate response). Not every shipping method
105
+ # has a class ID defined, and a shipping method can have multiple class IDs (for example, Priority
106
+ # Express has different class IDs for standard, hold for pickup, and Sunday/Holiday delivery).
107
+ #
108
+ # If we don't find a match for class ID, we next try to match a rate to a shipping method using the
109
+ # shipping method's service code. The USPS API rate response includes a name for each rate in the
110
+ # MailService element. We match to see if the name starts with the given value. For example:
111
+ # `Priority Mail Express 2-day™`
112
+ #
113
+ shipping_method =
114
+ SHIPPING_METHODS.detect { |sm| sm.data[:class_ids]&.include?(service_code) } ||
115
+ SHIPPING_METHODS.detect { |sm| service_name.tr('-', ' ').upcase.starts_with?(sm.service_code) }
106
116
 
107
117
  # We find out the box name using a bit of Regex magic using named captures. See the `BOX_REGEX`
108
118
  # constant above.
@@ -115,7 +115,7 @@ module FriendlyShipping
115
115
  # This will likely be somewhat more work in the future.
116
116
  MAIL_CLASSES = {
117
117
  '1' => 'Priority Mail Express',
118
- '2' => 'Priority',
118
+ '2' => 'Priority Mail',
119
119
  '3' => 'First-Class',
120
120
  '6' => 'Package Services'
121
121
  }.freeze
@@ -49,11 +49,13 @@ module FriendlyShipping
49
49
  def service_code
50
50
  return 'ALL' unless shipping_method
51
51
 
52
- if commercial_pricing
53
- "#{shipping_method.service_code} COMMERCIAL"
54
- else
55
- shipping_method.service_code
56
- end
52
+ # Cubic shipping methods don't have HFP or COMMERCIAL modifiers
53
+ return shipping_method.service_code if shipping_method.service_code =~ /CUBIC/
54
+
55
+ service_code = [shipping_method.service_code]
56
+ service_code << 'HFP' if hold_for_pickup
57
+ service_code << 'COMMERCIAL' if commercial_pricing
58
+ service_code.join(' ')
57
59
  end
58
60
  end
59
61
  end
@@ -29,22 +29,33 @@ module FriendlyShipping
29
29
  package_service_retail: 'PACKAGE SERVICE RETAIL'
30
30
  }.freeze
31
31
 
32
+ CLASS_IDS = {
33
+ priority_mail_express: {
34
+ standard: '3',
35
+ hold_for_pickup: '2',
36
+ sunday_holiday_delivery: '23'
37
+ },
38
+ priority_mail_cubic: '999'
39
+ }.freeze
40
+
32
41
  SHIPPING_METHODS = [
33
- 'First-Class',
34
- 'Package Services',
35
- 'Priority',
36
- 'Priority Mail Express',
37
- 'Standard Post',
38
- 'Retail Ground',
39
- 'Media Mail',
40
- 'Library Mail',
41
- ].map do |shipping_method_name|
42
+ ['FIRST CLASS', 'First-Class'],
43
+ ['PACKAGE SERVICES', 'Package Services'],
44
+ ['PRIORITY', 'Priority Mail'],
45
+ ['PRIORITY MAIL EXPRESS', 'Priority Mail Express', CLASS_IDS[:priority_mail_express].values],
46
+ ['PRIORITY MAIL CUBIC', 'Priority Mail Cubic', CLASS_IDS[:priority_mail_cubic]],
47
+ ['STANDARD POST', 'Standard Post'],
48
+ ['RETAIL GROUND', 'Retail Ground'],
49
+ ['MEDIA MAIL', 'Media Mail'],
50
+ ['LIBRARY MAIL', 'Library Mail'],
51
+ ].map do |code, name, class_ids|
42
52
  FriendlyShipping::ShippingMethod.new(
43
53
  origin_countries: [Carmen::Country.coded('US')],
44
- name: shipping_method_name,
45
- service_code: shipping_method_name.tr('-', ' ').upcase,
54
+ name: name,
55
+ service_code: code,
46
56
  domestic: true,
47
- international: false
57
+ international: false,
58
+ data: { class_ids: class_ids }
48
59
  )
49
60
  end.freeze
50
61
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module FriendlyShipping
4
4
  class ShippingMethod
5
- attr_reader :name, :service_code, :carrier, :origin_countries
5
+ attr_reader :name, :service_code, :carrier, :origin_countries, :data
6
6
 
7
7
  # @param [String] name The shipping method's name
8
8
  # @param [String] service_code The shipping method's service code
@@ -11,6 +11,7 @@ module FriendlyShipping
11
11
  # @param [Boolean] multi_package Whether this is a multi-package shipping method
12
12
  # @param [FriendlyShipping::Carrier] carrier This shipping method's carrier
13
13
  # @param [Array] origin_countries Countries this shipping method ships from
14
+ # @param [Hash] data Additional carrier-specific data for this shipping method
14
15
  def initialize(
15
16
  name: nil,
16
17
  service_code: nil,
@@ -18,7 +19,8 @@ module FriendlyShipping
18
19
  international: nil,
19
20
  multi_package: nil,
20
21
  carrier: nil,
21
- origin_countries: []
22
+ origin_countries: [],
23
+ data: {}
22
24
  )
23
25
  @name = name
24
26
  @service_code = service_code
@@ -27,6 +29,7 @@ module FriendlyShipping
27
29
  @multi_package = multi_package
28
30
  @carrier = carrier
29
31
  @origin_countries = origin_countries
32
+ @data = data
30
33
  end
31
34
 
32
35
  def domestic?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FriendlyShipping
4
- VERSION = "0.6.2"
4
+ VERSION = "0.6.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendly_shipping
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-12 00:00:00.000000000 Z
11
+ date: 2020-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: data_uri