From 450d744f9a9b2e8567475116d38025bc0f8c3ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Alby?= Date: Thu, 18 Feb 2021 12:47:17 +0100 Subject: [PATCH] Merge pull request #3665 from overleaf/cmg-jpa-members-limit-addon Support membersLimit add on for recurly subscriptions GitOrigin-RevId: d4256665d703e3c348cdac11af669c70f3c2feb2 --- .../Subscription/SubscriptionUpdater.js | 13 ++++ .../SubscriptionViewModelBuilder.js | 18 ++++- .../Subscription/SubscriptionUpdaterTests.js | 67 ++++++++++++++++++- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/services/web/app/src/Features/Subscription/SubscriptionUpdater.js b/services/web/app/src/Features/Subscription/SubscriptionUpdater.js index b2465471f7..e136b50def 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionUpdater.js +++ b/services/web/app/src/Features/Subscription/SubscriptionUpdater.js @@ -270,6 +270,19 @@ const SubscriptionUpdater = { } subscription.groupPlan = true subscription.membersLimit = plan.membersLimit + + // Some plans allow adding more seats than the base plan provides. + // This is recorded as a subscription add on. + if ( + plan.membersLimitAddOn && + Array.isArray(recurlySubscription.subscription_add_ons) + ) { + recurlySubscription.subscription_add_ons.forEach(addOn => { + if (addOn.add_on_code === plan.membersLimitAddOn) { + subscription.membersLimit += addOn.quantity + } + }) + } } subscription.save(function(error) { if (error) { diff --git a/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js b/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js index 4fec101c0e..0bfc182ab7 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js +++ b/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js @@ -185,6 +185,20 @@ module.exports = { (recurlySubscription != null ? recurlySubscription.tax_in_cents : undefined) || 0 + // Some plans allow adding more seats than the base plan provides. + // This is recorded as a subscription add on. + // Note: tax_in_cents already includes the tax for any addon. + let addOnPrice = 0 + if ( + plan.membersLimitAddOn && + Array.isArray(recurlySubscription.subscription_add_ons) + ) { + recurlySubscription.subscription_add_ons.forEach(addOn => { + if (addOn.add_on_code === plan.membersLimitAddOn) { + addOnPrice += addOn.quantity * addOn.unit_amount_in_cents + } + }) + } personalSubscription.recurly = { tax, taxRate: parseFloat( @@ -203,7 +217,9 @@ module.exports = { price: SubscriptionFormatters.formatPrice( (recurlySubscription != null ? recurlySubscription.unit_amount_in_cents - : undefined) + tax, + : undefined) + + addOnPrice + + tax, recurlySubscription != null ? recurlySubscription.currency : undefined diff --git a/services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js b/services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js index f512038bf0..769601fe2b 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js @@ -3,7 +3,7 @@ const chai = require('chai') const sinon = require('sinon') const modulePath = '../../../../app/src/Features/Subscription/SubscriptionUpdater' -const { assert } = require('chai') +const { assert, expect } = require('chai') const { ObjectId } = require('mongodb') chai.should() @@ -349,6 +349,71 @@ describe('SubscriptionUpdater', function() { } ) }) + + describe('when the plan allows adding more seats', function() { + beforeEach(function() { + this.membersLimitAddOn = 'add_on1' + this.PlansLocator.findLocalPlanInSettings + .withArgs(this.recurlySubscription.plan.plan_code) + .returns({ + groupPlan: true, + membersLimit: 5, + membersLimitAddOn: this.membersLimitAddOn + }) + }) + + function expectMembersLimit(limit) { + it('should set the membersLimit accordingly', function(done) { + this.SubscriptionUpdater._updateSubscriptionFromRecurly( + this.recurlySubscription, + this.subscription, + {}, + error => { + if (error) return done(error) + + expect(this.subscription.membersLimit).to.equal(limit) + done() + } + ) + }) + } + + describe('when the recurlySubscription does not have add ons', function() { + beforeEach(function() { + delete this.recurlySubscription.subscription_add_ons + }) + expectMembersLimit(5) + }) + + describe('when the recurlySubscription has non-matching add ons', function() { + beforeEach(function() { + this.recurlySubscription.subscription_add_ons = [ + { add_on_code: 'add_on_99', quantity: 3 } + ] + }) + expectMembersLimit(5) + }) + + describe('when the recurlySubscription has a matching add on', function() { + beforeEach(function() { + this.recurlySubscription.subscription_add_ons = [ + { add_on_code: this.membersLimitAddOn, quantity: 10 } + ] + }) + expectMembersLimit(15) + }) + + // NOTE: This is unexpected, but we are going to support it anyways. + describe('when the recurlySubscription has multiple matching add ons', function() { + beforeEach(function() { + this.recurlySubscription.subscription_add_ons = [ + { add_on_code: this.membersLimitAddOn, quantity: 10 }, + { add_on_code: this.membersLimitAddOn, quantity: 3 } + ] + }) + expectMembersLimit(18) + }) + }) }) describe('_createNewSubscription', function() {