reactive_shipping 3.0.0
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 +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +33 -0
- data/.yardopts +13 -0
- data/CHANGELOG.md +225 -0
- data/CONTRIBUTING.md +23 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +21 -0
- data/README.md +158 -0
- data/Rakefile +35 -0
- data/dev.yml +17 -0
- data/gemfiles/activesupport42.gemfile +5 -0
- data/gemfiles/activesupport50.gemfile +6 -0
- data/gemfiles/activesupport51.gemfile +5 -0
- data/gemfiles/activesupport52.gemfile +5 -0
- data/gemfiles/activesupport_master.gemfile +5 -0
- data/lib/certs/eParcel.dtd +111 -0
- data/lib/reactive_shipping.rb +26 -0
- data/lib/reactive_shipping/address_validation_response.rb +30 -0
- data/lib/reactive_shipping/carrier.rb +184 -0
- data/lib/reactive_shipping/carriers.rb +35 -0
- data/lib/reactive_shipping/carriers/australia_post.rb +248 -0
- data/lib/reactive_shipping/carriers/benchmark_carrier.rb +31 -0
- data/lib/reactive_shipping/carriers/bogus_carrier.rb +12 -0
- data/lib/reactive_shipping/carriers/canada_post.rb +263 -0
- data/lib/reactive_shipping/carriers/canada_post_pws.rb +908 -0
- data/lib/reactive_shipping/carriers/fedex.rb +797 -0
- data/lib/reactive_shipping/carriers/kunaki.rb +155 -0
- data/lib/reactive_shipping/carriers/new_zealand_post.rb +260 -0
- data/lib/reactive_shipping/carriers/shipwire.rb +178 -0
- data/lib/reactive_shipping/carriers/stamps.rb +860 -0
- data/lib/reactive_shipping/carriers/ups.rb +1060 -0
- data/lib/reactive_shipping/carriers/usps.rb +708 -0
- data/lib/reactive_shipping/carriers/usps_returns.rb +86 -0
- data/lib/reactive_shipping/delivery_date_estimate.rb +20 -0
- data/lib/reactive_shipping/delivery_date_estimates_response.rb +11 -0
- data/lib/reactive_shipping/errors.rb +35 -0
- data/lib/reactive_shipping/external_return_label_request.rb +417 -0
- data/lib/reactive_shipping/external_return_label_response.rb +26 -0
- data/lib/reactive_shipping/label.rb +10 -0
- data/lib/reactive_shipping/label_response.rb +10 -0
- data/lib/reactive_shipping/location.rb +166 -0
- data/lib/reactive_shipping/package.rb +165 -0
- data/lib/reactive_shipping/package_item.rb +60 -0
- data/lib/reactive_shipping/rate_estimate.rb +197 -0
- data/lib/reactive_shipping/rate_response.rb +33 -0
- data/lib/reactive_shipping/response.rb +44 -0
- data/lib/reactive_shipping/shipment_event.rb +22 -0
- data/lib/reactive_shipping/shipment_packer.rb +108 -0
- data/lib/reactive_shipping/shipping_response.rb +34 -0
- data/lib/reactive_shipping/tracking_response.rb +120 -0
- data/lib/reactive_shipping/version.rb +3 -0
- data/reactive_shipping.gemspec +38 -0
- data/shipit.rubygems.yml +1 -0
- data/test/console.rb +39 -0
- data/test/credentials.yml +76 -0
- data/test/fixtures/files/label1.pdf +0 -0
- data/test/fixtures/files/ups-shipping-label.gif +0 -0
- data/test/fixtures/json/australia_post/calculate_domestic.json +13 -0
- data/test/fixtures/json/australia_post/calculate_domestic_2.json +19 -0
- data/test/fixtures/json/australia_post/calculate_international.json +12 -0
- data/test/fixtures/json/australia_post/calculate_international_2.json +15 -0
- data/test/fixtures/json/australia_post/error_message.json +5 -0
- data/test/fixtures/json/australia_post/service_domestic.json +117 -0
- data/test/fixtures/json/australia_post/service_domestic_2.json +117 -0
- data/test/fixtures/json/australia_post/service_international.json +76 -0
- data/test/fixtures/json/australia_post/service_international_2.json +59 -0
- data/test/fixtures/json/newzealandpost/domestic_book.json +1 -0
- data/test/fixtures/json/newzealandpost/domestic_default.json +1 -0
- data/test/fixtures/json/newzealandpost/domestic_error.json +1 -0
- data/test/fixtures/json/newzealandpost/domestic_poster.json +1 -0
- data/test/fixtures/json/newzealandpost/domestic_small_half_pound.json +1 -0
- data/test/fixtures/json/newzealandpost/international_book.json +1 -0
- data/test/fixtures/json/newzealandpost/international_new_zealand_wii.json +1 -0
- data/test/fixtures/json/newzealandpost/international_small_half_pound.json +1 -0
- data/test/fixtures/json/newzealandpost/international_wii.json +1 -0
- data/test/fixtures/xml/canadapost/example_request.xml +25 -0
- data/test/fixtures/xml/canadapost/example_response.xml +130 -0
- data/test/fixtures/xml/canadapost/example_response_error.xml +16 -0
- data/test/fixtures/xml/canadapost/example_response_french.xml +122 -0
- data/test/fixtures/xml/canadapost/example_response_with_nil_value.xml +164 -0
- data/test/fixtures/xml/canadapost/example_response_with_postal_outlet.xml +155 -0
- data/test/fixtures/xml/canadapost/example_response_with_postal_outlet_french.xml +274 -0
- data/test/fixtures/xml/canadapost/example_response_with_strange_delivery_date.xml +130 -0
- data/test/fixtures/xml/canadapost_pws/dnc_tracking_details_en.xml +112 -0
- data/test/fixtures/xml/canadapost_pws/merchant_details_error.xml +7 -0
- data/test/fixtures/xml/canadapost_pws/merchant_details_response.xml +7 -0
- data/test/fixtures/xml/canadapost_pws/option_response.xml +13 -0
- data/test/fixtures/xml/canadapost_pws/option_response_no_conflicts.xml +7 -0
- data/test/fixtures/xml/canadapost_pws/rates_info.xml +190 -0
- data/test/fixtures/xml/canadapost_pws/rates_info_error.xml +7 -0
- data/test/fixtures/xml/canadapost_pws/receipt_response.xml +42 -0
- data/test/fixtures/xml/canadapost_pws/receipt_response_no_priced_options.xml +36 -0
- data/test/fixtures/xml/canadapost_pws/register_token_error.xml +7 -0
- data/test/fixtures/xml/canadapost_pws/register_token_response.xml +3 -0
- data/test/fixtures/xml/canadapost_pws/service_options_response.xml +42 -0
- data/test/fixtures/xml/canadapost_pws/services_error.xml +6 -0
- data/test/fixtures/xml/canadapost_pws/services_response.xml +32 -0
- data/test/fixtures/xml/canadapost_pws/shipment_domestic.xml +69 -0
- data/test/fixtures/xml/canadapost_pws/shipment_response.xml +20 -0
- data/test/fixtures/xml/canadapost_pws/shipment_us.xml +69 -0
- data/test/fixtures/xml/canadapost_pws/tracking_details_en.xml +152 -0
- data/test/fixtures/xml/canadapost_pws/tracking_details_en_error.xml +7 -0
- data/test/fixtures/xml/canadapost_pws/tracking_details_en_undelivered.xml +116 -0
- data/test/fixtures/xml/canadapost_pws/tracking_details_fr.xml +156 -0
- data/test/fixtures/xml/canadapost_pws/tracking_details_no_expected_delivery_date.xml +40 -0
- data/test/fixtures/xml/fedex/create_shipment_response.xml +2 -0
- data/test/fixtures/xml/fedex/freight_rate_request.xml +82 -0
- data/test/fixtures/xml/fedex/freight_rate_response.xml +506 -0
- data/test/fixtures/xml/fedex/invalid_fedex_reply.xml +27 -0
- data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_commercial_rate_request.xml +79 -0
- data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_no_saturday_rate_request.xml +79 -0
- data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_request.xml +80 -0
- data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_response.xml +214 -0
- data/test/fixtures/xml/fedex/raterequest_reply.xml +213 -0
- data/test/fixtures/xml/fedex/raterequest_response_with_ground_home_delivery.xml +206 -0
- data/test/fixtures/xml/fedex/reply_without_notifications.xml +185 -0
- data/test/fixtures/xml/fedex/tracking_request.xml +29 -0
- data/test/fixtures/xml/fedex/tracking_response_bad_tracking_number.xml +20 -0
- data/test/fixtures/xml/fedex/tracking_response_delivered_at_door.xml +254 -0
- data/test/fixtures/xml/fedex/tracking_response_delivered_at_facility.xml +403 -0
- data/test/fixtures/xml/fedex/tracking_response_delivered_with_signature.xml +269 -0
- data/test/fixtures/xml/fedex/tracking_response_empty_status_detail.xml +84 -0
- data/test/fixtures/xml/fedex/tracking_response_failure_code_9045.xml +52 -0
- data/test/fixtures/xml/fedex/tracking_response_failure_code_9080.xml +51 -0
- data/test/fixtures/xml/fedex/tracking_response_in_transit.xml +127 -0
- data/test/fixtures/xml/fedex/tracking_response_invalid_tracking_number.xml +52 -0
- data/test/fixtures/xml/fedex/tracking_response_missing_status_code.xml +89 -0
- data/test/fixtures/xml/fedex/tracking_response_multiple_results.xml +100 -0
- data/test/fixtures/xml/fedex/tracking_response_not_found.xml +52 -0
- data/test/fixtures/xml/fedex/tracking_response_shipment_exception.xml +209 -0
- data/test/fixtures/xml/fedex/tracking_response_unable_to_process.xml +32 -0
- data/test/fixtures/xml/fedex/tracking_response_with_blank_state.xml +107 -0
- data/test/fixtures/xml/fedex/unknown_fedex_document_reply.xml +3 -0
- data/test/fixtures/xml/kunaki/invalid_state_response.xml +3 -0
- data/test/fixtures/xml/kunaki/no_valid_items_response.xml +3 -0
- data/test/fixtures/xml/kunaki/successful_rates_response.xml +3 -0
- data/test/fixtures/xml/kunaki/unsuccessful_rates_response.xml +9 -0
- data/test/fixtures/xml/shipwire/international_rates_response.xml +17 -0
- data/test/fixtures/xml/shipwire/new_carrier_rate_response.xml +18 -0
- data/test/fixtures/xml/shipwire/no_rates_response.xml +7 -0
- data/test/fixtures/xml/shipwire/rates_response.xml +36 -0
- data/test/fixtures/xml/shipwire/rates_response_no_estimate.xml +14 -0
- data/test/fixtures/xml/stamps/authenticate_user_request.xml +15 -0
- data/test/fixtures/xml/stamps/authenticate_user_response.xml +10 -0
- data/test/fixtures/xml/stamps/cleanse_address_request.xml +19 -0
- data/test/fixtures/xml/stamps/cleanse_address_response.xml +27 -0
- data/test/fixtures/xml/stamps/create_indicium_request.xml +69 -0
- data/test/fixtures/xml/stamps/create_indicium_response.xml +40 -0
- data/test/fixtures/xml/stamps/expired_authenticator_response.xml +15 -0
- data/test/fixtures/xml/stamps/get_account_info_request.xml +11 -0
- data/test/fixtures/xml/stamps/get_account_info_response.xml +36 -0
- data/test/fixtures/xml/stamps/get_purchase_status_request.xml +12 -0
- data/test/fixtures/xml/stamps/get_purchase_status_response.xml +16 -0
- data/test/fixtures/xml/stamps/get_rates_request.xml +19 -0
- data/test/fixtures/xml/stamps/get_rates_response.xml +351 -0
- data/test/fixtures/xml/stamps/purchase_postage_request.xml +13 -0
- data/test/fixtures/xml/stamps/purchase_postage_response.xml +17 -0
- data/test/fixtures/xml/stamps/track_shipment_request.xml +12 -0
- data/test/fixtures/xml/stamps/track_shipment_response.xml +45 -0
- data/test/fixtures/xml/ups/access_request.xml +6 -0
- data/test/fixtures/xml/ups/delivered_shipment_with_refund.xml +290 -0
- data/test/fixtures/xml/ups/delivered_shipment_without_events_tracking_response.xml +62 -0
- data/test/fixtures/xml/ups/delivery_dates_response.xml +140 -0
- data/test/fixtures/xml/ups/example_tracking_response.xml +53 -0
- data/test/fixtures/xml/ups/in_transit_shipment.xml +183 -0
- data/test/fixtures/xml/ups/out_for_delivery_shipment.xml +165 -0
- data/test/fixtures/xml/ups/package_exceeds_maximum_length.xml +12 -0
- data/test/fixtures/xml/ups/rate_single_service.xml +54 -0
- data/test/fixtures/xml/ups/rescheduled_shipment.xml +204 -0
- data/test/fixtures/xml/ups/shipment_accept_response.xml +42 -0
- data/test/fixtures/xml/ups/shipment_confirm_response.xml +33 -0
- data/test/fixtures/xml/ups/shipment_from_tiger_direct.xml +222 -0
- data/test/fixtures/xml/ups/test_real_home_as_residential_destination_response.xml +290 -0
- data/test/fixtures/xml/ups/test_real_home_as_residential_destination_response_with_insured.xml +289 -0
- data/test/fixtures/xml/ups/test_real_home_as_residential_destination_with_origin_account_response.xml +311 -0
- data/test/fixtures/xml/ups/tracking_request.xml +9 -0
- data/test/fixtures/xml/ups/triple_accept_response.xml +72 -0
- data/test/fixtures/xml/ups/triple_confirm_response.xml +32 -0
- data/test/fixtures/xml/ups/void_shipment_response.xml +11 -0
- data/test/fixtures/xml/usps/api_error_rate_response.xml +53 -0
- data/test/fixtures/xml/usps/beverly_hills_to_new_york_book_commercial_base_rate_response.xml +2 -0
- data/test/fixtures/xml/usps/beverly_hills_to_new_york_book_commercial_plus_rate_response.xml +258 -0
- data/test/fixtures/xml/usps/beverly_hills_to_new_york_book_rate_response.xml +108 -0
- data/test/fixtures/xml/usps/beverly_hills_to_ottawa_american_wii_commercial_base_rate_response.xml +84 -0
- data/test/fixtures/xml/usps/beverly_hills_to_ottawa_american_wii_commercial_plus_rate_response.xml +212 -0
- data/test/fixtures/xml/usps/beverly_hills_to_ottawa_american_wii_rate_response.xml +230 -0
- data/test/fixtures/xml/usps/first_class_packages_with_invalid_mail_type_response.xml +12 -0
- data/test/fixtures/xml/usps/first_class_packages_with_mail_type_response.xml +16 -0
- data/test/fixtures/xml/usps/first_class_packages_without_mail_type_response.xml +12 -0
- data/test/fixtures/xml/usps/invalid_xml_response.xml +10 -0
- data/test/fixtures/xml/usps/invalid_xml_tracking_response_error.xml +2 -0
- data/test/fixtures/xml/usps/tracking_request.xml +10 -0
- data/test/fixtures/xml/usps/tracking_request_batch.xml +12 -0
- data/test/fixtures/xml/usps/tracking_response.xml +162 -0
- data/test/fixtures/xml/usps/tracking_response_alt.xml +53 -0
- data/test/fixtures/xml/usps/tracking_response_batch.xml +231 -0
- data/test/fixtures/xml/usps/tracking_response_failure.xml +11 -0
- data/test/fixtures/xml/usps/tracking_response_not_available.xml +12 -0
- data/test/fixtures/xml/usps/tracking_response_test_error.xml +8 -0
- data/test/fixtures/xml/usps/us_rate_request.xml +18 -0
- data/test/fixtures/xml/usps/us_rate_request_large.xml +18 -0
- data/test/fixtures/xml/usps/world_rate_request_only_country.xml +22 -0
- data/test/fixtures/xml/usps/world_rate_request_with_value.xml +24 -0
- data/test/fixtures/xml/usps/world_rate_request_without_value.xml +24 -0
- data/test/fixtures/xml/usps_returns/external_return_label_response.xml +2 -0
- data/test/fixtures/xml/usps_returns/external_return_label_response_failure.xml +10 -0
- data/test/remote/australia_post_test.rb +140 -0
- data/test/remote/canada_post_pws_platform_test.rb +259 -0
- data/test/remote/canada_post_pws_test.rb +169 -0
- data/test/remote/canada_post_test.rb +55 -0
- data/test/remote/fedex_test.rb +400 -0
- data/test/remote/kunaki_test.rb +37 -0
- data/test/remote/new_zealand_post_test.rb +149 -0
- data/test/remote/shipwire_test.rb +84 -0
- data/test/remote/stamps_test.rb +396 -0
- data/test/remote/usps_returns_test.rb +72 -0
- data/test/remote/usps_test.rb +243 -0
- data/test/test_helper.rb +296 -0
- data/test/unit/carrier_test.rb +130 -0
- data/test/unit/carriers/australia_post_test.rb +181 -0
- data/test/unit/carriers/benchmark_test.rb +18 -0
- data/test/unit/carriers/canada_post_pws_rating_test.rb +379 -0
- data/test/unit/carriers/canada_post_pws_register_test.rb +76 -0
- data/test/unit/carriers/canada_post_pws_shipping_test.rb +258 -0
- data/test/unit/carriers/canada_post_pws_test.rb +59 -0
- data/test/unit/carriers/canada_post_pws_tracking_test.rb +154 -0
- data/test/unit/carriers/canada_post_test.rb +148 -0
- data/test/unit/carriers/fedex_test.rb +693 -0
- data/test/unit/carriers/kunaki_test.rb +56 -0
- data/test/unit/carriers/new_zealand_post_test.rb +177 -0
- data/test/unit/carriers/shipwire_test.rb +188 -0
- data/test/unit/carriers/stamps_test.rb +245 -0
- data/test/unit/carriers/ups_test.rb +580 -0
- data/test/unit/carriers/usps_returns_test.rb +45 -0
- data/test/unit/carriers/usps_test.rb +633 -0
- data/test/unit/carriers_test.rb +16 -0
- data/test/unit/external_return_label_request_test.rb +258 -0
- data/test/unit/location_test.rb +234 -0
- data/test/unit/package_item_test.rb +232 -0
- data/test/unit/package_test.rb +404 -0
- data/test/unit/rate_estimate_test.rb +93 -0
- data/test/unit/response_test.rb +38 -0
- data/test/unit/shipment_event_test.rb +20 -0
- data/test/unit/shipment_packer_test.rb +212 -0
- data/test/unit/tracking_response_test.rb +41 -0
- metadata +684 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module ReactiveShipping #:nodoc:
|
|
2
|
+
|
|
3
|
+
class ExternalReturnLabelResponse < Response
|
|
4
|
+
attr_reader :carrier # symbol
|
|
5
|
+
attr_reader :carrier_name # string
|
|
6
|
+
attr_reader :tracking_number # string
|
|
7
|
+
attr_reader :return_label # string
|
|
8
|
+
attr_reader :postal_routing # string
|
|
9
|
+
|
|
10
|
+
def initialize(success, message, params = {}, options = {})
|
|
11
|
+
@carrier = options[:carrier].parameterize.to_sym
|
|
12
|
+
@carrier_name = options[:carrier]
|
|
13
|
+
@return_label = options[:return_label]
|
|
14
|
+
@tracking_number = options[:tracking_number]
|
|
15
|
+
@postal_routing = options[:postal_routing]
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def has_exception?
|
|
20
|
+
@status == :exception
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
alias_method(:exception?, :has_exception?)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
module ReactiveShipping #:nodoc:
|
|
2
|
+
class Location
|
|
3
|
+
ADDRESS_TYPES = %w(residential commercial po_box)
|
|
4
|
+
|
|
5
|
+
ATTRIBUTE_ALIASES = {
|
|
6
|
+
name: [:name],
|
|
7
|
+
country: [:country_code, :country],
|
|
8
|
+
postal_code: [:postal_code, :zip, :postal],
|
|
9
|
+
province: [:province_code, :state_code, :territory_code, :region_code, :province, :state, :territory, :region],
|
|
10
|
+
city: [:city, :town],
|
|
11
|
+
address1: [:address1, :address, :street],
|
|
12
|
+
address2: [:address2],
|
|
13
|
+
address3: [:address3],
|
|
14
|
+
phone: [:phone, :phone_number],
|
|
15
|
+
fax: [:fax, :fax_number],
|
|
16
|
+
email: [:email],
|
|
17
|
+
address_type: [:address_type],
|
|
18
|
+
company_name: [:company, :company_name],
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
attr_reader :options,
|
|
22
|
+
:country,
|
|
23
|
+
:postal_code,
|
|
24
|
+
:province,
|
|
25
|
+
:city,
|
|
26
|
+
:name,
|
|
27
|
+
:address1,
|
|
28
|
+
:address2,
|
|
29
|
+
:address3,
|
|
30
|
+
:phone,
|
|
31
|
+
:fax,
|
|
32
|
+
:email,
|
|
33
|
+
:address_type,
|
|
34
|
+
:company_name
|
|
35
|
+
|
|
36
|
+
alias_method :zip, :postal_code
|
|
37
|
+
alias_method :postal, :postal_code
|
|
38
|
+
alias_method :state, :province
|
|
39
|
+
alias_method :territory, :province
|
|
40
|
+
alias_method :region, :province
|
|
41
|
+
alias_method :company, :company_name
|
|
42
|
+
|
|
43
|
+
def initialize(options = {})
|
|
44
|
+
@country = if options[:country].nil? || options[:country].is_a?(ActiveUtils::Country)
|
|
45
|
+
options[:country]
|
|
46
|
+
else
|
|
47
|
+
ActiveUtils::Country.find(options[:country])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@postal_code = options[:postal_code] || options[:postal] || options[:zip]
|
|
51
|
+
@province = options[:province] || options[:state] || options[:territory] || options[:region]
|
|
52
|
+
@city = options[:city]
|
|
53
|
+
@name = options[:name]
|
|
54
|
+
@address1 = options[:address1]
|
|
55
|
+
@address2 = options[:address2]
|
|
56
|
+
@address3 = options[:address3]
|
|
57
|
+
@phone = options[:phone]
|
|
58
|
+
@fax = options[:fax]
|
|
59
|
+
@email = options[:email]
|
|
60
|
+
@company_name = options[:company_name] || options[:company]
|
|
61
|
+
|
|
62
|
+
self.address_type = options[:address_type]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.from(object, options = {})
|
|
66
|
+
return object if object.is_a?(ReactiveShipping::Location)
|
|
67
|
+
|
|
68
|
+
attributes = {}
|
|
69
|
+
|
|
70
|
+
hash_access = object.respond_to?(:[])
|
|
71
|
+
|
|
72
|
+
ATTRIBUTE_ALIASES.each do |attribute, aliases|
|
|
73
|
+
aliases.detect do |sym|
|
|
74
|
+
value = object[sym] if hash_access
|
|
75
|
+
if !value &&
|
|
76
|
+
object.respond_to?(sym) &&
|
|
77
|
+
(!hash_access || !Hash.public_instance_methods.include?(sym))
|
|
78
|
+
value = object.send(sym)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
attributes[attribute] = value if value
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
attributes.delete(:address_type) unless ADDRESS_TYPES.include?(attributes[:address_type].to_s)
|
|
86
|
+
|
|
87
|
+
new(attributes.update(options))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def country_code(format = :alpha2)
|
|
91
|
+
@country.nil? ? nil : @country.code(format).value
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def residential?
|
|
95
|
+
@address_type == 'residential'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def commercial?
|
|
99
|
+
@address_type == 'commercial'
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def po_box?
|
|
103
|
+
@address_type == 'po_box'
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def unknown?
|
|
107
|
+
country_code == 'ZZ'
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def address_type=(value)
|
|
111
|
+
return unless value.present?
|
|
112
|
+
raise ArgumentError.new("address_type must be one of #{ADDRESS_TYPES.join(', ')}") unless ADDRESS_TYPES.include?(value.to_s)
|
|
113
|
+
@address_type = value.to_s
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def to_hash
|
|
117
|
+
{
|
|
118
|
+
country: country_code,
|
|
119
|
+
postal_code: postal_code,
|
|
120
|
+
province: province,
|
|
121
|
+
city: city,
|
|
122
|
+
name: name,
|
|
123
|
+
address1: address1,
|
|
124
|
+
address2: address2,
|
|
125
|
+
address3: address3,
|
|
126
|
+
phone: phone,
|
|
127
|
+
fax: fax,
|
|
128
|
+
email: email,
|
|
129
|
+
address_type: address_type,
|
|
130
|
+
company_name: company_name
|
|
131
|
+
}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def to_s
|
|
135
|
+
prettyprint.gsub(/\n/, ' ')
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def prettyprint
|
|
139
|
+
chunks = [@name, @address1, @address2, @address3]
|
|
140
|
+
chunks << [@city, @province, @postal_code].reject(&:blank?).join(', ')
|
|
141
|
+
chunks << @country
|
|
142
|
+
chunks.reject(&:blank?).join("\n")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def inspect
|
|
146
|
+
string = prettyprint
|
|
147
|
+
string << "\nPhone: #{@phone}" unless @phone.blank?
|
|
148
|
+
string << "\nFax: #{@fax}" unless @fax.blank?
|
|
149
|
+
string << "\nEmail: #{@email}" unless @email.blank?
|
|
150
|
+
string
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Returns the postal code as a properly formatted Zip+4 code, e.g. "77095-2233"
|
|
154
|
+
def zip_plus_4
|
|
155
|
+
"#{$1}-#{$2}" if /(\d{5})-?(\d{4})/ =~ @postal_code
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def address2_and_3
|
|
159
|
+
[address2, address3].reject(&:blank?).join(", ")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def ==(other)
|
|
163
|
+
to_hash == other.to_hash
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
module ReactiveShipping #:nodoc:
|
|
2
|
+
class Package
|
|
3
|
+
cattr_accessor :default_options
|
|
4
|
+
attr_reader :options, :value, :currency
|
|
5
|
+
|
|
6
|
+
# Package.new(100, [10, 20, 30], :units => :metric)
|
|
7
|
+
# Package.new(Measured::Weight.new(100, :g), [10, 20, 30].map {|m| Length.new(m, :centimetres)})
|
|
8
|
+
# Package.new(100.grams, [10, 20, 30].map(&:centimetres))
|
|
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
|
+
@dimensions = [dimensions].flatten.reject(&:nil?)
|
|
15
|
+
|
|
16
|
+
imperial = (options[:units] == :imperial)
|
|
17
|
+
|
|
18
|
+
weight_imperial = dimensions_imperial = imperial if options.include?(:units)
|
|
19
|
+
|
|
20
|
+
if options.include?(:weight_units)
|
|
21
|
+
weight_imperial = (options[:weight_units] == :imperial)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if options.include?(:dim_units)
|
|
25
|
+
dimensions_imperial = (options[:dim_units] == :imperial)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@weight_unit_system = weight_imperial ? :imperial : :metric
|
|
29
|
+
@dimensions_unit_system = dimensions_imperial ? :imperial : :metric
|
|
30
|
+
|
|
31
|
+
@weight = attribute_from_metric_or_imperial(grams_or_ounces, Measured::Weight, @weight_unit_system, :grams, :ounces)
|
|
32
|
+
|
|
33
|
+
if @dimensions.blank?
|
|
34
|
+
zero_length = Measured::Length.new(0, (dimensions_imperial ? :inches : :centimetres))
|
|
35
|
+
@dimensions = [zero_length] * 3
|
|
36
|
+
else
|
|
37
|
+
process_dimensions
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@value = Package.cents_from(options[:value])
|
|
41
|
+
@currency = options[:currency] || (options[:value].currency if options[:value].respond_to?(:currency))
|
|
42
|
+
@cylinder = (options[:cylinder] || options[:tube]) ? true : false
|
|
43
|
+
@gift = options[:gift] ? true : false
|
|
44
|
+
@oversized = options[:oversized] ? true : false
|
|
45
|
+
@unpackaged = options[:unpackaged] ? true : false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def unpackaged?
|
|
49
|
+
@unpackaged
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def oversized?
|
|
53
|
+
@oversized
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def cylinder?
|
|
57
|
+
@cylinder
|
|
58
|
+
end
|
|
59
|
+
alias_method :tube?, :cylinder?
|
|
60
|
+
|
|
61
|
+
def gift?
|
|
62
|
+
@gift
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def ounces(options = {})
|
|
66
|
+
weight(options).convert_to(:oz).value.to_f
|
|
67
|
+
end
|
|
68
|
+
alias_method :oz, :ounces
|
|
69
|
+
|
|
70
|
+
def grams(options = {})
|
|
71
|
+
weight(options).convert_to(:g).value.to_f
|
|
72
|
+
end
|
|
73
|
+
alias_method :g, :grams
|
|
74
|
+
|
|
75
|
+
def pounds(options = {})
|
|
76
|
+
weight(options).convert_to(:lb).value.to_f
|
|
77
|
+
end
|
|
78
|
+
alias_method :lb, :pounds
|
|
79
|
+
alias_method :lbs, :pounds
|
|
80
|
+
|
|
81
|
+
def kilograms(options = {})
|
|
82
|
+
weight(options).convert_to(:kg).value.to_f
|
|
83
|
+
end
|
|
84
|
+
alias_method :kg, :kilograms
|
|
85
|
+
alias_method :kgs, :kilograms
|
|
86
|
+
|
|
87
|
+
def inches(measurement = nil)
|
|
88
|
+
@inches ||= @dimensions.map { |m| m.convert_to(:in).value.to_f }
|
|
89
|
+
measurement.nil? ? @inches : measure(measurement, @inches)
|
|
90
|
+
end
|
|
91
|
+
alias_method :in, :inches
|
|
92
|
+
|
|
93
|
+
def centimetres(measurement = nil)
|
|
94
|
+
@centimetres ||= @dimensions.map { |m| m.convert_to(:cm).value.to_f }
|
|
95
|
+
measurement.nil? ? @centimetres : measure(measurement, @centimetres)
|
|
96
|
+
end
|
|
97
|
+
alias_method :cm, :centimetres
|
|
98
|
+
|
|
99
|
+
def weight(options = {})
|
|
100
|
+
case options[:type]
|
|
101
|
+
when nil, :actual
|
|
102
|
+
@weight
|
|
103
|
+
when :volumetric, :dimensional
|
|
104
|
+
@volumetric_weight ||= begin
|
|
105
|
+
m = Measured::Weight.new((centimetres(:box_volume) / 6.0), :grams)
|
|
106
|
+
@weight_unit_system == :imperial ? m.convert_to(:oz) : m
|
|
107
|
+
end
|
|
108
|
+
when :billable
|
|
109
|
+
[weight, weight(:type => :volumetric)].max
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
alias_method :mass, :weight
|
|
113
|
+
|
|
114
|
+
def self.cents_from(money)
|
|
115
|
+
return nil if money.nil?
|
|
116
|
+
if money.respond_to?(:cents)
|
|
117
|
+
return money.cents
|
|
118
|
+
else
|
|
119
|
+
case money
|
|
120
|
+
when Float
|
|
121
|
+
(money * 100).round
|
|
122
|
+
when String
|
|
123
|
+
money =~ /\./ ? (money.to_f * 100).round : money.to_i
|
|
124
|
+
else
|
|
125
|
+
money.to_i
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def attribute_from_metric_or_imperial(obj, klass, unit_system, metric_unit, imperial_unit)
|
|
133
|
+
if obj.is_a?(klass)
|
|
134
|
+
return obj
|
|
135
|
+
else
|
|
136
|
+
return klass.new(obj, (unit_system == :imperial ? imperial_unit : metric_unit))
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def measure(measurement, ary)
|
|
141
|
+
case measurement
|
|
142
|
+
when Integer then ary[measurement]
|
|
143
|
+
when :x, :max, :length, :long then ary[2]
|
|
144
|
+
when :y, :mid, :width, :wide then ary[1]
|
|
145
|
+
when :z, :min, :height, :depth, :high, :deep then ary[0]
|
|
146
|
+
when :girth, :around, :circumference
|
|
147
|
+
self.cylinder? ? (Math::PI * (ary[0] + ary[1]) / 2) : (2 * ary[0]) + (2 * ary[1])
|
|
148
|
+
when :volume then self.cylinder? ? (Math::PI * (ary[0] + ary[1]) / 4)**2 * ary[2] : measure(:box_volume, ary)
|
|
149
|
+
when :box_volume then ary[0] * ary[1] * ary[2]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def process_dimensions
|
|
154
|
+
@dimensions = @dimensions.map do |l|
|
|
155
|
+
attribute_from_metric_or_imperial(l, Measured::Length, @dimensions_unit_system, :centimetres, :inches)
|
|
156
|
+
end.sort
|
|
157
|
+
# [1,2] => [1,1,2]
|
|
158
|
+
# [5] => [5,5,5]
|
|
159
|
+
# etc..
|
|
160
|
+
2.downto(@dimensions.length) do |_n|
|
|
161
|
+
@dimensions.unshift(@dimensions[0])
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module ReactiveShipping #:nodoc:
|
|
2
|
+
class PackageItem
|
|
3
|
+
attr_reader :sku, :hs_code, :value, :name, :weight, :quantity, :options
|
|
4
|
+
|
|
5
|
+
def initialize(name, grams_or_ounces, value, quantity, options = {})
|
|
6
|
+
@name = name
|
|
7
|
+
|
|
8
|
+
imperial = (options[:units] == :imperial)
|
|
9
|
+
|
|
10
|
+
@unit_system = imperial ? :imperial : :metric
|
|
11
|
+
|
|
12
|
+
@weight = grams_or_ounces
|
|
13
|
+
@weight = Measured::Weight.new(grams_or_ounces, (@unit_system == :imperial ? :oz : :g)) unless @weight.is_a?(Measured::Weight)
|
|
14
|
+
|
|
15
|
+
@value = Package.cents_from(value)
|
|
16
|
+
@quantity = quantity > 0 ? quantity : 1
|
|
17
|
+
|
|
18
|
+
@sku = options[:sku]
|
|
19
|
+
@hs_code = options[:hs_code]
|
|
20
|
+
@options = options
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def weight(options = {})
|
|
24
|
+
case options[:type]
|
|
25
|
+
when nil, :actual
|
|
26
|
+
@weight
|
|
27
|
+
when :volumetric, :dimensional
|
|
28
|
+
@volumetric_weight ||= begin
|
|
29
|
+
m = Measured::Weight.new((centimetres(:box_volume) / 6.0), :grams)
|
|
30
|
+
@unit_system == :imperial ? m.in_ounces : m
|
|
31
|
+
end
|
|
32
|
+
when :billable
|
|
33
|
+
[weight, weight(:type => :volumetric)].max
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
alias_method :mass, :weight
|
|
37
|
+
|
|
38
|
+
def ounces(options = {})
|
|
39
|
+
weight(options).convert_to(:oz).value
|
|
40
|
+
end
|
|
41
|
+
alias_method :oz, :ounces
|
|
42
|
+
|
|
43
|
+
def grams(options = {})
|
|
44
|
+
weight(options).convert_to(:g).value
|
|
45
|
+
end
|
|
46
|
+
alias_method :g, :grams
|
|
47
|
+
|
|
48
|
+
def pounds(options = {})
|
|
49
|
+
weight(options).convert_to(:lb).value
|
|
50
|
+
end
|
|
51
|
+
alias_method :lb, :pounds
|
|
52
|
+
alias_method :lbs, :pounds
|
|
53
|
+
|
|
54
|
+
def kilograms(options = {})
|
|
55
|
+
weight(options).convert_to(:kg).value
|
|
56
|
+
end
|
|
57
|
+
alias_method :kg, :kilograms
|
|
58
|
+
alias_method :kgs, :kilograms
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
module ReactiveShipping
|
|
2
|
+
|
|
3
|
+
# Class representing a shipping option with estimated price.
|
|
4
|
+
#
|
|
5
|
+
# @!attribute origin
|
|
6
|
+
# The origin of the shipment
|
|
7
|
+
# @return [ReactiveShipping::Location]
|
|
8
|
+
#
|
|
9
|
+
# @!attribute destination
|
|
10
|
+
# The destination of the shipment
|
|
11
|
+
# @return [ReactiveShipping::Location]
|
|
12
|
+
#
|
|
13
|
+
# @!attribute package_rates
|
|
14
|
+
# A list of rates for all the packages in the shipment
|
|
15
|
+
# @return [Array<{:rate => Integer, :package => ReactiveShipping::Package}>]
|
|
16
|
+
#
|
|
17
|
+
# @!attribute carrier
|
|
18
|
+
# The name of the carrier (e.g. 'USPS', 'FedEx')
|
|
19
|
+
# @return [String]
|
|
20
|
+
# @see ReactiveShipping::Carrier.name
|
|
21
|
+
#
|
|
22
|
+
# @!attribute service_name
|
|
23
|
+
# The name of the shipping service (e.g. 'First Class Ground')
|
|
24
|
+
# @return [String]
|
|
25
|
+
#
|
|
26
|
+
# @!attribute service_code
|
|
27
|
+
# The code of the shipping service
|
|
28
|
+
# @return [String]
|
|
29
|
+
#
|
|
30
|
+
# @!attribute description
|
|
31
|
+
# Public description of the shipping service (e.g. '2 days delivery')
|
|
32
|
+
# @return [String]
|
|
33
|
+
#
|
|
34
|
+
# @!attribute shipping_date
|
|
35
|
+
# The date on which the shipment will be expected. Normally, this means that the
|
|
36
|
+
# delivery date range can only be promised if the shipment is handed over on or
|
|
37
|
+
# before this date.
|
|
38
|
+
# @return [Date]
|
|
39
|
+
#
|
|
40
|
+
# @!attribute delivery_date
|
|
41
|
+
# The date on which the shipment will be delivered. This is usually only available
|
|
42
|
+
# for express shipments; in other cases a {#delivery_range} is given instead.
|
|
43
|
+
# @return [Date]
|
|
44
|
+
#
|
|
45
|
+
# @!attribute delivery_range
|
|
46
|
+
# The minimum and maximum date of when the shipment is expected to be delivered
|
|
47
|
+
# @return [Array<Date>]
|
|
48
|
+
#
|
|
49
|
+
# @!attribute currency
|
|
50
|
+
# ISO4217 currency code of the quoted rate estimates (e.g. `CAD`, `EUR`, or `USD`)
|
|
51
|
+
# @return [String]
|
|
52
|
+
# @see http://en.wikipedia.org/wiki/ISO_4217
|
|
53
|
+
#
|
|
54
|
+
# @!attribute negotiated_rate
|
|
55
|
+
# The negotiated rate in cents
|
|
56
|
+
# @return [Integer]
|
|
57
|
+
#
|
|
58
|
+
# @!attribute compare_price
|
|
59
|
+
# The comparable price in cents
|
|
60
|
+
# @return [Integer]
|
|
61
|
+
#
|
|
62
|
+
# @!attribute phone_required
|
|
63
|
+
# Specifies if a phone number is required for the shipping rate
|
|
64
|
+
# @return [Boolean]
|
|
65
|
+
#
|
|
66
|
+
# @!attribute insurance_price
|
|
67
|
+
# The price of insurance in cents
|
|
68
|
+
# @return [Integer]
|
|
69
|
+
#
|
|
70
|
+
# @!attribute delivery_category
|
|
71
|
+
# The general classification of the delivery method
|
|
72
|
+
# @return [String]
|
|
73
|
+
#
|
|
74
|
+
# @!attribute shipment_options
|
|
75
|
+
# Additional priced options bundled with the given rate estimate with price in cents
|
|
76
|
+
# @return [Array<{ code: String, price: Integer }>]
|
|
77
|
+
#
|
|
78
|
+
# @!attribute charge_items
|
|
79
|
+
# Breakdown of a shipping rate's price with amounts in cents.
|
|
80
|
+
# @return [Array<{ group: String, code: String, name: String, description: String, amount: Integer }>]
|
|
81
|
+
#
|
|
82
|
+
class RateEstimate
|
|
83
|
+
attr_accessor :origin, :destination, :package_rates,
|
|
84
|
+
:carrier, :service_name, :service_code, :description,
|
|
85
|
+
:shipping_date, :delivery_date, :delivery_range,
|
|
86
|
+
:currency, :negotiated_rate, :insurance_price,
|
|
87
|
+
:estimate_reference, :expires_at, :pickup_time,
|
|
88
|
+
:compare_price, :phone_required, :delivery_category,
|
|
89
|
+
:shipment_options, :charge_items, :messages
|
|
90
|
+
|
|
91
|
+
def initialize(origin, destination, carrier, service_name, options = {})
|
|
92
|
+
self.origin, self.destination, self.carrier, self.service_name = origin, destination, carrier, service_name
|
|
93
|
+
self.service_code = options[:service_code]
|
|
94
|
+
self.description = options[:description]
|
|
95
|
+
self.estimate_reference = options[:estimate_reference]
|
|
96
|
+
self.pickup_time = options[:pickup_time]
|
|
97
|
+
self.expires_at = options[:expires_at]
|
|
98
|
+
if options[:package_rates]
|
|
99
|
+
self.package_rates = options[:package_rates].map { |p| p.update(:rate => Package.cents_from(p[:rate])) }
|
|
100
|
+
else
|
|
101
|
+
self.package_rates = Array(options[:packages]).map { |p| {:package => p} }
|
|
102
|
+
end
|
|
103
|
+
self.total_price = options[:total_price]
|
|
104
|
+
self.negotiated_rate = options[:negotiated_rate]
|
|
105
|
+
self.compare_price = options[:compare_price]
|
|
106
|
+
self.phone_required = options[:phone_required]
|
|
107
|
+
self.currency = options[:currency]
|
|
108
|
+
self.delivery_range = options[:delivery_range]
|
|
109
|
+
self.shipping_date = options[:shipping_date]
|
|
110
|
+
self.delivery_date = @delivery_range.last
|
|
111
|
+
self.insurance_price = options[:insurance_price]
|
|
112
|
+
self.delivery_category = options[:delivery_category]
|
|
113
|
+
self.shipment_options = options[:shipment_options] || []
|
|
114
|
+
self.charge_items = options[:charge_items] || []
|
|
115
|
+
self.messages = options[:messages] || []
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# The total price of the shipments in cents.
|
|
119
|
+
# @return [Integer]
|
|
120
|
+
def total_price
|
|
121
|
+
@total_price || @package_rates.sum { |pr| pr[:rate] }
|
|
122
|
+
rescue NoMethodError
|
|
123
|
+
raise ArgumentError.new("RateEstimate must have a total_price set, or have a full set of valid package rates.")
|
|
124
|
+
end
|
|
125
|
+
alias_method :price, :total_price
|
|
126
|
+
|
|
127
|
+
# Adds a package to this rate estimate
|
|
128
|
+
# @param package [ReactiveShipping::Package] The package to add.
|
|
129
|
+
# @param rate [#cents, Float, String, nil] The rate for this package. This is only required if
|
|
130
|
+
# there is no total price for this shipment
|
|
131
|
+
# @return [self]
|
|
132
|
+
def add(package, rate = nil)
|
|
133
|
+
cents = Package.cents_from(rate)
|
|
134
|
+
raise ArgumentError.new("New packages must have valid rate information since this RateEstimate has no total_price set.") if cents.nil? and total_price.nil?
|
|
135
|
+
@package_rates << {:package => package, :rate => cents}
|
|
136
|
+
self
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# The list of packages for which rate estimates are given.
|
|
140
|
+
# @return [Array<ReactiveShipping::Package>]
|
|
141
|
+
def packages
|
|
142
|
+
package_rates.map { |p| p[:package] }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# The number of packages for which rate estimates are given.
|
|
146
|
+
# @return [Integer]
|
|
147
|
+
def package_count
|
|
148
|
+
package_rates.length
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
protected
|
|
152
|
+
|
|
153
|
+
def delivery_range=(delivery_range)
|
|
154
|
+
@delivery_range = delivery_range ? delivery_range.map { |date| date_for(date) }.compact : []
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def total_price=(total_price)
|
|
158
|
+
@total_price = Package.cents_from(total_price)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def negotiated_rate=(negotiated_rate)
|
|
162
|
+
@negotiated_rate = negotiated_rate ? Package.cents_from(negotiated_rate) : nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def compare_price=(compare_price)
|
|
166
|
+
@compare_price = compare_price ? Package.cents_from(compare_price) : nil
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def currency=(currency)
|
|
170
|
+
@currency = ActiveUtils::CurrencyCode.standardize(currency)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def phone_required=(phone_required)
|
|
174
|
+
@phone_required = !!phone_required
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def shipping_date=(shipping_date)
|
|
178
|
+
@shipping_date = date_for(shipping_date)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def insurance_price=(insurance_price)
|
|
182
|
+
@insurance_price = Package.cents_from(insurance_price)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
# Returns a Date object for a given input
|
|
188
|
+
# @param date [String, Date, Time, DateTime, ...] The object to infer a date from.
|
|
189
|
+
# @return [Date, nil] The Date object absed on the input, or `nil` if no date
|
|
190
|
+
# could be determined.
|
|
191
|
+
def date_for(date)
|
|
192
|
+
date && Date.strptime(date.to_s, "%Y-%m-%d")
|
|
193
|
+
rescue ArgumentError
|
|
194
|
+
nil
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|