google4r-checkout 1.0.3 → 1.0.6

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.
data/CHANGES CHANGED
@@ -1,5 +1,20 @@
1
1
  =google4r-checkout Changelog
2
2
 
3
+ == 1.0.6 (2010-04-01)
4
+
5
+ * Added beta Subscriptions API support.
6
+ * Fixed email-delivery XML generation bug.
7
+
8
+ == 1.0.5 (2008-9-17)
9
+
10
+ * Added Order Report API support. Thanks to Daniel Pirone!
11
+ * Added unit tests for OrderReportCommand. Added validation checks fro start_date, end_date, financial_state and fulfillment_state.
12
+
13
+ == 1.0.4 (2008-09-02)
14
+
15
+ * Fixed undefined email_delivery? method bug
16
+ * Added unit test to cover digital content creation
17
+
3
18
  == 1.0.3 (2008-08-08)
4
19
 
5
20
  * Added analytics_data support. Thanks to Will Schwenk!
@@ -49,6 +49,9 @@ module Google4R #:nodoc:
49
49
  CHECKOUT_API_URL = 'api/checkout/v2/merchantCheckout/Merchant/%s'
50
50
 
51
51
  ORDER_PROCESSING_API_URL = 'api/checkout/v2/request/Merchant/%s'
52
+
53
+ ORDER_REPORT_API_URL = 'api/checkout/v2/reports/Merchant/%s'
54
+
52
55
 
53
56
  # The Frontent class that was used to create this CheckoutCommand and whose
54
57
  # configuration will be used.
@@ -79,6 +82,7 @@ module Google4R #:nodoc:
79
82
  # TODO: The send-and-expect-response part should be adaptable to other commands and responses.
80
83
  #++
81
84
  def send_to_google_checkout
85
+ xml_response = (self.class == OrderReportCommand) ? false : true
82
86
  # Create HTTP(S) POST command and set up Basic Authentication.
83
87
  url_str =
84
88
  if frontend.configuration[:use_sandbox] then
@@ -89,6 +93,8 @@ module Google4R #:nodoc:
89
93
  url_str +=
90
94
  if self.class == CheckoutCommand then
91
95
  CHECKOUT_API_URL
96
+ elsif self.class == OrderReportCommand then
97
+ ORDER_REPORT_API_URL
92
98
  else
93
99
  ORDER_PROCESSING_API_URL
94
100
  end
@@ -110,18 +116,23 @@ module Google4R #:nodoc:
110
116
 
111
117
  case result
112
118
  when Net::HTTPSuccess then
113
- xml_doc = REXML::Document.new(result.body)
114
-
115
- case xml_doc.root.name
116
- when 'checkout-redirect'
117
- serial_number = xml_doc.elements['/checkout-redirect'].attributes['serial-number']
118
- redirect_url = xml_doc.elements['/checkout-redirect/redirect-url/text()'].value
119
- return CheckoutRedirectResponse.new(serial_number, redirect_url)
120
- when 'request-received'
121
- serial_number = xml_doc.elements['/request-received'].attributes['serial-number']
122
- return serial_number
119
+ if ( xml_response ) then
120
+ xml_doc = REXML::Document.new(result.body)
121
+
122
+ case xml_doc.root.name
123
+ when 'checkout-redirect'
124
+ serial_number = xml_doc.elements['/checkout-redirect'].attributes['serial-number']
125
+ redirect_url = xml_doc.elements['/checkout-redirect/redirect-url/text()'].value
126
+ return CheckoutRedirectResponse.new(serial_number, redirect_url)
127
+ when 'request-received'
128
+ serial_number = xml_doc.elements['/request-received'].attributes['serial-number']
129
+ return serial_number
130
+ else
131
+ raise "Unknown response:\n--\n#{xml_doc.to_s}\n--"
132
+ end
123
133
  else
124
- raise "Unknown response:\n--\n#{xml_doc.to_s}\n--"
134
+ # handle the CSV output of the order-report-list command
135
+ return result.body
125
136
  end
126
137
  when Net::HTTPClientError then
127
138
  xml_doc = REXML::Document.new(result.body)
@@ -296,6 +307,28 @@ module Google4R #:nodoc:
296
307
  return @redirect_url
297
308
  end
298
309
  end
310
+
311
+ # SubscriptionRequestReceivedResponse instances are returned when a
312
+ # CreateOrderRecurrenceRequestCommand is successfully processed by Google Checkout.
313
+ class SubscriptionRequestReceivedResponse
314
+ # The serial number of the <subscription-request-received> response.
315
+ attr_reader :serial_number
316
+
317
+ # The new order number that was generated for this request.
318
+ attr_reader :new_google_order_number
319
+
320
+ # Create a new SubscriptionRequestReceivedResponse with the given serial number and Google
321
+ # order number. Do not create SubscriptionRequestReceivedResponse instances in your own
322
+ # code. Google4R creates them for you.
323
+ def initialize(serial_number, new_google_order_number)
324
+ @serial_number = serial_number
325
+ @new_google_order_number = new_google_order_number
326
+ end
327
+
328
+ def to_s
329
+ return @new_google_order_number
330
+ end
331
+ end
299
332
 
300
333
  #
301
334
  # The ChargeOrderCommand instructs Google Checkout to charge the buyer for a
@@ -426,6 +459,24 @@ module Google4R #:nodoc:
426
459
  end
427
460
  end
428
461
 
462
+ # The <create-order-recurrence-request> tag contains a request to charge a customer for one or more items in a subscription.
463
+ class CreateOrderRecurrenceRequestCommand < Command
464
+ # The ID that uniquely identifies this order
465
+ attr_accessor :google_order_number
466
+
467
+ attr_reader :shopping_cart
468
+
469
+ # Initialize a new CreateOrderRecurrenceRequestCommand with a fresh ShoppingCart.
470
+ def initialize(frontend)
471
+ super(frontend)
472
+ @shopping_cart = ShoppingCart.new(self)
473
+ end
474
+
475
+ def to_xml
476
+ CreateOrderRecurrenceRequestCommandXmlGenerator.new(self).generate
477
+ end
478
+ end
479
+
429
480
  #
430
481
  # XML API Commands for Line-item Shipping
431
482
  #
@@ -465,8 +516,7 @@ module Google4R #:nodoc:
465
516
  # The <cancel-items> command lets you specify that one or more
466
517
  # specific items in an order have been cancelled, meaning they
467
518
  # will not be delivered to the customer.
468
- class CancelItemsCommand < ItemsCommand
469
-
519
+ class CancelItemsCommand < ItemsCommand
470
520
  # The reason that you are canceling one or more line items
471
521
  attr_accessor :reason
472
522
 
@@ -493,5 +543,65 @@ module Google4R #:nodoc:
493
543
  ResetItemsShippingInformationCommandXmlGenerator.new(self).generate
494
544
  end
495
545
  end
546
+
547
+ # The <order-list-request> command lets you to download a list of
548
+ # Google Checkout orders into a comma-separated file.
549
+ # The API will return a list of orders for a period of up to 31 days,
550
+ # and you can limit results to orders that have specific financial or
551
+ # fulfillment order states.
552
+ # http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Order_Report_API.html
553
+ class OrderReportCommand < Command
554
+ # The earliest time that an order could have been submitted to be
555
+ # included in the API response (Time)
556
+ attr_reader :start_date
557
+
558
+ # The time before which an order must have been sent to be included
559
+ # in the API response (Time)
560
+ attr_reader :end_date
561
+
562
+ # The financial status of an order
563
+ attr_accessor :financial_state
564
+
565
+ # The fulfillment status of an order
566
+ attr_accessor :fulfillment_state
567
+
568
+ # The time zone that will be associated with the start date and
569
+ # end date for the report
570
+ attr_accessor :date_time_zone
571
+
572
+ def initialize(frontend, start_date, end_date)
573
+ super frontend
574
+ raise 'start_date has to be of type Time' unless start_date.class == Time
575
+ raise 'end_date has to be of type Time' unless start_date.class == Time
576
+ raise 'end_date has to be before start_date' unless
577
+ end_date >= start_date
578
+ @start_date = start_date
579
+ @end_date = end_date
580
+ end
581
+
582
+ def start_date
583
+ return @start_date.strftime('%Y-%m-%dT%H:%M:%S')
584
+ end
585
+
586
+ def end_date
587
+ return @end_date.strftime('%Y-%m-%dT%H:%M:%S')
588
+ end
589
+
590
+ def financial_state=(financial_state)
591
+ raise 'Invalid financial state %s' % financial_state unless
592
+ FinancialState.constants.include?(financial_state)
593
+ @financial_state = financial_state
594
+ end
595
+
596
+ def fulfillment_state=(fulfillment_state)
597
+ raise 'Invalid fulfillment state %s' % fulfillment_state unless
598
+ FulfillmentState.constants.include?(fulfillment_state)
599
+ @fulfillment_state = fulfillment_state
600
+ end
601
+
602
+ def to_xml
603
+ ReturnOrderReportCommandXmlGenerator.new(self).generate
604
+ end
605
+ end
496
606
  end
497
607
  end
@@ -170,6 +170,12 @@ module Google4R #:nodoc:
170
170
  return UnarchiveOrderCommand.new(self)
171
171
  end
172
172
 
173
+ # Factory method to create a new CreateOrderRecurrenceRequestCommand object. Use this method to create
174
+ # your CreateOrderRecurrenceRequestCommand instances.
175
+ def create_create_order_recurrence_request_command
176
+ return CreateOrderRecurrenceRequestCommand.new(self)
177
+ end
178
+
173
179
  # Factory method to create a new ShipItemsCommand object. Use this method to create
174
180
  # your ShipItemsCommand instances.
175
181
  def create_ship_items_command
@@ -199,6 +205,12 @@ module Google4R #:nodoc:
199
205
  def create_reset_items_shipping_information_command
200
206
  return ResetItemsShippingInformationCommand.new(self)
201
207
  end
208
+
209
+ # Factory method that creates a new OrderReportCommand object. Use this method to create
210
+ # your OrderReportCommand instances.
211
+ def create_order_report_command(start_date, end_date)
212
+ return OrderReportCommand.new(self, start_date, end_date)
213
+ end
202
214
  end
203
215
  end
204
216
  end
File without changes
@@ -132,6 +132,8 @@ module Google4R #:nodoc:
132
132
  ChargebackAmountNotification.create_from_element(root, frontend)
133
133
  when 'authorization-amount-notification' then
134
134
  AuthorizationAmountNotification.create_from_element(root, frontend)
135
+ when 'cancelled-subscription-notification' then
136
+ CancelledSubscriptionNotification.create_from_element(root, frontend)
135
137
  else
136
138
  raise UnknownNotificationType, "Unknown notification type: #{root.name}"
137
139
  end
@@ -487,6 +489,33 @@ module Google4R #:nodoc:
487
489
  end
488
490
  end
489
491
 
492
+ # CancelledSubscriptionNotification objects contain information about the
493
+ # cancellation of a subscription, including the item-ids, google order number,
494
+ # reason, and timestamp.
495
+ class CancelledSubscriptionNotification < Notification
496
+
497
+ # The reason for the cancellation (String, can be nil.)
498
+ attr_accessor :reason
499
+
500
+ # The ids of the items being cancelled (Array of Strings)
501
+ attr_accessor :item_ids
502
+
503
+ # The google order number of the subscription being cancelled
504
+ attr_accessor :google_order_number
505
+
506
+ def self.create_from_element(element, frontend)
507
+ csn = CancelledSubscriptionNotification.new(frontend)
508
+
509
+ csn.serial_number = element.attributes['serial-number']
510
+ csn.timestamp = Time.parse(element.elements['timestamp'].text)
511
+ csn.reason = element.elements['reason'].text rescue nil
512
+ csn.item_ids = element.elements['item-ids/item-id/merchant-item-id'].collect {|i| i.text} rescue []
513
+ csn.google_order_number = element.elements['google-order-number'].text
514
+
515
+ return csn
516
+ end
517
+ end
518
+
490
519
  # Container for the valid financial order states as defined in the
491
520
  # Google Checkout API.
492
521
  module FinancialOrderState
@@ -266,6 +266,26 @@ module Google4R #:nodoc:
266
266
  return @digital_content
267
267
  end
268
268
 
269
+ # Subscription information for this item. Optional.
270
+ attr_reader :subscription
271
+
272
+ def create_subscription(subscription=nil, &block)
273
+
274
+ if @subscription.nil?
275
+ if subscription.nil?
276
+ @subscription = Subscription.new
277
+ else
278
+ @subscription = subscription
279
+ end
280
+ end
281
+
282
+ if block_given?
283
+ yield @subscription
284
+ end
285
+
286
+ return @subscription
287
+ end
288
+
269
289
  # Create a new Item in the given Cart. You should not instantize this class directly
270
290
  # but use Cart#create_item instead.
271
291
  def initialize(shopping_cart)
@@ -365,6 +385,179 @@ module Google4R #:nodoc:
365
385
  return result
366
386
  end
367
387
  end
388
+
389
+ class Subscription
390
+
391
+ # Constants for period
392
+ DAILY = 'DAILY'
393
+ WEEKLY = 'WEEKLY'
394
+ SEMI_MONTHLY = 'SEMI_MONTHLY'
395
+ MONTHLY = 'MONTHLY'
396
+ EVERY_TWO_MONTHS = 'EVERY_TWO_MONTHS'
397
+ QUARTERLY = 'QUARTERLY'
398
+ YEARLY = 'YEARLY'
399
+
400
+ # Constants for type
401
+ MERCHANT = 'merchant'
402
+ GOOGLE = 'google'
403
+
404
+ # Optional. The no-charge-after attribute specifies the latest date and time that
405
+ # you can charge the customer for the subscription. This element can help you to
406
+ # ensure that you do not overcharge your customers.
407
+ attr_accessor :no_charge_after
408
+
409
+ # Required. The period attribute specifies how frequently you will charge the
410
+ # customer for the subscription. Valid values for this attribute are DAILY,
411
+ # WEEKLY, SEMI_MONTHLY, MONTHLY, EVERY_TWO_MONTHS, QUARTERLY, and YEARLY.
412
+ attr_reader :period
413
+
414
+ def period=(period)
415
+ unless [DAILY, WEEKLY, SEMI_MONTHLY, MONTHLY, EVERY_TWO_MONTHS, QUARTERLY, YEARLY].include?(period)
416
+ raise "period can only be set to DAILY, WEEKLY, SEMI_MONTHLY, MONTHLY, EVERY_TWO_MONTHS, QUARTERLY, or YEARLY"
417
+ end
418
+ @period = period
419
+ end
420
+
421
+ # Optional. The start-date attribute specifies the date that the subscription's
422
+ # recurrence period will begin. Like all dates in Checkout, this is in ISO 8601
423
+ # format. If you set the <unit-price> tag's value to a nonzero value, then the
424
+ # start-date for the subscription will automatically be set to the time that is
425
+ # exactly one recurrence period after the order is placed.
426
+ attr_accessor :start_date
427
+
428
+ # Required. The type attribute identifies the type of subscription that you are
429
+ # creating. The valid values for this attribute are merchant and google, and this
430
+ # specifies who handles the recurrences. The merchant value specifies
431
+ # Merchant-Handled recurrences, and the google value specifies Google-Handled
432
+ # recurrences.
433
+ attr_reader :type
434
+
435
+ def type=(type)
436
+ unless [MERCHANT, GOOGLE].include?(type)
437
+ raise "type can only be set to MERCHANT or GOOGLE"
438
+ end
439
+ @type = type
440
+ end
441
+
442
+ # Container for payments
443
+ attr_reader :payments
444
+
445
+ def add_payment(&block)
446
+ payment = SubscriptionPayment.new(self)
447
+ @payments << payment
448
+
449
+ # Pass the newly generated payment to the given block to set its attributes.
450
+ yield(payment) if block_given?
451
+
452
+ return payment
453
+ end
454
+
455
+ # Container for recurrent items
456
+ attr_reader :recurrent_items
457
+
458
+ def add_recurrent_item(&block)
459
+ item = RecurrentItem.new(self)
460
+ @recurrent_items << item
461
+
462
+ # Pass the newly generated item to the given block to set its attributes.
463
+ yield(item) if block_given?
464
+
465
+ return item
466
+ end
467
+
468
+ def initialize
469
+ @payments = []
470
+ @recurrent_items = []
471
+ end
472
+
473
+ def self.create_from_element(element)
474
+ result = Subscription.new
475
+ result.no_charge_after = Time.iso8601(element.attributes['no-charge-after']) rescue nil
476
+ result.period = element.attributes['period'] rescue nil
477
+ result.start_date = Time.iso8601(element.attributes['start-date']) rescue nil
478
+ result.type = element.attributes['type'] rescue nil
479
+
480
+ element.elements['payments/subscription-payment'].each do |payment_element|
481
+ result.payments << SubscriptionPayment.create_from_element(subscription, payment_element)
482
+ end
483
+
484
+ element.elements['recurrent-item'].each do |item_element|
485
+ result.recurrent_items << Item.create_from_element(item_element)
486
+ end
487
+
488
+ return result
489
+ end
490
+
491
+ class SubscriptionPayment
492
+
493
+ attr_accessor :subscription
494
+
495
+ # Optional. The times attribute indicates how many times you will charge the
496
+ # customer for a defined subscription payment. A subscription may have multiple
497
+ # payment schedules, and the times attribute lets you indicate how many times
498
+ # each charge will be assessed. For example, you might charge the customer a
499
+ # reduced rate for the first three months of a subscription and then charge the
500
+ # standard rate each month thereafter.
501
+ attr_accessor :times
502
+
503
+ # The maximum amount that you will be allowed to charge the customer, including
504
+ # tax, for all recurrences (Money instance, required).
505
+ attr_reader :maximum_charge
506
+
507
+ def initialize(subscription)
508
+ @subscription = subscription
509
+ end
510
+
511
+ # Sets the maximum charge for this subscription payment. money must respond to
512
+ # :cents and :currency as the Money class does.
513
+ def maximum_charge=(money)
514
+ if not (money.respond_to?(:cents) and money.respond_to?(:currency)) then
515
+ raise "Invalid price - does not respond to :cents and :currency - #{money.inspect}."
516
+ end
517
+
518
+ @maximum_charge = money
519
+ end
520
+
521
+ def self.create_from_element(subscription, element)
522
+ result = SubscriptionPayment.new
523
+ result.subscription = subscription
524
+ result.times = element.attributes['times'].to_i rescue nil
525
+
526
+ maximum_charge = (element.elements['maximum-charge'].text.to_f * 100).to_i
527
+ maximum_charge_currency = element.elements['maximum-charge'].attributes['currency']
528
+ result.maximum_charge = Money.new(maximum_charge, maximum_charge_currency)
529
+
530
+ return result
531
+ end
532
+ end
533
+
534
+ class RecurrentItem < Item
535
+
536
+ attr_accessor :subscription
537
+
538
+ def initialize(subscription)
539
+ @subscription = subscription
540
+ end
541
+
542
+ def self.create_from_element(element, subscription)
543
+ item = super(element, nil)
544
+
545
+ result = RecurrentItem.new(subscription)
546
+ result.description = item.description
547
+ result.digital_content = item.digital_content
548
+ result.id = item.id
549
+ result.name = item.name
550
+ result.private_data = item.private_data
551
+ result.quantity = item.quantity
552
+ result.tax_table = item.tax_table
553
+ result.unit_price = item.unit_price
554
+ result.weight = item.weight
555
+
556
+ return result
557
+ end
558
+
559
+ end
560
+ end
368
561
  end
369
562
 
370
563
  # A TaxTable is an ordered array of TaxRule objects. You should create the TaxRule
@@ -1052,5 +1245,35 @@ module Google4R #:nodoc:
1052
1245
  @tracking_number = tracking_number.to_s
1053
1246
  end
1054
1247
  end
1248
+
1249
+ # financial_state
1250
+ # REVIEWING - Google Checkout is reviewing the order.
1251
+ # CHARGEABLE - The order is ready to be charged.
1252
+ # CHARGING - The order is being charged; you may not refund or cancel an order until is the charge is completed.
1253
+ # CHARGED - The order has been successfully charged; if the order was only partially charged, the buyer's account page will reflect the partial charge.
1254
+ # PAYMENT_DECLINED - The charge attempt failed.
1255
+ # CANCELLED - The seller canceled the order; an order's financial state cannot be changed after the order is canceled.
1256
+ # CANCELLED_BY_GOOGLE - Google canceled the order. Google may cancel orders due to a failed charge without a replacement credit card being provided within a set period of time or due to a failed risk check. If Google cancels an order, you will be notified of the reason the order was canceled in the <reason> tag of an <order-state-change-notification>.
1257
+ class FinancialState
1258
+ REVIEWING = 'REVIEWING'
1259
+ CHARGEABLE = 'CHARGEABLE'
1260
+ CHARGING = 'CHARGING'
1261
+ CHARGED = 'CHARGED'
1262
+ PAYMENT_DECLINED = 'PAYMENT_DECLINED'
1263
+ CANCELLED = 'CANCELLED'
1264
+ CANCELLED_BY_GOOGLE = 'CANCELLED_BY_GOOGLE'
1265
+ end
1266
+
1267
+ # fulfillment_state
1268
+ # NEW - The order has been received but not prepared for shipping.
1269
+ # PROCESSING - The order is being prepared for shipping.
1270
+ # DELIVERED - The seller has shipped the order.
1271
+ # WILL_NOT_DELIVER - The seller will not ship the order; this status is used for canceled orders.
1272
+ class FulfillmentState
1273
+ NEW = 'NEW'
1274
+ PROCESSING = 'PROCESSING'
1275
+ DELIVERED = 'DELIVERED'
1276
+ WILL_NOT_DELIVER = 'WILL_NOT_DELIVER'
1277
+ end
1055
1278
  end
1056
1279
  end
@@ -30,6 +30,7 @@
30
30
 
31
31
  require 'stringio'
32
32
  require 'rexml/document'
33
+ require 'time'
33
34
 
34
35
  module Google4R #:nodoc:
35
36
  module Checkout #:nodoc:
@@ -67,11 +68,13 @@ module Google4R #:nodoc:
67
68
  SendBuyerMessageCommand => 'send-buyer-message',
68
69
  ArchiveOrderCommand => 'archive-order',
69
70
  UnarchiveOrderCommand => 'unarchive-order',
71
+ CreateOrderRecurrenceRequestCommand => 'create-order-recurrence-request',
70
72
  ShipItemsCommand => 'ship-items',
71
73
  BackorderItemsCommand => 'backorder-items',
72
74
  CancelItemsCommand => 'cancel-items',
73
75
  ReturnItemsCommand => 'return-items',
74
- ResetItemsShippingInformationCommand => 'reset-items-shipping-information'
76
+ ResetItemsShippingInformationCommand => 'reset-items-shipping-information',
77
+ OrderReportCommand => 'order-list-request',
75
78
  }
76
79
 
77
80
  def initialize(command)
@@ -289,6 +292,66 @@ module Google4R #:nodoc:
289
292
  if not item.digital_content.nil? then
290
293
  self.process_digital_content(item_element, item.digital_content)
291
294
  end
295
+
296
+ if not (item.kind_of? Item::Subscription::RecurrentItem or item.subscription.nil?) then
297
+ self.process_subscription(item_element, item.subscription)
298
+ end
299
+ end
300
+
301
+ # Adds a <subscription> element to a parent (<item>) element
302
+ def process_subscription(parent, subscription)
303
+ subscription_element = parent.add_element('subscription')
304
+
305
+ if not subscription.no_charge_after.nil? then
306
+ subscription_element.attributes['no-charge-after'] = subscription.no_charge_after.xmlschema
307
+ end
308
+
309
+ if not subscription.period.nil? then
310
+ subscription_element.attributes['period'] = subscription.period.to_s
311
+ end
312
+
313
+ if not subscription.start_date.nil? then
314
+ subscription_element.attributes['start-date'] = subscription.start_date.xmlschema
315
+ end
316
+
317
+ if not subscription.type.nil? then
318
+ subscription_element.attributes['type'] = subscription.type.to_s
319
+ end
320
+
321
+ if subscription.payments.length > 0
322
+ payments_element = subscription_element.add_element('payments')
323
+
324
+ subscription.payments.each do |payment|
325
+ self.process_subscription_payment(payments_element, payment)
326
+ end
327
+ end
328
+
329
+ if subscription.recurrent_items.length > 0
330
+ # this is a little bit of a hack; we use the normal way of generating items
331
+ # for a shopping cart, and then rename the elements to 'recurrent-item'
332
+ # after the fact
333
+
334
+ subscription.recurrent_items.each do |item|
335
+ self.process_item(subscription_element, item)
336
+ end
337
+
338
+ subscription_element.elements.each('item') do |item_element|
339
+ item_element.name = 'recurrent-item'
340
+ end
341
+ end
342
+ end
343
+
344
+ # Adds a <subcription-payment> element to a parent (<payments>) element
345
+ def process_subscription_payment(parent, payment)
346
+ payment_element = parent.add_element('subscription-payment')
347
+
348
+ if not payment.times.nil? then
349
+ payment_element.attributes['times'] = payment.times.to_s
350
+ end
351
+
352
+ if not payment.maximum_charge.nil? then
353
+ payment_element.add_element('maximum-charge', { 'currency' => payment.maximum_charge.currency }).text = payment.maximum_charge.to_s
354
+ end
292
355
  end
293
356
 
294
357
  # Adds a <digital-content> element to a parent (<item>) element
@@ -299,7 +362,7 @@ module Google4R #:nodoc:
299
362
  digital_content_element.add_element('description').text = digital_content.description.to_s
300
363
  end
301
364
 
302
- if digital_content.email_delivery? then
365
+ if not digital_content.email_delivery.nil? then
303
366
  digital_content_element.add_element('email-delivery').text = digital_content.email_delivery.to_s
304
367
  end
305
368
 
@@ -432,17 +495,17 @@ module Google4R #:nodoc:
432
495
  if not package.height.nil?
433
496
  height_element = element.add_element('height')
434
497
  height_element.add_attribute('unit', package.height.unit)
435
- height_element.add_attribute('value', package.height.value)
498
+ height_element.add_attribute('value', package.height.value.to_s)
436
499
  end
437
500
  if not package.length.nil?
438
501
  length_element = element.add_element('length')
439
502
  length_element.add_attribute('unit', package.length.unit)
440
- length_element.add_attribute('value', package.length.value)
503
+ length_element.add_attribute('value', package.length.value.to_s)
441
504
  end
442
505
  if not package.width.nil?
443
506
  width_element = element.add_element('width')
444
507
  width_element.add_attribute('unit', package.width.unit)
445
- width_element.add_attribute('value', package.width.value)
508
+ width_element.add_attribute('value', package.width.value.to_s)
446
509
  end
447
510
  end
448
511
 
@@ -685,6 +748,19 @@ module Google4R #:nodoc:
685
748
  end
686
749
  end
687
750
 
751
+ class CreateOrderRecurrenceRequestCommandXmlGenerator < CheckoutCommandXmlGenerator
752
+
753
+ protected
754
+
755
+ def process_command(command)
756
+ root = @document.add_element("create-order-recurrence-request" , { 'xmlns' => 'http://checkout.google.com/schema/2' })
757
+
758
+ root.attributes['google-order-number'] = command.google_order_number
759
+
760
+ self.process_shopping_shopping_cart(root, command.shopping_cart)
761
+ end
762
+ end
763
+
688
764
  class MerchantCalculationResultsXmlGenerator < XmlGenerator
689
765
 
690
766
  def initialize(merchant_calculation_results)
@@ -841,5 +917,39 @@ module Google4R #:nodoc:
841
917
 
842
918
  class ResetItemsShippingInformationCommandXmlGenerator < ItemsCommandXmlGenerator
843
919
  end
920
+
921
+ class ReturnOrderReportCommandXmlGenerator < CommandXmlGenerator
922
+ def initialize(command)
923
+ @command = command
924
+ end
925
+
926
+ protected
927
+
928
+ def process_command(command)
929
+ root = super
930
+ # TODO - sanity check format ?
931
+ root.add_attribute('start-date', command.start_date.to_s)
932
+ root.add_attribute('end-date', command.end_date.to_s)
933
+ flow_element = root
934
+
935
+ # <financial-state>
936
+ if command.financial_state then
937
+ financial_state_element = flow_element.add_element('financial-state')
938
+ financial_state_element.text = command.financial_state.to_s
939
+ end
940
+
941
+ # <fulfillment-state>
942
+ if command.fulfillment_state then
943
+ fulfillment_state_element = flow_element.add_element('fulfillment-state')
944
+ fulfillment_state_element.text = command.fulfillment_state.to_s
945
+ end
946
+
947
+ # <date-time-zone>
948
+ if command.date_time_zone then
949
+ dtz_element = flow_element.add_element('date-time-zone')
950
+ dtz_element.text = command.date_time_zone.to_s
951
+ end
952
+ end
953
+ end
844
954
  end
845
955
  end
@@ -62,6 +62,85 @@ class Google4R::Checkout::CheckoutCommandIntegrationTest < Test::Unit::TestCase
62
62
  # the redirect URL
63
63
  #puts @command.to_xml
64
64
  #puts result
65
+
66
+ assert_kind_of CheckoutRedirectResponse, result
67
+ end
68
+
69
+ def test_sending_to_google_works_with_google_handled_subscription
70
+ setup_command(@command)
71
+
72
+ @command.shopping_cart.create_item do |item|
73
+ item.name = "Test subscription"
74
+ item.description = "12 month subscription"
75
+ item.unit_price = Money.us_dollar(0)
76
+ item.quantity = 1
77
+ item.private_data = { :id => 123456 }
78
+
79
+ item.create_subscription do |subscription|
80
+ subscription.type = "google"
81
+ subscription.period = "MONTHLY"
82
+
83
+ subscription.add_payment do |payment|
84
+ payment.times = 12
85
+ payment.maximum_charge = Money.us_dollar(1200)
86
+ end
87
+
88
+ subscription.add_recurrent_item do |rec_item|
89
+ rec_item.name = "Usage of My Awesome Website for One Month"
90
+ rec_item.description = "Your flat charge for accessing my web site"
91
+ rec_item.quantity = 1
92
+ rec_item.unit_price = Money.us_dollar(1200)
93
+
94
+ rec_item.create_digital_content do |content|
95
+ content.display_disposition = "OPTIMISTIC"
96
+ content.url = "http://mywebsite.example.com"
97
+ content.description = "Pie is found at this web site!"
98
+ end
99
+ end
100
+
101
+ item.create_digital_content do |content|
102
+ content.display_disposition = "OPTIMISTIC"
103
+ content.description = "Congratulations! Your subscription is being set up. Feel free to log onto" +
104
+ "<a href=\"http://mywebsite.example.com\">My website</a>."
105
+ end
106
+ end
107
+ end
108
+
109
+ result = @command.send_to_google_checkout
110
+ assert_kind_of CheckoutRedirectResponse, result
111
+ end
112
+
113
+ def test_sending_to_google_works_with_merchant_handled_subscription
114
+ setup_command(@command)
115
+
116
+ @command.shopping_cart.create_item do |item|
117
+ item.name = "Test subscription"
118
+ item.description = "12 month subscription"
119
+ item.unit_price = Money.us_dollar(0)
120
+ item.quantity = 1
121
+ item.private_data = { :id => 123456 }
122
+
123
+ item.create_subscription do |subscription|
124
+ subscription.type = "merchant"
125
+ subscription.period = "MONTHLY"
126
+ subscription.start_date = Time.new
127
+ subscription.start_date += (60 * 60 * 24 * 30)
128
+ subscription.no_charge_after = subscription.start_date + (60 * 60 * 24 * 365)
129
+
130
+ subscription.add_payment do |payment|
131
+ payment.times = 12
132
+ payment.maximum_charge = Money.us_dollar(1200)
133
+ end
134
+
135
+ item.create_digital_content do |content|
136
+ content.display_disposition = "OPTIMISTIC"
137
+ content.description = "Congratulations! Your subscription is being set up. Feel free to log onto" +
138
+ "<a href=\"http://mywebsite.example.com\">My website</a>."
139
+ end
140
+ end
141
+ end
142
+
143
+ result = @command.send_to_google_checkout
65
144
  assert_kind_of CheckoutRedirectResponse, result
66
145
  end
67
146
 
@@ -158,6 +237,16 @@ class Google4R::Checkout::CheckoutCommandIntegrationTest < Test::Unit::TestCase
158
237
  item.quantity = i * 3
159
238
  item.id = "test-#{i}-123456789"
160
239
  item.weight = Weight.new(2.2)
240
+ if (i == 5)
241
+ item.create_digital_content do |dc|
242
+ dc.display_disposition =
243
+ Google4R::Checkout::Item::DigitalContent::OPTIMISTIC
244
+ dc.description = "Information on how to get your content"
245
+ dc.url = "http://my.domain.com/downloads"
246
+ dc.key = "abcde12345"
247
+ dc.email_delivery = false
248
+ end
249
+ end
161
250
  end
162
251
  end
163
252
  end
@@ -55,7 +55,8 @@ class Google4R::Checkout::FrontendTest < Test::Unit::TestCase
55
55
  :create_add_tracking_data_command, :create_archive_order_command,
56
56
  :create_unarchive_order_command, :create_ship_items_command,
57
57
  :create_backorder_items_command, :create_return_items_command,
58
- :create_cancel_items_command, :create_reset_items_shipping_information_command
58
+ :create_cancel_items_command, :create_reset_items_shipping_information_command,
59
+ :create_order_report_command
59
60
  ].each do |symbol|
60
61
  assert_respond_to @frontend, symbol
61
62
  end
@@ -130,7 +131,14 @@ class Google4R::Checkout::FrontendTest < Test::Unit::TestCase
130
131
  end
131
132
 
132
133
  def test_create_reset_items_shipping_information_command_works_correctly
133
- assert_kind_of ResetItemsShippingInformationCommand, @frontend.create_reset_items_shipping_information_command
134
+ assert_kind_of ResetItemsShippingInformationCommand,
135
+ @frontend.create_reset_items_shipping_information_command
134
136
  end
135
137
 
138
+ def test_create_order_report_command_works_correctly
139
+ assert_kind_of OrderReportCommand,
140
+ @frontend.create_order_report_command(
141
+ Time.utc(2007, 9, 1, 0, 0, 0),
142
+ Time.utc(2007, 9, 30, 23, 59, 59))
143
+ end
136
144
  end
@@ -0,0 +1,111 @@
1
+ #--
2
+ # Project: google_checkout4r
3
+ # File: test/unit/order_report_command_test.rb
4
+ # Author: Tony Chan <api.htchan at gmail dot com>
5
+ # Copyright: (c) 2007 by Dan Dukeson
6
+ # License: MIT License as follows:
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining
9
+ # a copy of this software and associated documentation files (the
10
+ # "Software"), to deal in the Software without restriction, including
11
+ # without limitation the rights to use, copy, modify, merge, publish,
12
+ # distribute, sublicense, and/or sell copies of the Software, and to permit
13
+ # persons to whom the Software is furnished to do so, subject to the
14
+ # following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included
17
+ # in all copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
25
+ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ #++
27
+
28
+ require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
29
+
30
+ require 'google4r/checkout'
31
+
32
+ require 'test/frontend_configuration'
33
+
34
+ # Tests for the CancelItemsCommand class.
35
+ class Google4R::Checkout::OrderReportCommandTest < Test::Unit::TestCase
36
+ include Google4R::Checkout
37
+
38
+ def setup
39
+ @frontend = Frontend.new(FRONTEND_CONFIGURATION)
40
+ @command = @frontend.create_order_report_command(
41
+ Time.utc(2007, 9, 1, 0, 0, 0),
42
+ Time.utc(2007, 9, 30, 23, 59, 59))
43
+ @command.financial_state = 'CHARGED'
44
+ @command.fulfillment_state = 'NEW'
45
+ @command.date_time_zone = 'America/New_York'
46
+
47
+ @sample_xml=%Q{<?xml version='1.0' encoding='UTF-8'?>
48
+ <order-list-request end-date='2007-09-30T23:59:59' start-date='2007-09-01T00:00:00' xmlns='http://checkout.google.com/schema/2'>
49
+ <financial-state>CHARGED</financial-state>
50
+ <fulfillment-state>NEW</fulfillment-state>
51
+ <date-time-zone>America/New_York</date-time-zone>
52
+ </order-list-request>}
53
+ end
54
+
55
+ def test_behaves_correctly
56
+ [ :start_date, :end_date,
57
+ :financial_state, :financial_state=,
58
+ :fulfillment_state, :fulfillment_state=,
59
+ :date_time_zone, :date_time_zone= ].each do |symbol|
60
+ assert_respond_to @command, symbol
61
+ end
62
+ end
63
+
64
+ def test_to_xml
65
+ assert_strings_equal(@sample_xml, @command.to_xml)
66
+ end
67
+
68
+ def test_accessors
69
+ assert_equal('2007-09-01T00:00:00', @command.start_date)
70
+ assert_equal('2007-09-30T23:59:59', @command.end_date)
71
+ assert_equal('CHARGED', @command.financial_state)
72
+ assert_equal('NEW', @command.fulfillment_state)
73
+ assert_equal('America/New_York', @command.date_time_zone)
74
+ end
75
+
76
+ def test_good_dates
77
+ assert_nothing_raised RuntimeError do
78
+ @frontend.create_order_report_command(
79
+ Time.utc(2007, 9, 1, 0, 0, 0),
80
+ Time.utc(2007, 9, 30, 23, 59, 59))
81
+ end
82
+ end
83
+
84
+ def test_dates_should_not_be_string
85
+ assert_raise RuntimeError do
86
+ @frontend.create_order_report_command(
87
+ '2007-09-01T00:00:00',
88
+ '2007-09-30T23:59:59')
89
+ end
90
+ end
91
+
92
+ def test_end_date_before_start_date
93
+ assert_raise RuntimeError do
94
+ @frontend.create_order_report_command(
95
+ Time.utc(2007, 9, 1, 0, 0, 0),
96
+ Time.utc(2006, 9, 30, 23, 59, 59))
97
+ end
98
+ end
99
+
100
+ def test_financial_state
101
+ assert_raise RuntimeError do
102
+ @command.financial_state = 'DUMMY'
103
+ end
104
+ end
105
+
106
+ def test_fulfillment_state
107
+ assert_raise RuntimeError do
108
+ @command.fulfillment_state = 'DUMMY'
109
+ end
110
+ end
111
+ end
metadata CHANGED
@@ -1,125 +1,149 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
3
- specification_version: 1
4
2
  name: google4r-checkout
5
3
  version: !ruby/object:Gem::Version
6
- version: 1.0.3
7
- date: 2008-08-12 00:00:00 -07:00
8
- summary: Ruby library to access the Google Checkout service and implement notification handlers.
9
- require_paths:
10
- - lib
11
- email:
12
- homepage:
13
- rubyforge_project:
14
- description: Ruby library to access the Google Checkout service and implement notification handlers.
15
- autorequire: ""
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: false
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.8.4
24
- version:
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 6
9
+ version: 1.0.6
25
10
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
11
  authors:
30
12
  - Tony Chan
13
+ autorequire: ""
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-01 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: money
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 7
30
+ - 1
31
+ version: 1.7.1
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: Ruby library to access the Google Checkout service and implement notification handlers.
35
+ email:
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README
42
+ - LICENSE
43
+ - CHANGES
31
44
  files:
32
- - lib/google4r/checkout/commands.rb
45
+ - lib/google4r/checkout.rb
33
46
  - lib/google4r/checkout/frontend.rb
34
- - lib/google4r/checkout/merchant_calculation.rb
35
- - lib/google4r/checkout/notifications.rb
36
47
  - lib/google4r/checkout/shared.rb
48
+ - lib/google4r/checkout/notifications.rb
37
49
  - lib/google4r/checkout/utils.rb
38
50
  - lib/google4r/checkout/xml_generation.rb
39
- - lib/google4r/checkout.rb
51
+ - lib/google4r/checkout/commands.rb
52
+ - lib/google4r/checkout/merchant_calculation.rb
40
53
  - var/cacert.pem
41
54
  - README
42
55
  - LICENSE
43
56
  - CHANGES
57
+ has_rdoc: true
58
+ homepage:
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options: []
63
+
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 1
72
+ - 8
73
+ - 4
74
+ version: 1.8.4
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.3.6
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Ruby library to access the Google Checkout service and implement notification handlers.
44
89
  test_files:
45
90
  - test/integration/checkout_command_test.rb
46
- - test/unit/add_merchant_order_number_command_test.rb
47
- - test/unit/add_tracking_data_command_test.rb
48
- - test/unit/address_test.rb
49
- - test/unit/anonymous_address_test.rb
50
- - test/unit/archive_order_command_test.rb
51
- - test/unit/area_test.rb
52
- - test/unit/authorization_amount_notification_test.rb
91
+ - test/unit/order_adjustment_test.rb
92
+ - test/unit/pickup_shipping_test.rb
93
+ - test/unit/merchant_code_test.rb
94
+ - test/unit/chargeback_amount_notification_test.rb
53
95
  - test/unit/authorize_order_command_test.rb
96
+ - test/unit/merchant_calculation_results_test.rb
97
+ - test/unit/risk_information_notification_test.rb
98
+ - test/unit/ship_items_command_test.rb
99
+ - test/unit/tax_table_test.rb
54
100
  - test/unit/backorder_items_command_test.rb
101
+ - test/unit/flat_rate_shipping_test.rb
102
+ - test/unit/refund_order_command_test.rb
103
+ - test/unit/merchant_code_result_test.rb
55
104
  - test/unit/callback_handler_test.rb
56
- - test/unit/cancel_items_command_test.rb
57
- - test/unit/cancel_order_command_test.rb
58
- - test/unit/carrier_calculated_shipping_test.rb
59
- - test/unit/charge_amount_notification_test.rb
60
105
  - test/unit/charge_order_command_test.rb
61
- - test/unit/chargeback_amount_notification_test.rb
62
- - test/unit/checkout_command_test.rb
63
- - test/unit/checkout_command_xml_generator_test.rb
64
- - test/unit/command_test.rb
65
- - test/unit/deliver_order_command_test.rb
66
- - test/unit/delivery_method_test.rb
67
- - test/unit/digital_content_test.rb
68
- - test/unit/flat_rate_shipping_test.rb
106
+ - test/unit/charge_amount_notification_test.rb
107
+ - test/unit/carrier_calculated_shipping_test.rb
108
+ - test/unit/return_items_command_test.rb
109
+ - test/unit/add_merchant_order_number_command_test.rb
110
+ - test/unit/authorization_amount_notification_test.rb
69
111
  - test/unit/frontend_test.rb
112
+ - test/unit/add_tracking_data_command_test.rb
113
+ - test/unit/address_test.rb
70
114
  - test/unit/item_info_test.rb
115
+ - test/unit/order_state_change_notification_test.rb
116
+ - test/unit/us_state_area_test.rb
117
+ - test/unit/notification_handler_test.rb
118
+ - test/unit/us_country_area_test.rb
119
+ - test/unit/shopping_cart_test.rb
120
+ - test/unit/reset_items_shipping_information_command_test.rb
121
+ - test/unit/order_report_command_test.rb
122
+ - test/unit/send_buyer_message_command_test.rb
71
123
  - test/unit/item_test.rb
72
- - test/unit/marketing_preferences_test.rb
124
+ - test/unit/deliver_order_command_test.rb
125
+ - test/unit/cancel_order_command_test.rb
126
+ - test/unit/archive_order_command_test.rb
127
+ - test/unit/world_area_test.rb
128
+ - test/unit/new_order_notification_test.rb
129
+ - test/unit/private_data_parser_test.rb
73
130
  - test/unit/merchant_calculated_shipping_test.rb
74
- - test/unit/merchant_calculation_callback_test.rb
131
+ - test/unit/cancel_items_command_test.rb
132
+ - test/unit/checkout_command_test.rb
133
+ - test/unit/area_test.rb
75
134
  - test/unit/merchant_calculation_result_test.rb
76
- - test/unit/merchant_calculation_results_test.rb
77
- - test/unit/merchant_code_result_test.rb
78
- - test/unit/merchant_code_test.rb
79
- - test/unit/new_order_notification_test.rb
135
+ - test/unit/marketing_preferences_test.rb
80
136
  - test/unit/notification_acknowledgement_test.rb
81
- - test/unit/notification_handler_test.rb
82
- - test/unit/order_adjustment_test.rb
83
- - test/unit/order_state_change_notification_test.rb
84
- - test/unit/pickup_shipping_test.rb
85
- - test/unit/postal_area_test.rb
86
- - test/unit/private_data_parser_test.rb
87
137
  - test/unit/refund_amount_notification_test.rb
88
- - test/unit/refund_order_command_test.rb
89
- - test/unit/reset_items_shipping_information_command_test.rb
90
- - test/unit/return_items_command_test.rb
91
- - test/unit/risk_information_notification_test.rb
92
- - test/unit/send_buyer_message_command_test.rb
93
- - test/unit/ship_items_command_test.rb
94
- - test/unit/shipping_adjustment_test.rb
95
- - test/unit/shopping_cart_test.rb
96
138
  - test/unit/tax_rule_test.rb
97
- - test/unit/tax_table_test.rb
98
- - test/unit/tracking_data_test.rb
99
139
  - test/unit/unarchive_order_command_test.rb
100
- - test/unit/us_country_area_test.rb
101
- - test/unit/us_state_area_test.rb
140
+ - test/unit/delivery_method_test.rb
141
+ - test/unit/checkout_command_xml_generator_test.rb
142
+ - test/unit/shipping_adjustment_test.rb
143
+ - test/unit/tracking_data_test.rb
144
+ - test/unit/postal_area_test.rb
145
+ - test/unit/anonymous_address_test.rb
102
146
  - test/unit/us_zip_area_test.rb
103
- - test/unit/world_area_test.rb
104
- rdoc_options: []
105
-
106
- extra_rdoc_files:
107
- - README
108
- - LICENSE
109
- - CHANGES
110
- executables: []
111
-
112
- extensions: []
113
-
114
- requirements: []
115
-
116
- dependencies:
117
- - !ruby/object:Gem::Dependency
118
- name: money
119
- version_requirement:
120
- version_requirements: !ruby/object:Gem::Version::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: 1.7.1
125
- version:
147
+ - test/unit/digital_content_test.rb
148
+ - test/unit/command_test.rb
149
+ - test/unit/merchant_calculation_callback_test.rb