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:
@@ -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/',
|
||||
|
||||
Reference in New Issue
Block a user