mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-27 11:31:13 +00:00
Merge pull request #23415 from overleaf/ii-flexible-group-licensing-manually-collected
[web] Manually collected subscriptions with flexible licensing GitOrigin-RevId: ca7cf2abf5cfa1d873614bf3407fb5a259a78a76
This commit is contained in:
parent
d66c11e786
commit
8421bcc5d2
17 changed files with 263 additions and 45 deletions
|
@ -16,9 +16,12 @@ class AddOnNotPresentError extends OError {}
|
|||
|
||||
class MissingBillingInfoError extends OError {}
|
||||
|
||||
class ManuallyCollectedError extends OError {}
|
||||
|
||||
module.exports = {
|
||||
RecurlyTransactionError,
|
||||
DuplicateAddOnError,
|
||||
AddOnNotPresentError,
|
||||
MissingBillingInfoError,
|
||||
ManuallyCollectedError,
|
||||
}
|
||||
|
|
|
@ -261,7 +261,8 @@ function subscriptionFromApi(apiSubscription) {
|
|||
apiSubscription.currency == null ||
|
||||
apiSubscription.currentPeriodStartedAt == null ||
|
||||
apiSubscription.currentPeriodEndsAt == null ||
|
||||
apiSubscription.createdAt == null
|
||||
apiSubscription.createdAt == null ||
|
||||
apiSubscription.collectionMethod == null
|
||||
) {
|
||||
throw new OError('Invalid Recurly subscription', {
|
||||
subscription: apiSubscription,
|
||||
|
@ -283,6 +284,7 @@ function subscriptionFromApi(apiSubscription) {
|
|||
periodStart: apiSubscription.currentPeriodStartedAt,
|
||||
periodEnd: apiSubscription.currentPeriodEndsAt,
|
||||
createdAt: apiSubscription.createdAt,
|
||||
collectionMethod: apiSubscription.collectionMethod,
|
||||
})
|
||||
|
||||
if (apiSubscription.pendingChange != null) {
|
||||
|
|
|
@ -26,6 +26,7 @@ class RecurlySubscription {
|
|||
* @param {Date} props.periodStart
|
||||
* @param {Date} props.periodEnd
|
||||
* @param {Date} props.createdAt
|
||||
* @param {string} props.collectionMethod
|
||||
* @param {RecurlySubscriptionChange} [props.pendingChange]
|
||||
*/
|
||||
constructor(props) {
|
||||
|
@ -43,6 +44,7 @@ class RecurlySubscription {
|
|||
this.periodStart = props.periodStart
|
||||
this.periodEnd = props.periodEnd
|
||||
this.createdAt = props.createdAt
|
||||
this.collectionMethod = props.collectionMethod
|
||||
this.pendingChange = props.pendingChange ?? null
|
||||
}
|
||||
|
||||
|
@ -246,6 +248,15 @@ class RecurlySubscription {
|
|||
planCode: newPlanCode,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this subscription is manually collected
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
get isCollectionMethodManual() {
|
||||
return this.collectionMethod === 'manual'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,7 @@ import ErrorController from '../Errors/ErrorController.js'
|
|||
import UserGetter from '../User/UserGetter.js'
|
||||
import { Subscription } from '../../models/Subscription.js'
|
||||
import { isProfessionalGroupPlan } from './PlansHelper.mjs'
|
||||
import { MissingBillingInfoError } from './Errors.js'
|
||||
import { MissingBillingInfoError, ManuallyCollectedError } from './Errors.js'
|
||||
import RecurlyClient from './RecurlyClient.js'
|
||||
|
||||
/**
|
||||
|
@ -126,11 +126,14 @@ async function _removeUserFromGroup(
|
|||
async function addSeatsToGroupSubscription(req, res) {
|
||||
try {
|
||||
const userId = SessionManager.getLoggedInUserId(req.session)
|
||||
const { subscription, plan } =
|
||||
const { subscription, recurlySubscription, plan } =
|
||||
await SubscriptionGroupHandler.promises.getUsersGroupSubscriptionDetails(
|
||||
userId
|
||||
)
|
||||
await SubscriptionGroupHandler.promises.ensureFlexibleLicensingEnabled(plan)
|
||||
await SubscriptionGroupHandler.promises.ensureSubscriptionCollectionMethodIsNotManual(
|
||||
recurlySubscription
|
||||
)
|
||||
// Check if the user has missing billing details
|
||||
await RecurlyClient.promises.getPaymentMethod(userId)
|
||||
await SubscriptionGroupHandler.promises.ensureSubscriptionIsActive(
|
||||
|
@ -155,6 +158,12 @@ async function addSeatsToGroupSubscription(req, res) {
|
|||
)
|
||||
}
|
||||
|
||||
if (error instanceof ManuallyCollectedError) {
|
||||
return res.redirect(
|
||||
'/user/subscription/group/manually-collected-subscription'
|
||||
)
|
||||
}
|
||||
|
||||
return res.redirect('/user/subscription')
|
||||
}
|
||||
}
|
||||
|
@ -268,6 +277,12 @@ async function subscriptionUpgradePage(req, res) {
|
|||
)
|
||||
}
|
||||
|
||||
if (error instanceof ManuallyCollectedError) {
|
||||
return res.redirect(
|
||||
'/user/subscription/group/manually-collected-subscription'
|
||||
)
|
||||
}
|
||||
|
||||
return res.redirect('/user/subscription')
|
||||
}
|
||||
}
|
||||
|
@ -301,6 +316,24 @@ async function missingBillingInformation(req, res) {
|
|||
}
|
||||
}
|
||||
|
||||
async function manuallyCollectedSubscription(req, res) {
|
||||
try {
|
||||
const userId = SessionManager.getLoggedInUserId(req.session)
|
||||
const subscription =
|
||||
await SubscriptionLocator.promises.getUsersSubscription(userId)
|
||||
|
||||
res.render('subscriptions/manually-collected-subscription', {
|
||||
groupName: subscription.teamName,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.err(
|
||||
{ error },
|
||||
'error trying to render manually collected subscription page'
|
||||
)
|
||||
return res.render('/user/subscription')
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
removeUserFromGroup: expressify(removeUserFromGroup),
|
||||
removeSelfFromGroup: expressify(removeSelfFromGroup),
|
||||
|
@ -316,4 +349,5 @@ export default {
|
|||
subscriptionUpgradePage: expressify(subscriptionUpgradePage),
|
||||
upgradeSubscription: expressify(upgradeSubscription),
|
||||
missingBillingInformation: expressify(missingBillingInformation),
|
||||
manuallyCollectedSubscription: expressify(manuallyCollectedSubscription),
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ const PlansLocator = require('./PlansLocator')
|
|||
const SubscriptionHandler = require('./SubscriptionHandler')
|
||||
const GroupPlansData = require('./GroupPlansData')
|
||||
const { MEMBERS_LIMIT_ADD_ON_CODE } = require('./RecurlyEntities')
|
||||
const { ManuallyCollectedError } = require('./Errors')
|
||||
|
||||
async function removeUserFromGroup(subscriptionId, userIdToRemove) {
|
||||
await SubscriptionUpdater.promises.removeUserFromGroup(
|
||||
|
@ -68,6 +69,19 @@ async function ensureSubscriptionIsActive(subscription) {
|
|||
}
|
||||
}
|
||||
|
||||
async function ensureSubscriptionCollectionMethodIsNotManual(
|
||||
recurlySubscription
|
||||
) {
|
||||
if (recurlySubscription.isCollectionMethodManual) {
|
||||
throw new ManuallyCollectedError(
|
||||
'This subscription is being collected manually',
|
||||
{
|
||||
recurlySubscription_id: recurlySubscription.id,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function getUsersGroupSubscriptionDetails(userId) {
|
||||
const subscription =
|
||||
await SubscriptionLocator.promises.getUsersSubscription(userId)
|
||||
|
@ -99,6 +113,8 @@ async function _addSeatsSubscriptionChange(userId, adding) {
|
|||
await getUsersGroupSubscriptionDetails(userId)
|
||||
await ensureFlexibleLicensingEnabled(plan)
|
||||
await ensureSubscriptionIsActive(subscription)
|
||||
await ensureSubscriptionCollectionMethodIsNotManual(recurlySubscription)
|
||||
|
||||
const currentAddonQuantity =
|
||||
recurlySubscription.addOns.find(
|
||||
addOn => addOn.code === MEMBERS_LIMIT_ADD_ON_CODE
|
||||
|
@ -207,6 +223,8 @@ async function _getGroupPlanUpgradeChangeRequest(ownerId) {
|
|||
olSubscription.recurlySubscription_id
|
||||
)
|
||||
|
||||
await ensureSubscriptionCollectionMethodIsNotManual(recurlySubscription)
|
||||
|
||||
return recurlySubscription.getRequestForGroupPlanUpgrade(newPlanCode)
|
||||
}
|
||||
|
||||
|
@ -244,6 +262,9 @@ module.exports = {
|
|||
replaceUserReferencesInGroups: callbackify(replaceUserReferencesInGroups),
|
||||
ensureFlexibleLicensingEnabled: callbackify(ensureFlexibleLicensingEnabled),
|
||||
ensureSubscriptionIsActive: callbackify(ensureSubscriptionIsActive),
|
||||
ensureSubscriptionCollectionMethodIsNotManual: callbackify(
|
||||
ensureSubscriptionCollectionMethodIsNotManual
|
||||
),
|
||||
getTotalConfirmedUsersInGroup: callbackify(getTotalConfirmedUsersInGroup),
|
||||
isUserPartOfGroup: callbackify(isUserPartOfGroup),
|
||||
getGroupPlanUpgradePreview: callbackify(getGroupPlanUpgradePreview),
|
||||
|
@ -253,6 +274,7 @@ module.exports = {
|
|||
replaceUserReferencesInGroups,
|
||||
ensureFlexibleLicensingEnabled,
|
||||
ensureSubscriptionIsActive,
|
||||
ensureSubscriptionCollectionMethodIsNotManual,
|
||||
getTotalConfirmedUsersInGroup,
|
||||
isUserPartOfGroup,
|
||||
getUsersGroupSubscriptionDetails,
|
||||
|
|
|
@ -127,6 +127,14 @@ export default {
|
|||
SubscriptionGroupController.missingBillingInformation
|
||||
)
|
||||
|
||||
webRouter.get(
|
||||
'/user/subscription/group/manually-collected-subscription',
|
||||
AuthenticationController.requireLogin(),
|
||||
RateLimiterMiddleware.rateLimit(subscriptionRateLimiter),
|
||||
SubscriptionGroupController.flexibleLicensingSplitTest,
|
||||
SubscriptionGroupController.manuallyCollectedSubscription
|
||||
)
|
||||
|
||||
// Team invites
|
||||
webRouter.get(
|
||||
'/subscription/invites/:token/',
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
extends ../layout-marketing
|
||||
|
||||
block vars
|
||||
- bootstrap5PageStatus = 'enabled' // Enforce BS5 version
|
||||
|
||||
block entrypointVar
|
||||
- entrypoint = 'pages/user/subscription/group-management/manually-collected-subscription'
|
||||
|
||||
block append meta
|
||||
meta(name="ol-groupName", data-type="string", content=groupName)
|
||||
|
||||
block content
|
||||
main.content.content-alt#manually-collected-subscription-root
|
|
@ -45,6 +45,7 @@
|
|||
"access_denied": "",
|
||||
"access_edit_your_projects": "",
|
||||
"access_levels_changed": "",
|
||||
"account_billed_manually": "",
|
||||
"account_has_been_link_to_institution_account": "",
|
||||
"account_has_past_due_invoice_change_plan_warning": "",
|
||||
"account_managed_by_group_administrator": "",
|
||||
|
@ -804,6 +805,7 @@
|
|||
"is_email_affiliated": "",
|
||||
"issued_on": "",
|
||||
"it_looks_like_that_didnt_work_you_can_try_again_or_get_in_touch": "",
|
||||
"it_looks_like_your_account_is_billed_manually": "",
|
||||
"it_looks_like_your_payment_details_are_missing_please_update_your_billing_information": "",
|
||||
"join_beta_program": "",
|
||||
"join_now": "",
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import getMeta from '@/utils/meta'
|
||||
import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
|
||||
import { Card as BSCard, CardBody, Col, Row } from 'react-bootstrap-5'
|
||||
import IconButton from '@/features/ui/components/bootstrap-5/icon-button'
|
||||
|
||||
type CardProps = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function Card({ children }: CardProps) {
|
||||
const { t } = useTranslation()
|
||||
const groupName = getMeta('ol-groupName')
|
||||
const { isReady } = useWaitForI18n()
|
||||
|
||||
if (!isReady) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<Row>
|
||||
<Col xl={{ span: 4, offset: 4 }} md={{ span: 6, offset: 3 }}>
|
||||
<div className="group-heading" data-testid="group-heading">
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
href="/user/subscription"
|
||||
size="lg"
|
||||
icon="arrow_back"
|
||||
accessibilityLabel={t('back_to_subscription')}
|
||||
/>
|
||||
<h2>{groupName || t('group_subscription')}</h2>
|
||||
</div>
|
||||
<BSCard>
|
||||
<CardBody>{children}</CardBody>
|
||||
</BSCard>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Card
|
|
@ -0,0 +1,28 @@
|
|||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import OLNotification from '@/features/ui/components/ol/ol-notification'
|
||||
import Card from '@/features/group-management/components/card'
|
||||
|
||||
function ManuallyCollectedSubscription() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<OLNotification
|
||||
type="error"
|
||||
title={t('account_billed_manually')}
|
||||
content={
|
||||
<Trans
|
||||
i18nKey="it_looks_like_your_account_is_billed_manually"
|
||||
components={[
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a href="/contact" rel="noreferrer noopener" />,
|
||||
]}
|
||||
/>
|
||||
}
|
||||
className="m-0"
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default ManuallyCollectedSubscription
|
|
@ -1,50 +1,29 @@
|
|||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Card, CardBody, Row, Col } from 'react-bootstrap-5'
|
||||
import getMeta from '@/utils/meta'
|
||||
import IconButton from '@/features/ui/components/bootstrap-5/icon-button'
|
||||
import OLNotification from '@/features/ui/components/ol/ol-notification'
|
||||
import Card from '@/features/group-management/components/card'
|
||||
|
||||
function MissingBillingInformation() {
|
||||
const { t } = useTranslation()
|
||||
const groupName = getMeta('ol-groupName')
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<Row>
|
||||
<Col xl={{ span: 4, offset: 4 }} md={{ span: 6, offset: 3 }}>
|
||||
<div className="group-heading" data-testid="group-heading">
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
href="/user/subscription"
|
||||
size="lg"
|
||||
icon="arrow_back"
|
||||
accessibilityLabel={t('back_to_subscription')}
|
||||
/>
|
||||
<h2>{groupName || t('group_subscription')}</h2>
|
||||
</div>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<OLNotification
|
||||
type="error"
|
||||
title={t('missing_payment_details')}
|
||||
content={
|
||||
<Trans
|
||||
i18nKey="it_looks_like_your_payment_details_are_missing_please_update_your_billing_information"
|
||||
components={[
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a href="/user/subscription" rel="noreferrer noopener" />,
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a href="/contact" rel="noreferrer noopener" />,
|
||||
]}
|
||||
/>
|
||||
}
|
||||
className="m-0"
|
||||
/>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<Card>
|
||||
<OLNotification
|
||||
type="error"
|
||||
title={t('missing_payment_details')}
|
||||
content={
|
||||
<Trans
|
||||
i18nKey="it_looks_like_your_payment_details_are_missing_please_update_your_billing_information"
|
||||
components={[
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a href="/user/subscription" rel="noreferrer noopener" />,
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a href="/contact" rel="noreferrer noopener" />,
|
||||
]}
|
||||
/>
|
||||
}
|
||||
className="m-0"
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import '../base'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ManuallyCollectedSubscription from '@/features/group-management/components/manually-collected-subscription'
|
||||
|
||||
const element = document.getElementById('manually-collected-subscription-root')
|
||||
if (element) {
|
||||
ReactDOM.render(<ManuallyCollectedSubscription />, element)
|
||||
}
|
|
@ -51,6 +51,7 @@
|
|||
"access_edit_your_projects": "Access and edit your projects",
|
||||
"access_levels_changed": "Access levels changed",
|
||||
"account": "Account",
|
||||
"account_billed_manually": "Account billed manually",
|
||||
"account_has_been_link_to_institution_account": "Your __appName__ account on <b>__email__</b> has been linked to your <b>__institutionName__</b> institutional account.",
|
||||
"account_has_past_due_invoice_change_plan_warning": "Your account currently has a past due invoice. You will not be able to change your plan until this is resolved.",
|
||||
"account_linking": "Account Linking",
|
||||
|
@ -1061,6 +1062,7 @@
|
|||
"issued_on": "Issued: __date__",
|
||||
"it": "Italian",
|
||||
"it_looks_like_that_didnt_work_you_can_try_again_or_get_in_touch": "It looks like that didn’t work. You can try again or <0>get in touch</0> with our Support team for more help.",
|
||||
"it_looks_like_your_account_is_billed_manually": "It looks like your account is being billed manually - adding seats or upgrading your subscription can only be done by the Support team. Please <0>get in touch</0> for help.",
|
||||
"it_looks_like_your_payment_details_are_missing_please_update_your_billing_information": "It looks like your payment details are missing. Please <0>update your billing information</0>, or <1>get in touch</1> with our Support team for more help.",
|
||||
"ja": "Japanese",
|
||||
"january": "January",
|
||||
|
|
|
@ -51,6 +51,7 @@ describe('RecurlyClient', function () {
|
|||
periodStart: new Date(),
|
||||
periodEnd: new Date(),
|
||||
createdAt: new Date(),
|
||||
collectionMethod: 'automatic',
|
||||
})
|
||||
|
||||
this.recurlySubscription = {
|
||||
|
@ -81,6 +82,7 @@ describe('RecurlyClient', function () {
|
|||
currentPeriodStartedAt: this.subscription.periodStart,
|
||||
currentPeriodEndsAt: this.subscription.periodEnd,
|
||||
createdAt: this.subscription.createdAt,
|
||||
collectionMethod: this.subscription.collectionMethod,
|
||||
}
|
||||
|
||||
this.recurlySubscriptionChange = new recurly.SubscriptionChange()
|
||||
|
|
|
@ -373,6 +373,7 @@ describe('RecurlyEntities', function () {
|
|||
periodStart: new Date(),
|
||||
periodEnd: new Date(),
|
||||
createdAt: new Date(),
|
||||
collectionMethod: 'automatic',
|
||||
})
|
||||
const change = new RecurlySubscriptionChange({
|
||||
subscription,
|
||||
|
|
|
@ -57,6 +57,7 @@ describe('SubscriptionGroupController', function () {
|
|||
.resolves(this.createSubscriptionChangeData),
|
||||
ensureFlexibleLicensingEnabled: sinon.stub().resolves(),
|
||||
ensureSubscriptionIsActive: sinon.stub().resolves(),
|
||||
ensureSubscriptionCollectionMethodIsNotManual: sinon.stub().resolves(),
|
||||
getGroupPlanUpgradePreview: sinon
|
||||
.stub()
|
||||
.resolves(this.previewSubscriptionChangeData),
|
||||
|
@ -109,6 +110,7 @@ describe('SubscriptionGroupController', function () {
|
|||
this.RecurlyClient = {
|
||||
promises: {
|
||||
getPaymentMethod: sinon.stub().resolves(this.paymentMethod),
|
||||
// getSubscription: sinon.stub().resolves(this.subscription),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -122,6 +124,7 @@ describe('SubscriptionGroupController', function () {
|
|||
|
||||
this.Errors = {
|
||||
MissingBillingInfoError: class MissingBillingInfoError extends Error {},
|
||||
ManuallyCollectedError: class ManuallyCollectedError extends Error {},
|
||||
}
|
||||
|
||||
this.Controller = await esmock.strict(modulePath, {
|
||||
|
@ -408,6 +411,22 @@ describe('SubscriptionGroupController', function () {
|
|||
this.Controller.addSeatsToGroupSubscription(this.req, res)
|
||||
})
|
||||
|
||||
it('should redirect to manually collected subscription error page when collection method is manual', function (done) {
|
||||
this.SubscriptionGroupHandler.promises.ensureSubscriptionCollectionMethodIsNotManual =
|
||||
sinon.stub().throws(new this.Errors.ManuallyCollectedError())
|
||||
|
||||
const res = {
|
||||
redirect: url => {
|
||||
url.should.equal(
|
||||
'/user/subscription/group/manually-collected-subscription'
|
||||
)
|
||||
done()
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.addSeatsToGroupSubscription(this.req, res)
|
||||
})
|
||||
|
||||
it('should redirect to subscription page when subscription is not active', function (done) {
|
||||
this.SubscriptionGroupHandler.promises.ensureSubscriptionIsActive = sinon
|
||||
.stub()
|
||||
|
@ -598,13 +617,32 @@ describe('SubscriptionGroupController', function () {
|
|||
})
|
||||
|
||||
it('should redirect to missing billing information page when billing information is missing', function (done) {
|
||||
this.RecurlyClient.promises.getPaymentMethod = sinon
|
||||
this.SubscriptionGroupHandler.promises.getGroupPlanUpgradePreview = sinon
|
||||
.stub()
|
||||
.throws(new this.Errors.MissingBillingInfoError())
|
||||
|
||||
const res = {
|
||||
redirect: url => {
|
||||
url.should.equal('/user/subscription')
|
||||
url.should.equal(
|
||||
'/user/subscription/group/missing-billing-information'
|
||||
)
|
||||
done()
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.subscriptionUpgradePage(this.req, res)
|
||||
})
|
||||
|
||||
it('should redirect to manually collected subscription error page when collection method is manual', function (done) {
|
||||
this.SubscriptionGroupHandler.promises.getGroupPlanUpgradePreview = sinon
|
||||
.stub()
|
||||
.throws(new this.Errors.ManuallyCollectedError())
|
||||
|
||||
const res = {
|
||||
redirect: url => {
|
||||
url.should.equal(
|
||||
'/user/subscription/group/manually-collected-subscription'
|
||||
)
|
||||
done()
|
||||
},
|
||||
}
|
||||
|
|
|
@ -543,6 +543,28 @@ describe('SubscriptionGroupHandler', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('ensureSubscriptionCollectionMethodIsNotManual', function () {
|
||||
it('should throw if the subscription is manually collected', async function () {
|
||||
await expect(
|
||||
this.Handler.promises.ensureSubscriptionCollectionMethodIsNotManual({
|
||||
get isCollectionMethodManual() {
|
||||
return true
|
||||
},
|
||||
})
|
||||
).to.be.rejectedWith('This subscription is being collected manually')
|
||||
})
|
||||
|
||||
it('should not throw if the subscription is automatically collected', async function () {
|
||||
await expect(
|
||||
this.Handler.promises.ensureSubscriptionCollectionMethodIsNotManual({
|
||||
get isCollectionMethodManual() {
|
||||
return false
|
||||
},
|
||||
})
|
||||
).to.not.be.rejected
|
||||
})
|
||||
})
|
||||
|
||||
describe('upgradeGroupPlan', function () {
|
||||
it('should upgrade the subscription for flexible licensing group plans', async function () {
|
||||
this.SubscriptionLocator.promises.getUsersSubscription = sinon
|
||||
|
|
Loading…
Add table
Reference in a new issue