saucy 0.10.7 → 0.10.8

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ 0.10.8
2
+
3
+ Backport merchant account TZ fixes from 0.13.1
4
+
1
5
  0.10.7
2
6
 
3
7
  Fix for daylight savings time issue with coupons
data/README.md CHANGED
@@ -53,7 +53,6 @@ followup emails being sent to users that have already set up their accounts.
53
53
  It will also send an "Activated" event to Kissmetrics through Saucy-kiss, if
54
54
  Saucy-kiss is installed.
55
55
 
56
-
57
56
  Development environment
58
57
  -----------------------
59
58
 
@@ -123,3 +122,9 @@ Make sure you don't do this in ApplicationController:
123
122
 
124
123
  Saucy's internal controllers don't skip any before filters.
125
124
 
125
+ If your billing merchant account is not configured to run in the Eastern
126
+ timezone, you can override the timezone in Saucy configuration as
127
+ merchant_account_time_zone. You'll want this set accurately so that the daily
128
+ cronjob which syncs the Braintree subscription billing cycle dates runs for the
129
+ correct accounts. If this is set incorrectly, you will occasionally see
130
+ duplicate receipt emails being delivered.
@@ -8,6 +8,7 @@ module Saucy
8
8
  cattr_accessor :merchant_account_id
9
9
  cattr_accessor :admin_username
10
10
  cattr_accessor :admin_password
11
+ cattr_accessor :merchant_account_time_zone
11
12
 
12
13
  def initialize
13
14
  @@manager_email_address = 'manager@example.com'
@@ -15,6 +16,7 @@ module Saucy
15
16
  @@layouts = Layouts.new
16
17
  @@admin_username = 'admin'
17
18
  @@admin_password = 'admin'
19
+ @@merchant_account_time_zone = 'Eastern Time (US & Canada)'
18
20
  end
19
21
 
20
22
  end
data/lib/saucy/engine.rb CHANGED
@@ -31,7 +31,10 @@ module Saucy
31
31
  ActionView::Base.send :include, CouponsHelper
32
32
  end
33
33
 
34
- {:short_date => "%x"}.each do |k, v|
34
+ {
35
+ :short_date => "%x",
36
+ :braintree_date => "%Y-%m-%d"
37
+ }.each do |k, v|
35
38
  Time::DATE_FORMATS[k] = v
36
39
  end
37
40
 
@@ -102,7 +102,7 @@ ShamRack.at("www.braintreegateway.com", 443).sinatra do
102
102
  subscription["transactions"] = []
103
103
  subscription["add_ons"] = []
104
104
  subscription["discounts"] = []
105
- subscription["next_billing_date"] = 1.month.from_now
105
+ subscription["next_billing_date"] = 1.month.from_now.to_s(:braintree_date)
106
106
  subscription["status"] = Braintree::Subscription::Status::Active
107
107
  FakeBraintree.subscriptions[subscription["id"]] = subscription
108
108
  [201, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(subscription.to_xml(:root => 'subscription'))]
@@ -88,7 +88,8 @@ module Saucy
88
88
  def update_subscription_cache!
89
89
  flush_cache :subscription
90
90
  update_attribute(:subscription_status, subscription.status)
91
- update_attribute(:next_billing_date, subscription.next_billing_date)
91
+ update_attribute(:next_billing_date, Time.parse(subscription.next_billing_date).in_time_zone(
92
+ Saucy::Configuration.merchant_account_time_zone))
92
93
  end
93
94
 
94
95
  def changing_plan?(attributes)
@@ -198,7 +199,8 @@ module Saucy
198
199
  result = Braintree::Subscription.create(subscription_attributes)
199
200
  if result.success?
200
201
  self.subscription_token = result.subscription.id
201
- self.next_billing_date = result.subscription.next_billing_date
202
+ self.next_billing_date = Time.parse(result.subscription.next_billing_date).in_time_zone(
203
+ Saucy::Configuration.merchant_account_time_zone)
202
204
  self.subscription_status = result.subscription.status
203
205
  else
204
206
  false
@@ -226,6 +228,8 @@ module Saucy
226
228
  recently_billed.each do |account|
227
229
  account.subscription_status = account.subscription.status
228
230
  account.next_billing_date = account.subscription.next_billing_date
231
+ account.next_billing_date = Time.parse(account.subscription.next_billing_date).in_time_zone(
232
+ Saucy::Configuration.merchant_account_time_zone)
229
233
  account.save!
230
234
  if account.past_due?
231
235
  BillingMailer.problem(account, account.subscription.transactions.last).deliver!
data/spec/environment.rb CHANGED
@@ -49,8 +49,8 @@ end
49
49
 
50
50
  Testapp::Application.initialize!
51
51
 
52
- require "lib/generators/saucy/features/templates/factories"
53
- require "lib/generators/saucy/install/templates/create_saucy_tables"
52
+ require "./lib/generators/saucy/features/templates/factories"
53
+ require "./lib/generators/saucy/install/templates/create_saucy_tables"
54
54
 
55
55
  class ClearanceCreateUsers < ActiveRecord::Migration
56
56
  def self.up
@@ -122,8 +122,9 @@ describe Account, "with a paid plan" do
122
122
  subject.subscription.should_not be_nil
123
123
  end
124
124
 
125
- it "has a next_billing_date" do
126
- subject.next_billing_date.should_not be_nil
125
+ it "has a next_billing_date in the merchant account timezone" do
126
+ Time.stubs(:now => Time.parse("2011-09-16 02:00 -0000"))
127
+ subject.next_billing_date.should == Time.parse("Sun, 16 Oct 2011 00:00:00 EDT -04:00")
127
128
  end
128
129
 
129
130
  it "has an active subscription status" do
@@ -295,14 +296,16 @@ describe Account, "with a paid subscription" do
295
296
  end
296
297
 
297
298
  it "gets marked as past due and updates its next_billing_date when subscriptions are updated and it has been rejected by the gateway" do
299
+ next_billing_date_string = 2.months.from_now.to_s(:braintree_date)
298
300
  subscription = FakeBraintree.subscriptions[subject.subscription_token]
299
301
  subscription["status"] = Braintree::Subscription::Status::PastDue
300
- subscription["next_billing_date"] = 2.months.from_now
302
+ subscription["next_billing_date"] = next_billing_date_string
301
303
 
302
304
  Timecop.travel(subject.next_billing_date + 1.day) do
303
305
  Account.update_subscriptions!
304
306
  subject.reload.subscription_status.should == Braintree::Subscription::Status::PastDue
305
- subject.next_billing_date.to_s.should == subscription["next_billing_date"].to_s
307
+ subject.next_billing_date.should == Time.parse(next_billing_date_string).in_time_zone(
308
+ Saucy::Configuration.merchant_account_time_zone)
306
309
  subject.past_due?.should be
307
310
  end
308
311
  end
@@ -312,7 +315,7 @@ describe Account, "with a paid subscription" do
312
315
 
313
316
  before do
314
317
  subscription["status"] = Braintree::Subscription::Status::PastDue
315
- subscription["next_billing_date"] = 2.months.from_now
318
+ subscription["next_billing_date"] = 2.months.from_now.to_s(:braintree_date)
316
319
  FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Failed,
317
320
  :subscription_id => subject.subscription_token }
318
321
  subscription["transactions"] = [FakeBraintree.generated_transaction]
@@ -339,12 +342,49 @@ describe Account, "with a paid subscription" do
339
342
  end
340
343
  end
341
344
 
345
+ context "with a merchant account timezone different from the system timezone" do
346
+ def build_subscription(account)
347
+ subscription = FakeBraintree.subscriptions[account.subscription_token]
348
+ subscription["status"] = Braintree::Subscription::Status::Active
349
+ subscription["transactions"] = [FakeBraintree.generated_transaction]
350
+ subscription
351
+ end
352
+
353
+ before do
354
+ Saucy::Configuration.merchant_account_time_zone = "Eastern Time (US & Canada)"
355
+ subject.next_billing_date = "2011-08-15"
356
+ subject.save!
357
+
358
+ subscription = build_subscription(subject)
359
+ subscription["next_billing_date"] = "2011-09-15"
360
+
361
+ Time.stubs(:now => Time.parse("2011-08-16 02:00 -0000"))
362
+ Account.update_subscriptions!
363
+ ActionMailer::Base.deliveries.clear
364
+
365
+ subject.reload.next_billing_date.should == Time.parse("2011-09-15 00:00 -0400")
366
+ end
367
+
368
+ it "does not update accounts if their billing date hasn't elapsed from the merchant account's perspective" do
369
+ Time.stubs(:now => Time.parse("2011-09-15 02:00 -0000"))
370
+ Account.update_subscriptions!
371
+ ActionMailer::Base.deliveries.should be_empty
372
+ end
373
+
374
+ it "does update accounts if their billing date has elapsed from the merchant account's perspective" do
375
+ Time.stubs(:now => Time.parse("2011-09-16 02:00 -0000"))
376
+ Account.update_subscriptions!
377
+ ActionMailer::Base.deliveries.should_not be_empty
378
+ end
379
+ end
380
+
342
381
  context "and an active subscription due 2 months from now" do
343
382
  let(:subscription) { FakeBraintree.subscriptions[subject.subscription_token] }
383
+ let(:next_billing_date_string) { 2.months.from_now.to_s(:braintree_date) }
344
384
 
345
385
  before do
346
386
  subscription["status"] = Braintree::Subscription::Status::Active
347
- subscription["next_billing_date"] = 2.months.from_now
387
+ subscription["next_billing_date"] = next_billing_date_string
348
388
  FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Settled,
349
389
  :subscription_id => subject.subscription_token }
350
390
  subscription["transactions"] = [FakeBraintree.generated_transaction]
@@ -354,7 +394,8 @@ describe Account, "with a paid subscription" do
354
394
  Timecop.travel(subject.next_billing_date + 1.day) do
355
395
  Account.update_subscriptions!
356
396
  subject.reload.subscription_status.should == Braintree::Subscription::Status::Active
357
- subject.next_billing_date.to_s.should == subscription["next_billing_date"].to_s
397
+ subject.next_billing_date.should == Time.parse(next_billing_date_string).in_time_zone(
398
+ Saucy::Configuration.merchant_account_time_zone)
358
399
  end
359
400
  end
360
401
 
@@ -415,7 +456,7 @@ describe Account, "with a paid subscription that is past due" do
415
456
  before do
416
457
  subscription = FakeBraintree.subscriptions[subject.subscription_token]
417
458
  subscription["status"] = Braintree::Subscription::Status::PastDue
418
- subscription["next_billing_date"] = 2.months.from_now
459
+ subscription["next_billing_date"] = 2.months.from_now.to_s(:braintree_date)
419
460
 
420
461
  Timecop.travel(subject.next_billing_date + 1.day) do
421
462
  Account.update_subscriptions!
@@ -424,9 +465,10 @@ describe Account, "with a paid subscription that is past due" do
424
465
  end
425
466
 
426
467
  it "retries the subscription charge and updates the subscription when the billing information is correctly updated" do
468
+ next_billing_date_string = 1.day.from_now.to_s(:braintree_date)
427
469
  subscription = FakeBraintree.subscriptions[subject.subscription_token]
428
470
  subscription["status"] = Braintree::Subscription::Status::Active
429
- subscription["next_billing_date"] = 2.months.from_now
471
+ subscription["next_billing_date"] = next_billing_date_string
430
472
  FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Settled,
431
473
  :subscription_id => subject.subscription_token }
432
474
  transaction = FakeBraintree.generated_transaction
@@ -444,13 +486,15 @@ describe Account, "with a paid subscription that is past due" do
444
486
  :expiration_year => 2012).should be
445
487
 
446
488
  subject.reload.subscription_status.should == Braintree::Subscription::Status::Active
447
- subject.next_billing_date.to_s.should == subscription["next_billing_date"].to_s
489
+ subject.next_billing_date.should == Time.parse(next_billing_date_string).in_time_zone(
490
+ Saucy::Configuration.merchant_account_time_zone)
448
491
  end
449
492
 
450
493
  it "retries the subscription charge and updates the subscription when the payment processing fails" do
494
+ next_billing_date_string = 2.months.from_now.to_s(:braintree_date)
451
495
  subscription = FakeBraintree.subscriptions[subject.subscription_token]
452
496
  subscription["status"] = Braintree::Subscription::Status::PastDue
453
- subscription["next_billing_date"] = 1.day.from_now
497
+ subscription["next_billing_date"] = next_billing_date_string
454
498
  FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Failed,
455
499
  :subscription_id => subject.subscription_token }
456
500
  transaction = FakeBraintree.generated_transaction
@@ -469,13 +513,15 @@ describe Account, "with a paid subscription that is past due" do
469
513
 
470
514
  subject.errors[:card_number].should include("was denied by the payment processor with the message: no good")
471
515
  subject.reload.subscription_status.should == Braintree::Subscription::Status::PastDue
472
- subject.next_billing_date.to_s.should == subscription["next_billing_date"].to_s
516
+ subject.next_billing_date.should == Time.parse(next_billing_date_string).in_time_zone(
517
+ Saucy::Configuration.merchant_account_time_zone)
473
518
  end
474
519
 
475
520
  it "retries the subscription charge and updates the subscription when the settlement fails" do
521
+ next_billing_date_string = 1.day.from_now.to_s(:braintree_date)
476
522
  subscription = FakeBraintree.subscriptions[subject.subscription_token]
477
523
  subscription["status"] = Braintree::Subscription::Status::PastDue
478
- subscription["next_billing_date"] = 1.day.from_now
524
+ subscription["next_billing_date"] = next_billing_date_string
479
525
  FakeBraintree.transaction = { :status => Braintree::Transaction::Status::Failed,
480
526
  :subscription_id => subject.subscription_token }
481
527
  transaction = FakeBraintree.generated_transaction
@@ -494,7 +540,8 @@ describe Account, "with a paid subscription that is past due" do
494
540
 
495
541
  subject.errors[:card_number].should include("no good")
496
542
  subject.reload.subscription_status.should == Braintree::Subscription::Status::PastDue
497
- subject.next_billing_date.to_s.should == subscription["next_billing_date"].to_s
543
+ subject.next_billing_date.should == Time.parse(next_billing_date_string).in_time_zone(
544
+ Saucy::Configuration.merchant_account_time_zone)
498
545
  end
499
546
  end
500
547
 
@@ -36,4 +36,18 @@ describe Saucy::Configuration do
36
36
  subject.support_email_address = old_address
37
37
  end
38
38
  end
39
+
40
+ it "can assign a merchant_account_time_zone" do
41
+ old_merchant_account_time_zone = subject.merchant_account_time_zone
42
+ begin
43
+ subject.merchant_account_time_zone = "International Date Line West"
44
+ subject.merchant_account_time_zone.should == "International Date Line West"
45
+ ensure
46
+ subject.merchant_account_time_zone = old_merchant_account_time_zone
47
+ end
48
+ end
49
+
50
+ it "defaults merchant_account_time_zone to 'Eastern Time (US & Canada)'" do
51
+ subject.merchant_account_time_zone.should == "Eastern Time (US & Canada)"
52
+ end
39
53
  end
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saucy
3
3
  version: !ruby/object:Gem::Version
4
- hash: 57
5
4
  prerelease:
6
- segments:
7
- - 0
8
- - 10
9
- - 7
10
- version: 0.10.7
5
+ version: 0.10.8
11
6
  platform: ruby
12
7
  authors:
13
8
  - thoughtbot, inc.
@@ -20,119 +15,84 @@ autorequire:
20
15
  bindir: bin
21
16
  cert_chain: []
22
17
 
23
- date: 2011-08-12 00:00:00 -04:00
24
- default_executable:
18
+ date: 2011-10-17 00:00:00 Z
25
19
  dependencies:
26
20
  - !ruby/object:Gem::Dependency
27
21
  name: clearance
28
- prerelease: false
29
- type: :runtime
30
22
  requirement: &id001 !ruby/object:Gem::Requirement
31
23
  none: false
32
24
  requirements:
33
25
  - - ~>
34
26
  - !ruby/object:Gem::Version
35
- hash: 55
36
- segments:
37
- - 0
38
- - 11
39
- - 2
40
27
  version: 0.11.2
28
+ type: :runtime
29
+ prerelease: false
41
30
  version_requirements: *id001
42
31
  - !ruby/object:Gem::Dependency
43
32
  name: formtastic
44
- prerelease: false
45
- type: :runtime
46
33
  requirement: &id002 !ruby/object:Gem::Requirement
47
34
  none: false
48
35
  requirements:
49
36
  - - ">="
50
37
  - !ruby/object:Gem::Version
51
- hash: 11
52
- segments:
53
- - 1
54
- - 2
55
38
  version: "1.2"
39
+ type: :runtime
40
+ prerelease: false
56
41
  version_requirements: *id002
57
42
  - !ruby/object:Gem::Dependency
58
43
  name: railties
59
- prerelease: false
60
- type: :runtime
61
44
  requirement: &id003 !ruby/object:Gem::Requirement
62
45
  none: false
63
46
  requirements:
64
47
  - - ">="
65
48
  - !ruby/object:Gem::Version
66
- hash: 1
67
- segments:
68
- - 3
69
- - 0
70
- - 3
71
49
  version: 3.0.3
50
+ type: :runtime
51
+ prerelease: false
72
52
  version_requirements: *id003
73
53
  - !ruby/object:Gem::Dependency
74
54
  name: braintree
75
- prerelease: false
76
- type: :runtime
77
55
  requirement: &id004 !ruby/object:Gem::Requirement
78
56
  none: false
79
57
  requirements:
80
58
  - - ">="
81
59
  - !ruby/object:Gem::Version
82
- hash: 19
83
- segments:
84
- - 2
85
- - 6
86
- - 2
87
60
  version: 2.6.2
61
+ type: :runtime
62
+ prerelease: false
88
63
  version_requirements: *id004
89
64
  - !ruby/object:Gem::Dependency
90
65
  name: sham_rack
91
- prerelease: false
92
- type: :runtime
93
66
  requirement: &id005 !ruby/object:Gem::Requirement
94
67
  none: false
95
68
  requirements:
96
69
  - - "="
97
70
  - !ruby/object:Gem::Version
98
- hash: 29
99
- segments:
100
- - 1
101
- - 3
102
- - 3
103
71
  version: 1.3.3
72
+ type: :runtime
73
+ prerelease: false
104
74
  version_requirements: *id005
105
75
  - !ruby/object:Gem::Dependency
106
76
  name: sinatra
107
- prerelease: false
108
- type: :runtime
109
77
  requirement: &id006 !ruby/object:Gem::Requirement
110
78
  none: false
111
79
  requirements:
112
80
  - - ">="
113
81
  - !ruby/object:Gem::Version
114
- hash: 23
115
- segments:
116
- - 1
117
- - 1
118
- - 2
119
82
  version: 1.1.2
83
+ type: :runtime
84
+ prerelease: false
120
85
  version_requirements: *id006
121
86
  - !ruby/object:Gem::Dependency
122
87
  name: aruba
123
- prerelease: false
124
- type: :development
125
88
  requirement: &id007 !ruby/object:Gem::Requirement
126
89
  none: false
127
90
  requirements:
128
91
  - - "="
129
92
  - !ruby/object:Gem::Version
130
- hash: 27
131
- segments:
132
- - 0
133
- - 2
134
- - 6
135
93
  version: 0.2.6
94
+ type: :development
95
+ prerelease: false
136
96
  version_requirements: *id007
137
97
  description: Clearance-based Rails engine for Software as a Service (Saas) that provides account and project management
138
98
  email: support@thoughtbot.com
@@ -314,7 +274,6 @@ files:
314
274
  - spec/support/clearance_matchers.rb
315
275
  - spec/support/notifications.rb
316
276
  - spec/views/accounts/_account.html.erb_spec.rb
317
- has_rdoc: true
318
277
  homepage: http://github.com/thoughtbot/saucy
319
278
  licenses: []
320
279
 
@@ -328,7 +287,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
328
287
  requirements:
329
288
  - - ">="
330
289
  - !ruby/object:Gem::Version
331
- hash: 3
290
+ hash: 209378144174508178
332
291
  segments:
333
292
  - 0
334
293
  version: "0"
@@ -337,14 +296,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
337
296
  requirements:
338
297
  - - ">="
339
298
  - !ruby/object:Gem::Version
340
- hash: 3
341
- segments:
342
- - 0
343
299
  version: "0"
344
300
  requirements: []
345
301
 
346
302
  rubyforge_project:
347
- rubygems_version: 1.6.1
303
+ rubygems_version: 1.8.10
348
304
  signing_key:
349
305
  specification_version: 3
350
306
  summary: Clearance-based Rails engine for SaaS