google4r-checkout 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +16 -0
- data/lib/google4r/checkout/commands.rb +101 -61
- data/lib/google4r/checkout/frontend.rb +30 -0
- data/lib/google4r/checkout/merchant_calculation.rb +4 -0
- data/lib/google4r/checkout/notifications.rb +53 -129
- data/lib/google4r/checkout/shared.rb +382 -7
- data/lib/google4r/checkout/utils.rb +94 -0
- data/lib/google4r/checkout/xml_generation.rb +229 -17
- data/test/integration/checkout_command_test.rb +72 -11
- data/test/unit/backorder_items_command_test.rb +83 -0
- data/test/unit/cancel_items_command_test.rb +89 -0
- data/test/unit/carrier_calculated_shipping_test.rb +57 -0
- data/test/unit/checkout_command_test.rb +6 -1
- data/test/unit/checkout_command_xml_generator_test.rb +1 -0
- data/test/unit/command_test.rb +3 -2
- data/test/unit/digital_content_test.rb +105 -0
- data/test/unit/frontend_test.rb +70 -10
- data/test/unit/item_info_test.rb +69 -0
- data/test/unit/item_test.rb +13 -1
- data/test/unit/merchant_calculated_shipping_test.rb +39 -3
- data/test/unit/notification_acknowledgement_test.rb +25 -1
- data/test/unit/reset_items_shipping_information_command_test.rb +83 -0
- data/test/unit/return_items_command_test.rb +83 -0
- data/test/unit/ship_items_command_test.rb +101 -0
- data/test/unit/tracking_data_test.rb +54 -0
- metadata +13 -3
@@ -202,6 +202,17 @@ module Google4R #:nodoc:
|
|
202
202
|
@unit_price = money
|
203
203
|
end
|
204
204
|
|
205
|
+
# The weigth of the cart item (Weight, required when carrier calculated
|
206
|
+
# shipping is used)
|
207
|
+
attr_reader :weight
|
208
|
+
|
209
|
+
# Sets the weight of this item
|
210
|
+
def weight=(weight)
|
211
|
+
raise "Invalid object type for weight" unless weight.kind_of? Weight
|
212
|
+
@weight = weight
|
213
|
+
end
|
214
|
+
|
215
|
+
|
205
216
|
# Number of units that this item represents (integer, required).
|
206
217
|
attr_accessor :quantity
|
207
218
|
|
@@ -234,7 +245,27 @@ module Google4R #:nodoc:
|
|
234
245
|
|
235
246
|
@tax_table = table
|
236
247
|
end
|
237
|
-
|
248
|
+
|
249
|
+
# DigitalContent information for this item. Optional.
|
250
|
+
attr_reader :digital_content
|
251
|
+
|
252
|
+
def create_digital_content(digital_content=nil, &block)
|
253
|
+
|
254
|
+
if @digital_content.nil?
|
255
|
+
if digital_content.nil?
|
256
|
+
@digital_content = DigitalContent.new
|
257
|
+
else
|
258
|
+
@digital_content = digital_content
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
if block_given?
|
263
|
+
yield @digital_content
|
264
|
+
end
|
265
|
+
|
266
|
+
return @digital_content
|
267
|
+
end
|
268
|
+
|
238
269
|
# Create a new Item in the given Cart. You should not instantize this class directly
|
239
270
|
# but use Cart#create_item instead.
|
240
271
|
def initialize(shopping_cart)
|
@@ -249,6 +280,11 @@ module Google4R #:nodoc:
|
|
249
280
|
result.description = element.elements['item-description'].text
|
250
281
|
result.quantity = element.elements['quantity'].text.to_i
|
251
282
|
result.id = element.elements['merchant-item-id'].text rescue nil
|
283
|
+
|
284
|
+
weight_element = element.elements['item-weight']
|
285
|
+
if not weight_element.nil?
|
286
|
+
result.weight = Weight.create_from_element(weight_element)
|
287
|
+
end
|
252
288
|
|
253
289
|
data_element = element.elements['merchant-private-item-data']
|
254
290
|
if not data_element.nil? then
|
@@ -265,8 +301,70 @@ module Google4R #:nodoc:
|
|
265
301
|
unit_price_currency = element.elements['unit-price'].attributes['currency']
|
266
302
|
result.unit_price = Money.new(unit_price, unit_price_currency)
|
267
303
|
|
304
|
+
digital_content_element = element.elements['digital-content']
|
305
|
+
if not digital_content_element.nil?
|
306
|
+
result.create_digital_content(DigitalContent.create_from_element(digital_content_element))
|
307
|
+
end
|
308
|
+
|
268
309
|
return result
|
269
310
|
end
|
311
|
+
|
312
|
+
# A DigitalContent item represents the information relating to online delivery of digital items
|
313
|
+
#
|
314
|
+
# You should never initialize it directly but use Item#digital_content instead
|
315
|
+
#
|
316
|
+
# See http://code.google.com/apis/checkout/developer/Google_Checkout_Digital_Delivery.html
|
317
|
+
# for information on Google Checkout's idea of digital content.
|
318
|
+
#
|
319
|
+
# item.digital_content do |dc|
|
320
|
+
# dc.optimistic!
|
321
|
+
# dc.description = %{Here's some information on how to get your content}
|
322
|
+
# end
|
323
|
+
class DigitalContent
|
324
|
+
|
325
|
+
# Constants for display-disposition
|
326
|
+
OPTIMISTIC = 'OPTIMISTIC'
|
327
|
+
PESSIMISTIC = 'PESSIMISTIC'
|
328
|
+
|
329
|
+
# A description of how the user should access the digital content
|
330
|
+
# after completing the order (string, required for description-based
|
331
|
+
# delivery, otherwise optional)
|
332
|
+
attr_accessor :description
|
333
|
+
|
334
|
+
# Either 'OPTIMISTIC' or 'PESSIMISTIC'. If OPTIMISTIC, then Google
|
335
|
+
# will display instructions for accessing the digital content as soon
|
336
|
+
#as the buyer confirms the order. Optional, but default is PESSIMISTIC
|
337
|
+
attr_reader :display_disposition
|
338
|
+
|
339
|
+
def display_disposition=(disposition)
|
340
|
+
raise "display_disposition can only be set to PESSIMISTIC or OPTIMISTIC" unless disposition == OPTIMISTIC || disposition == PESSIMISTIC
|
341
|
+
@display_disposition = disposition
|
342
|
+
end
|
343
|
+
|
344
|
+
# A boolean identifying whether email delivery is used for this item.
|
345
|
+
attr_accessor :email_delivery
|
346
|
+
|
347
|
+
# A key required by the user to access this digital content after completing the order (string, optional)
|
348
|
+
attr_accessor :key
|
349
|
+
|
350
|
+
# A URL required by the user to access this digital content after completing the order (string, optional)
|
351
|
+
attr_accessor :url
|
352
|
+
|
353
|
+
def initialize
|
354
|
+
@display_disposition = PESSIMISTIC
|
355
|
+
end
|
356
|
+
|
357
|
+
# Creates a new DigitalContent object from a REXML::Element object
|
358
|
+
def self.create_from_element(element)
|
359
|
+
result = DigitalContent.new
|
360
|
+
result.description = element.elements['description'].text rescue nil
|
361
|
+
result.display_disposition = element.elements['display-disposition'].text rescue nil
|
362
|
+
result.email_delivery = element.elements['email-delivery'].text rescue nil # TODO need to convert to boolean?
|
363
|
+
result.key = element.elements['key'].text rescue nil
|
364
|
+
result.url = element.elements['url'].text rescue nil
|
365
|
+
return result
|
366
|
+
end
|
367
|
+
end
|
270
368
|
end
|
271
369
|
|
272
370
|
# A TaxTable is an ordered array of TaxRule objects. You should create the TaxRule
|
@@ -391,8 +489,7 @@ module Google4R #:nodoc:
|
|
391
489
|
#
|
392
490
|
# country_code should be a two-letter ISO 3166 country code
|
393
491
|
# postal_code_pattern should be a full or partial postcode string, using * as a wildcard
|
394
|
-
def initialize(country_code, postal_code_pattern=nil)
|
395
|
-
|
492
|
+
def initialize(country_code=nil, postal_code_pattern=nil)
|
396
493
|
@country_code = country_code
|
397
494
|
@postal_code_pattern = postal_code_pattern
|
398
495
|
end
|
@@ -405,7 +502,7 @@ module Google4R #:nodoc:
|
|
405
502
|
|
406
503
|
# You can optionally initialize the Area with its value.
|
407
504
|
def initialize(state=nil)
|
408
|
-
|
505
|
+
@state = state unless state.nil?
|
409
506
|
end
|
410
507
|
|
411
508
|
# Writer for the state attribute. value must match /^[A-Z]{2,2}$/.
|
@@ -515,8 +612,8 @@ module Google4R #:nodoc:
|
|
515
612
|
return area
|
516
613
|
end
|
517
614
|
|
518
|
-
# Creates a new Area, adds it to the internal list of allowed areas for
|
519
|
-
#
|
615
|
+
# Creates a new Area, adds it to the internal list of allowed areas for shipping
|
616
|
+
# restrictions. If you passed a block (preferred) then the block is called
|
520
617
|
# with the Area as the only parameter.
|
521
618
|
#
|
522
619
|
# The area to be created depends on the given parameter clazz. It can be one
|
@@ -551,7 +648,10 @@ module Google4R #:nodoc:
|
|
551
648
|
# end
|
552
649
|
def create_excluded_area(clazz, &block)
|
553
650
|
return create_area(:shipping_restrictions, :excluded_areas, clazz, &block)
|
554
|
-
end
|
651
|
+
end
|
652
|
+
|
653
|
+
alias :create_shipping_restrictions_allowed_area :create_allowed_area
|
654
|
+
alias :create_shipping_restrictions_excluded_area :create_excluded_area
|
555
655
|
end
|
556
656
|
|
557
657
|
# A class that represents the "pickup" shipping method.
|
@@ -584,6 +684,246 @@ module Google4R #:nodoc:
|
|
584
684
|
@address_filters_excluded_areas = Array.new
|
585
685
|
end
|
586
686
|
|
687
|
+
# Creates a new Area, adds it to the internal list of allowed areas for
|
688
|
+
# address filters. If you passed a block (preferred) then the block is
|
689
|
+
# called with the Area as the only parameter.
|
690
|
+
#
|
691
|
+
# The area to be created depends on the given parameter clazz. It can be one
|
692
|
+
# of { PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea }.
|
693
|
+
#
|
694
|
+
# Raises a RuntimeError if the parameter clazz is invalid.
|
695
|
+
#
|
696
|
+
# === Example
|
697
|
+
#
|
698
|
+
# method = FlatRateShipping.new
|
699
|
+
# method.create_allowed_area(UsCountryArea) do |area|
|
700
|
+
# area.area = UsCountryArea::ALL
|
701
|
+
# end
|
702
|
+
def create_address_filters_allowed_area(clazz, &block)
|
703
|
+
return create_area(:address_filters, :allowed_areas, clazz, &block)
|
704
|
+
end
|
705
|
+
|
706
|
+
# Creates a new Area, adds it to the internal list of excluded areas for
|
707
|
+
# address filters. If you passed a block (preferred) then the block is
|
708
|
+
# called with the Area as the only parameter.
|
709
|
+
#
|
710
|
+
# The area to be created depends on the given parameter clazz. It can be one
|
711
|
+
# of { PostalArea, UsCountryArea, UsStateArea, UsZipArea, WorldArea }.
|
712
|
+
#
|
713
|
+
# Raises a RuntimeError if the parameter clazz is invalid.
|
714
|
+
#
|
715
|
+
# === Example
|
716
|
+
#
|
717
|
+
# method = FlatRateShipping.new
|
718
|
+
# method.create_allowed_area(UsCountryArea) do |area|
|
719
|
+
# area.area = UsCountryArea::ALL
|
720
|
+
# end
|
721
|
+
def create_address_filters_allowed_area(clazz, &block)
|
722
|
+
return create_area(:address_filters, :allowed_areas, clazz, &block)
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
# A class that represents the "merchant-calculated" shipping method
|
727
|
+
class CarrierCalculatedShipping
|
728
|
+
# This encapsulates information about all of the shipping methods
|
729
|
+
# for which Google Checkout should obtain shipping costs.
|
730
|
+
attr_reader :carrier_calculated_shipping_options
|
731
|
+
|
732
|
+
# This encapsulates information about all of the packages that will be
|
733
|
+
# shipped to the buyer. At this time, merchants may only specify
|
734
|
+
# one package per order.
|
735
|
+
attr_reader :shipping_packages
|
736
|
+
|
737
|
+
def initialize()
|
738
|
+
@carrier_calculated_shipping_options = Array.new
|
739
|
+
@shipping_packages = Array.new
|
740
|
+
end
|
741
|
+
|
742
|
+
def create_carrier_calculated_shipping_option(&block)
|
743
|
+
option = CarrierCalculatedShippingOption.new(self)
|
744
|
+
@carrier_calculated_shipping_options << option
|
745
|
+
|
746
|
+
# Pass the newly generated rule to the given block to set its attributes.
|
747
|
+
yield(option) if block_given?
|
748
|
+
|
749
|
+
return option
|
750
|
+
end
|
751
|
+
|
752
|
+
def create_shipping_package(&block)
|
753
|
+
package = ShippingPackage.new(self)
|
754
|
+
@shipping_packages << package
|
755
|
+
|
756
|
+
# Pass the newly generated rule to the given block to set its attributes.
|
757
|
+
yield(package) if block_given?
|
758
|
+
|
759
|
+
return package
|
760
|
+
end
|
761
|
+
|
762
|
+
# Creates a new CarrierCalculatedShipping from the given
|
763
|
+
# REXML::Element instance.
|
764
|
+
# For testing only.
|
765
|
+
def create_from_element(element)
|
766
|
+
result = CarrierCalculatedShipping.new
|
767
|
+
element.elements.each('carrier-calculated-shipping-options/carrier-calculated-shipping-option') do |shipping_option_element|
|
768
|
+
result.carrier_calculated_shipping_options << CarrierCalculatedShippingOption.create_from_element(self, shipping_option_element)
|
769
|
+
end
|
770
|
+
element.elements.each('shipping-packages/shipping-package') do |shipping_package_element|
|
771
|
+
result.shipping_packages << ShippingPackage.create_from_element(self, shipping_package_element)
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
class CarrierCalculatedShippingOption < DeliveryMethod
|
776
|
+
# Constants for shipping company
|
777
|
+
FEDEX = 'FedEx'
|
778
|
+
UPS = 'UPS'
|
779
|
+
USPS = 'USPS'
|
780
|
+
|
781
|
+
# Constants for carrier pickup
|
782
|
+
DROP_OFF = 'DROP_OFF'
|
783
|
+
REGULAR_PICKUP = 'REGULAR_PICKUP'
|
784
|
+
SPECIAL_PICKUP = 'SPECIAL_PICKUP'
|
785
|
+
|
786
|
+
# The CarrierCalculatedShipping instance that this option belongs to.
|
787
|
+
attr_reader :carrier_calculated_shipping
|
788
|
+
|
789
|
+
# The name of the company that will ship the order.
|
790
|
+
# The only valid values for this tag are FedEx, UPS and USPS.
|
791
|
+
# (String, required)
|
792
|
+
alias :shipping_company :name
|
793
|
+
alias :shipping_company= :name=
|
794
|
+
|
795
|
+
# The shipping option that is being offered to the buyer
|
796
|
+
attr_accessor :shipping_type
|
797
|
+
|
798
|
+
# This specifies how the package will be transferred from the merchant
|
799
|
+
# to the shipper. Valid values for this tag are REGULAR_PICKUP,
|
800
|
+
# SPECIAL_PICKUP and DROP_OFF. The default value for this tag is DROP_OFF.
|
801
|
+
# (optional)
|
802
|
+
attr_accessor :carrier_pickup
|
803
|
+
|
804
|
+
# The fixed charge that will be added to the total cost of an order
|
805
|
+
# if the buyer selects the associated shipping option
|
806
|
+
# (Money, optional)
|
807
|
+
attr_accessor :additional_fixed_charge
|
808
|
+
|
809
|
+
# The percentage amount by which a carrier-calculated shipping rate
|
810
|
+
# will be adjusted. The tag's value may be positive or negative.
|
811
|
+
# (Float, optional)
|
812
|
+
attr_accessor :additional_variable_charge_percent
|
813
|
+
|
814
|
+
def initialize(carrier_calculated_shipping)
|
815
|
+
@carrier_calculated_shipping = carrier_calculated_shipping
|
816
|
+
#@carrier_pickup = DROP_OFF
|
817
|
+
end
|
818
|
+
|
819
|
+
# Creates a new CarrierCalculatedShippingOption from the given
|
820
|
+
# REXML::Element instance.
|
821
|
+
# For testing only.
|
822
|
+
def self.create_from_element(this_shipping, element)
|
823
|
+
result = CarrierCalculatedShippingOption.new(this_shipping)
|
824
|
+
result.shipping_company = element.elements['shipping-company'].text
|
825
|
+
price = (element.elements['price'].text.to_f * 100).to_i
|
826
|
+
price_currency = element.elements['price'].attributes['currency']
|
827
|
+
result.price = Money.new(price, price_currency)
|
828
|
+
result.shipping_type = element.elements['shipping-type']
|
829
|
+
result.carrier_pickup = element.elements['carrier-pickup'] rescue nil
|
830
|
+
result.additional_fixed_charge =
|
831
|
+
element.elements['additional-fixed-charge'] rescue nil
|
832
|
+
result.additional_variable_charge_percent =
|
833
|
+
element.elements['additional-variable-charge-percent'] rescue nil
|
834
|
+
end
|
835
|
+
end
|
836
|
+
|
837
|
+
class ShippingPackage
|
838
|
+
# Constants for delivery address category
|
839
|
+
RESIDENTIAL = 'RESIDENTIAL'
|
840
|
+
COMMERCIAL = 'COMMERCIAL'
|
841
|
+
|
842
|
+
# The CarrierCalculatedShipping instance that this package belongs to.
|
843
|
+
attr_reader :carrier_calculated_shipping
|
844
|
+
|
845
|
+
# This contains information about the location from which an order
|
846
|
+
# will be shipped. (AnonymousAddress)
|
847
|
+
attr_accessor :ship_from
|
848
|
+
|
849
|
+
# This indicates whether the shipping method should be applied to
|
850
|
+
# a residential or a commercial address. Valid values for this tag
|
851
|
+
# are RESIDENTIAL and COMMERCIAL. (String, optional)
|
852
|
+
attr_accessor :delivery_address_category
|
853
|
+
|
854
|
+
# This contains information about the height of the package being
|
855
|
+
# shipped to the customer. (Google::Checktou::Dimension, optional)
|
856
|
+
attr_accessor :height
|
857
|
+
|
858
|
+
# This contains information about the length of the package being
|
859
|
+
# shipped to the customer. (Google::Checktou::Dimension, optional)
|
860
|
+
attr_accessor :length
|
861
|
+
|
862
|
+
# This contains information about the width of the package being
|
863
|
+
# shipped to the customer. (Google::Checktou::Dimension, optional)
|
864
|
+
attr_accessor :width
|
865
|
+
|
866
|
+
def initialize(carrier_calculated_shipping)
|
867
|
+
@carrier_calculated_shipping = carrier_calculated_shipping
|
868
|
+
end
|
869
|
+
|
870
|
+
# Creates a new ShippingPackage from the given REXML::Element instance.
|
871
|
+
# For testing only.
|
872
|
+
def self.create_from_element(this_shipping, element)
|
873
|
+
result = ShippingPackage.new(this_shipping)
|
874
|
+
result.ship_from = ShipFromAddress.create_from_element(element.elements['ship-from'])
|
875
|
+
result.delivery_address_category = element.elements['delivery-address-category'].text rescue nil
|
876
|
+
result.height = element.elements['height'].text rescue nil
|
877
|
+
result.length = element.elements['length'].text rescue nil
|
878
|
+
result.width = element.elements['width'].text rescue nil
|
879
|
+
return result
|
880
|
+
end
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
884
|
+
# This is a base class for defining the unit of weight and dimension
|
885
|
+
class Unit
|
886
|
+
# This specifies the unit of measurement that corresponds to a shipping
|
887
|
+
# package's length, width or height. The only valid value for
|
888
|
+
# this attribute is IN.
|
889
|
+
attr_accessor :unit
|
890
|
+
|
891
|
+
# This specifies the numeric value of a unit of measurement
|
892
|
+
# corresponding to an item or a shipping package. (float)
|
893
|
+
attr_accessor :value
|
894
|
+
|
895
|
+
def initialize
|
896
|
+
raise "Google::Checkout::Unit is an abstract class!"
|
897
|
+
end
|
898
|
+
|
899
|
+
# Creates a new Unit from the given REXML::Element instance.
|
900
|
+
def self.create_from_element(element)
|
901
|
+
result = self.new(element.attributes['value'].to_f)
|
902
|
+
return result
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
# This defines package dimension
|
907
|
+
class Dimension < Unit
|
908
|
+
|
909
|
+
# Constants for unit
|
910
|
+
INCH = 'IN'
|
911
|
+
|
912
|
+
def initialize(value, unit=INCH)
|
913
|
+
@unit = unit
|
914
|
+
@value = value.to_f
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
# This defines item weight
|
919
|
+
class Weight < Unit
|
920
|
+
# Constants for unit
|
921
|
+
LB = 'LB'
|
922
|
+
|
923
|
+
def initialize(value, unit=LB)
|
924
|
+
@unit = unit
|
925
|
+
@value = value.to_f
|
926
|
+
end
|
587
927
|
end
|
588
928
|
|
589
929
|
# This address is used in merchant calculation callback
|
@@ -677,5 +1017,40 @@ module Google4R #:nodoc:
|
|
677
1017
|
return result
|
678
1018
|
end
|
679
1019
|
end
|
1020
|
+
|
1021
|
+
# ItemInfo instances are used in Line-item shipping commands
|
1022
|
+
class ItemInfo
|
1023
|
+
# The merchant item id (String)
|
1024
|
+
attr_reader :merchant_item_id
|
1025
|
+
|
1026
|
+
# An array of tracking data for this item
|
1027
|
+
attr_reader :tracking_data_arr
|
1028
|
+
|
1029
|
+
def initialize(merchant_item_id)
|
1030
|
+
@merchant_item_id = merchant_item_id
|
1031
|
+
@tracking_data_arr = Array.new
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def create_tracking_data(carrier, tracking_number)
|
1035
|
+
tracking_data = TrackingData.new(carrier, tracking_number)
|
1036
|
+
@tracking_data_arr << tracking_data
|
1037
|
+
return tracking_data
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
# TrackingData instances are used in Line-item shipping commands
|
1042
|
+
class TrackingData
|
1043
|
+
# The name of the company responsible for shipping the item. Valid values
|
1044
|
+
# for this tag are DHL, FedEx, UPS, USPS and Other.
|
1045
|
+
attr_reader :carrier
|
1046
|
+
|
1047
|
+
# The shipper's tracking number that is associated with an order
|
1048
|
+
attr_reader :tracking_number
|
1049
|
+
|
1050
|
+
def initialize(carrier, tracking_number)
|
1051
|
+
@carrier = carrier.to_s
|
1052
|
+
@tracking_number = tracking_number.to_s
|
1053
|
+
end
|
1054
|
+
end
|
680
1055
|
end
|
681
1056
|
end
|