mirror of
https://github.com/overleaf/overleaf.git
synced 2025-02-12 02:32:44 +00:00
Display Pricing Exceptions on Subscription Dashboard (#1720)
Display Pricing Exceptions on Subscription Dashboard GitOrigin-RevId: 31de89824db70b7af1f8704e6da592064ce44bfd
This commit is contained in:
parent
44ba16bb7c
commit
85e7f688d5
8 changed files with 118 additions and 4 deletions
|
@ -321,6 +321,27 @@ module.exports = RecurlyWrapper =
|
|||
RecurlyWrapper._parseAccountXml body, callback
|
||||
)
|
||||
|
||||
getAccountActiveCoupons: (accountId, callback) ->
|
||||
RecurlyWrapper.apiRequest({
|
||||
url: "accounts/#{accountId}/redemptions"
|
||||
}, (error, response, body) =>
|
||||
return callback(error) if error?
|
||||
RecurlyWrapper._parseRedemptionsXml body, (error, redemptions) ->
|
||||
return callback(error) if error?
|
||||
activeRedemptions = redemptions.filter (redemption) ->
|
||||
redemption.state == 'active'
|
||||
couponCodes = activeRedemptions.map (redemption) ->
|
||||
redemption.coupon_code
|
||||
Async.map couponCodes, RecurlyWrapper.getCoupon, (error, coupons) ->
|
||||
return callback(error) if error?
|
||||
callback(null, coupons)
|
||||
)
|
||||
|
||||
getCoupon: (couponCode, callback) ->
|
||||
opts = { url: "coupons/#{couponCode}" }
|
||||
RecurlyWrapper.apiRequest opts, (error, response, body) ->
|
||||
RecurlyWrapper._parseCouponXml body, callback
|
||||
|
||||
getBillingInfo: (accountId, callback)->
|
||||
RecurlyWrapper.apiRequest({
|
||||
url: "accounts/#{accountId}/billing_info"
|
||||
|
@ -465,6 +486,12 @@ module.exports = RecurlyWrapper =
|
|||
_parseBillingInfoXml: (xml, callback) ->
|
||||
RecurlyWrapper._parseXmlAndGetAttribute xml, "billing_info", callback
|
||||
|
||||
_parseRedemptionsXml: (xml, callback) ->
|
||||
RecurlyWrapper._parseXmlAndGetAttribute xml, "redemptions", callback
|
||||
|
||||
_parseCouponXml: (xml, callback) ->
|
||||
RecurlyWrapper._parseXmlAndGetAttribute xml, "coupon", callback
|
||||
|
||||
_parseXmlAndGetAttribute: (xml, attribute, callback) ->
|
||||
RecurlyWrapper._parseXml xml, (error, data) ->
|
||||
return callback(error) if error?
|
||||
|
@ -505,6 +532,7 @@ module.exports = RecurlyWrapper =
|
|||
parser = new xml2js.Parser(
|
||||
explicitRoot : true
|
||||
explicitArray : false
|
||||
emptyTag: ''
|
||||
)
|
||||
parser.parseString xml, (error, data) ->
|
||||
return callback(error) if error?
|
||||
|
|
|
@ -150,7 +150,7 @@ module.exports = SubscriptionController =
|
|||
return res.redirect '/user/subscription/plans'
|
||||
res.render "subscriptions/successful_subscription",
|
||||
title: "thank_you"
|
||||
subscription:personalSubscription
|
||||
personalSubscription: personalSubscription
|
||||
|
||||
cancelSubscription: (req, res, next) ->
|
||||
user = AuthenticationController.getSessionUser(req)
|
||||
|
|
|
@ -34,6 +34,11 @@ module.exports =
|
|||
return cb(null, null)
|
||||
RecurlyWrapper.getSubscription personalSubscription.recurlySubscription_id, includeAccount: true, cb
|
||||
]
|
||||
recurlyCoupons: ['recurlySubscription', (cb, {recurlySubscription}) ->
|
||||
return cb(null, null) if !recurlySubscription
|
||||
accountId = recurlySubscription.account.account_code
|
||||
RecurlyWrapper.getAccountActiveCoupons accountId, cb
|
||||
]
|
||||
plan: ['personalSubscription', (cb, {personalSubscription}) ->
|
||||
return cb() if !personalSubscription?
|
||||
plan = PlansLocator.findLocalPlanInSettings(personalSubscription.planCode)
|
||||
|
@ -65,6 +70,7 @@ module.exports =
|
|||
managedPublishers,
|
||||
v1SubscriptionStatus,
|
||||
recurlySubscription,
|
||||
recurlyCoupons,
|
||||
plan
|
||||
} = results
|
||||
memberGroupSubscriptions ?= []
|
||||
|
@ -72,6 +78,7 @@ module.exports =
|
|||
confirmedMemberInstitutions ?= []
|
||||
managedInstitutions ?= []
|
||||
v1SubscriptionStatus ?= {}
|
||||
recurlyCoupons ?= []
|
||||
|
||||
|
||||
if personalSubscription?.toObject?
|
||||
|
@ -93,6 +100,7 @@ module.exports =
|
|||
state: recurlySubscription.state
|
||||
trialEndsAtFormatted: SubscriptionFormatters.formatDate(recurlySubscription?.trial_ends_at)
|
||||
trial_ends_at: recurlySubscription.trial_ends_at
|
||||
activeCoupons: recurlyCoupons
|
||||
}
|
||||
|
||||
for memberGroupSubscription in memberGroupSubscriptions
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
p
|
||||
i * !{translate("subject_to_additional_vat")}
|
||||
|
||||
if (personalSubscription.recurly.activeCoupons.length > 0)
|
||||
i * !{translate("coupons_not_included")}:
|
||||
ul
|
||||
each coupon in personalSubscription.recurly.activeCoupons
|
||||
li
|
||||
i= coupon.description || coupon.name
|
|
@ -13,6 +13,7 @@ div(ng-controller="RecurlySubscriptionController")
|
|||
-if (personalSubscription.recurly.trialEndsAtFormatted && personalSubscription.recurly.trial_ends_at > Date.now())
|
||||
p You're on a free trial which ends on <strong ng-non-bindable>#{personalSubscription.recurly.trialEndsAtFormatted}</strong>
|
||||
p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>" + personalSubscription.recurly.price + "</strong>", collectionDate:"<strong>" + personalSubscription.recurly.nextPaymentDueAt + "</strong>"})}
|
||||
include ./../_price_exceptions
|
||||
p.pull-right
|
||||
p
|
||||
a(href=personalSubscription.recurly.billingDetailsLink, target="_blank").btn.btn-info #{translate("update_your_billing_details")}
|
||||
|
|
|
@ -9,14 +9,15 @@ block content
|
|||
.page-header
|
||||
h2 #{translate("thanks_for_subscribing")}
|
||||
.alert.alert-success
|
||||
p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>"+subscription.recurly.price+"</strong>", collectionDate:"<strong>"+subscription.recurly.nextPaymentDueAt+"</strong>"})}
|
||||
p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>"+personalSubscription.recurly.price+"</strong>", collectionDate:"<strong>"+personalSubscription.recurly.nextPaymentDueAt+"</strong>"})}
|
||||
include ./_price_exceptions
|
||||
p #{translate("to_modify_your_subscription_go_to")}
|
||||
a(href="/user/subscription") #{translate("manage_subscription")}.
|
||||
p
|
||||
- if (subscription.groupPlan == true)
|
||||
- if (personalSubscription.groupPlan == true)
|
||||
a.btn.btn-success.btn-large(href="/subscription/group") #{translate("add_your_first_group_member_now")}
|
||||
p.letter-from-founders
|
||||
p #{translate("thanks_for_subscribing_you_help_sl", {planName:subscription.plan.name})}
|
||||
p #{translate("thanks_for_subscribing_you_help_sl", {planName:personalSubscription.plan.name})}
|
||||
p #{translate("need_anything_contact_us_at")}
|
||||
a(href=`mailto:${settings.adminEmail}`, ng-non-bindable) #{settings.adminEmail}
|
||||
| .
|
||||
|
|
|
@ -42,6 +42,11 @@ describe 'Subscriptions', ->
|
|||
account_id: 'mock-account-id',
|
||||
trial_ends_at: new Date(2018, 6, 7)
|
||||
}
|
||||
MockRecurlyApi.coupons = @coupons = {
|
||||
'test-coupon-1': { description: 'Test Coupon 1' }
|
||||
'test-coupon-2': { description: 'Test Coupon 2' }
|
||||
'test-coupon-3': { name: 'TestCoupon3' }
|
||||
}
|
||||
Subscription.create {
|
||||
admin_id: @user._id,
|
||||
manager_ids: [@user._id],
|
||||
|
@ -57,6 +62,8 @@ describe 'Subscriptions', ->
|
|||
after (done) ->
|
||||
MockRecurlyApi.accounts = {}
|
||||
MockRecurlyApi.subscriptions = {}
|
||||
MockRecurlyApi.coupons = {}
|
||||
MockRecurlyApi.redemptions = {}
|
||||
Subscription.remove {
|
||||
admin_id: @user._id
|
||||
}, done
|
||||
|
@ -68,6 +75,7 @@ describe 'Subscriptions', ->
|
|||
expect(subscription.planCode).to.equal 'collaborator'
|
||||
expect(subscription.recurly).to.exist
|
||||
expect(subscription.recurly).to.deep.equal {
|
||||
"activeCoupons": []
|
||||
"billingDetailsLink": "https://test.recurly.com/account/billing_info/edit?ht=mock-login-token"
|
||||
"currency": "GBP"
|
||||
"nextPaymentDueAt": "5th May 2018"
|
||||
|
@ -82,6 +90,30 @@ describe 'Subscriptions', ->
|
|||
it 'should return no memberGroupSubscriptions', ->
|
||||
expect(@data.memberGroupSubscriptions).to.deep.equal []
|
||||
|
||||
it 'should include redeemed coupons', (done) ->
|
||||
MockRecurlyApi.redemptions['mock-account-id'] = [
|
||||
{ state: 'active', coupon_code: 'test-coupon-1' }
|
||||
{ state: 'inactive', coupon_code: 'test-coupon-2' }
|
||||
{ state: 'active', coupon_code: 'test-coupon-3' }
|
||||
]
|
||||
|
||||
# rebuild the view model with the redemptions
|
||||
SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, data) ->
|
||||
expect(error).to.not.exist
|
||||
expect(data.personalSubscription.recurly.activeCoupons).to.deep.equal [
|
||||
{
|
||||
coupon_code: 'test-coupon-1',
|
||||
name: '',
|
||||
description: 'Test Coupon 1'
|
||||
}
|
||||
{
|
||||
coupon_code: 'test-coupon-3',
|
||||
name: 'TestCoupon3',
|
||||
description: ''
|
||||
}
|
||||
]
|
||||
done()
|
||||
|
||||
describe 'when the user has a subscription without recurly', ->
|
||||
before (done) ->
|
||||
Subscription.create {
|
||||
|
|
|
@ -9,6 +9,10 @@ module.exports = MockRecurlyApi =
|
|||
|
||||
accounts: {}
|
||||
|
||||
redemptions: {}
|
||||
|
||||
coupons: {}
|
||||
|
||||
run: () ->
|
||||
app.get '/subscriptions/:id', (req, res, next) =>
|
||||
subscription = @subscriptions[req.params.id]
|
||||
|
@ -36,10 +40,41 @@ module.exports = MockRecurlyApi =
|
|||
else
|
||||
res.send """
|
||||
<account>
|
||||
<account_code>#{req.params.id}</account_code>
|
||||
<hosted_login_token>#{account.hosted_login_token}</hosted_login_token>
|
||||
</account>
|
||||
"""
|
||||
|
||||
app.get '/coupons/:code', (req, res, next) =>
|
||||
coupon = @coupons[req.params.code]
|
||||
if !coupon?
|
||||
res.status(404).end()
|
||||
else
|
||||
res.send """
|
||||
<coupon>
|
||||
<coupon_code>#{req.params.code}</coupon_code>
|
||||
<name>#{coupon.name or ''}</name>
|
||||
<description>#{coupon.description or ''}</description>
|
||||
</coupon>
|
||||
"""
|
||||
|
||||
app.get '/accounts/:id/redemptions', (req, res, next) =>
|
||||
redemptions = @redemptions[req.params.id] or []
|
||||
redemptionsListXml = ''
|
||||
for redemption in redemptions
|
||||
redemptionsListXml += """
|
||||
<redemption>
|
||||
<state>#{redemption.state}</state>
|
||||
<coupon_code>#{redemption.coupon_code}</coupon_code>
|
||||
</redemption>
|
||||
"""
|
||||
|
||||
res.send """
|
||||
<redemptions type="array">
|
||||
#{redemptionsListXml}
|
||||
</redemptions>
|
||||
"""
|
||||
|
||||
app.listen 6034, (error) ->
|
||||
throw error if error?
|
||||
|
||||
|
|
Loading…
Reference in a new issue