|
|
@@ -1,122 +1,112 @@
|
|
|
const { db, ObjectId } = require('../../infrastructure/mongodb')
|
|
|
const OError = require('@overleaf/o-error')
|
|
|
const async = require('async')
|
|
|
-const { promisifyAll } = require('../../util/promises')
|
|
|
+const { promisify, callbackify } = require('../../util/promises')
|
|
|
const { Subscription } = require('../../models/Subscription')
|
|
|
const SubscriptionLocator = require('./SubscriptionLocator')
|
|
|
const UserGetter = require('../User/UserGetter')
|
|
|
const PlansLocator = require('./PlansLocator')
|
|
|
const FeaturesUpdater = require('./FeaturesUpdater')
|
|
|
+const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
|
|
const { DeletedSubscription } = require('../../models/DeletedSubscription')
|
|
|
+const logger = require('logger-sharelatex')
|
|
|
|
|
|
-const SubscriptionUpdater = {
|
|
|
- /**
|
|
|
- * Change the admin of the given subscription.
|
|
|
- *
|
|
|
- * If the subscription is a group, add the new admin as manager while keeping
|
|
|
- * the old admin. Otherwise, replace the manager.
|
|
|
- *
|
|
|
- * Validation checks are assumed to have been made:
|
|
|
- * * subscription exists
|
|
|
- * * user exists
|
|
|
- * * user does not have another subscription
|
|
|
- * * subscription is not a Recurly subscription
|
|
|
- *
|
|
|
- * If the subscription is Recurly, we silently do nothing.
|
|
|
- */
|
|
|
- updateAdmin(subscription, adminId, callback) {
|
|
|
- const query = {
|
|
|
- _id: ObjectId(subscription._id),
|
|
|
- customAccount: true,
|
|
|
- }
|
|
|
- const update = {
|
|
|
- $set: { admin_id: ObjectId(adminId) },
|
|
|
- }
|
|
|
- if (subscription.groupPlan) {
|
|
|
- update.$addToSet = { manager_ids: ObjectId(adminId) }
|
|
|
- } else {
|
|
|
- update.$set.manager_ids = [ObjectId(adminId)]
|
|
|
- }
|
|
|
- Subscription.updateOne(query, update, callback)
|
|
|
- },
|
|
|
+/**
|
|
|
+ * Change the admin of the given subscription.
|
|
|
+ *
|
|
|
+ * If the subscription is a group, add the new admin as manager while keeping
|
|
|
+ * the old admin. Otherwise, replace the manager.
|
|
|
+ *
|
|
|
+ * Validation checks are assumed to have been made:
|
|
|
+ * * subscription exists
|
|
|
+ * * user exists
|
|
|
+ * * user does not have another subscription
|
|
|
+ * * subscription is not a Recurly subscription
|
|
|
+ *
|
|
|
+ * If the subscription is Recurly, we silently do nothing.
|
|
|
+ */
|
|
|
+function updateAdmin(subscription, adminId, callback) {
|
|
|
+ const query = {
|
|
|
+ _id: ObjectId(subscription._id),
|
|
|
+ customAccount: true,
|
|
|
+ }
|
|
|
+ const update = {
|
|
|
+ $set: { admin_id: ObjectId(adminId) },
|
|
|
+ }
|
|
|
+ if (subscription.groupPlan) {
|
|
|
+ update.$addToSet = { manager_ids: ObjectId(adminId) }
|
|
|
+ } else {
|
|
|
+ update.$set.manager_ids = [ObjectId(adminId)]
|
|
|
+ }
|
|
|
+ Subscription.updateOne(query, update, callback)
|
|
|
+}
|
|
|
|
|
|
- syncSubscription(recurlySubscription, adminUserId, requesterData, callback) {
|
|
|
- if (!callback) {
|
|
|
- callback = requesterData
|
|
|
- requesterData = {}
|
|
|
- }
|
|
|
- SubscriptionLocator.getUsersSubscription(
|
|
|
- adminUserId,
|
|
|
- function (err, subscription) {
|
|
|
- if (err != null) {
|
|
|
- return callback(err)
|
|
|
- }
|
|
|
- if (subscription != null) {
|
|
|
- SubscriptionUpdater._updateSubscriptionFromRecurly(
|
|
|
+function syncSubscription(
|
|
|
+ recurlySubscription,
|
|
|
+ adminUserId,
|
|
|
+ requesterData,
|
|
|
+ callback
|
|
|
+) {
|
|
|
+ if (!callback) {
|
|
|
+ callback = requesterData
|
|
|
+ requesterData = {}
|
|
|
+ }
|
|
|
+ SubscriptionLocator.getUsersSubscription(
|
|
|
+ adminUserId,
|
|
|
+ function (err, subscription) {
|
|
|
+ if (err != null) {
|
|
|
+ return callback(err)
|
|
|
+ }
|
|
|
+ if (subscription != null) {
|
|
|
+ updateSubscriptionFromRecurly(
|
|
|
+ recurlySubscription,
|
|
|
+ subscription,
|
|
|
+ requesterData,
|
|
|
+ callback
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ _createNewSubscription(adminUserId, function (err, subscription) {
|
|
|
+ if (err != null) {
|
|
|
+ return callback(err)
|
|
|
+ }
|
|
|
+ updateSubscriptionFromRecurly(
|
|
|
recurlySubscription,
|
|
|
subscription,
|
|
|
requesterData,
|
|
|
callback
|
|
|
)
|
|
|
- } else {
|
|
|
- SubscriptionUpdater._createNewSubscription(
|
|
|
- adminUserId,
|
|
|
- function (err, subscription) {
|
|
|
- if (err != null) {
|
|
|
- return callback(err)
|
|
|
- }
|
|
|
- SubscriptionUpdater._updateSubscriptionFromRecurly(
|
|
|
- recurlySubscription,
|
|
|
- subscription,
|
|
|
- requesterData,
|
|
|
- callback
|
|
|
- )
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
+ })
|
|
|
}
|
|
|
- )
|
|
|
- },
|
|
|
-
|
|
|
- addUserToGroup(subscriptionId, userId, callback) {
|
|
|
- SubscriptionUpdater.addUsersToGroup(subscriptionId, [userId], callback)
|
|
|
- },
|
|
|
+ }
|
|
|
+ )
|
|
|
+}
|
|
|
|
|
|
- addUsersToGroup(subscriptionId, memberIds, callback) {
|
|
|
- SubscriptionUpdater.addUsersToGroupWithoutFeaturesRefresh(
|
|
|
- subscriptionId,
|
|
|
- memberIds,
|
|
|
- function (err) {
|
|
|
- if (err != null) {
|
|
|
- return callback(err)
|
|
|
- }
|
|
|
- async.map(
|
|
|
- memberIds,
|
|
|
- function (userId, cb) {
|
|
|
- FeaturesUpdater.refreshFeatures(userId, 'add-to-group', cb)
|
|
|
- },
|
|
|
- callback
|
|
|
- )
|
|
|
+function addUserToGroup(subscriptionId, userId, callback) {
|
|
|
+ Subscription.updateOne(
|
|
|
+ { _id: subscriptionId },
|
|
|
+ { $addToSet: { member_ids: userId } },
|
|
|
+ function (err) {
|
|
|
+ if (err != null) {
|
|
|
+ return callback(err)
|
|
|
}
|
|
|
- )
|
|
|
- },
|
|
|
-
|
|
|
- addUsersToGroupWithoutFeaturesRefresh(subscriptionId, memberIds, callback) {
|
|
|
- const searchOps = { _id: subscriptionId }
|
|
|
- const insertOperation = { $addToSet: { member_ids: { $each: memberIds } } }
|
|
|
-
|
|
|
- Subscription.updateOne(searchOps, insertOperation, callback)
|
|
|
- },
|
|
|
+ FeaturesUpdater.refreshFeatures(userId, 'add-to-group', function () {
|
|
|
+ callbackify(_sendUserGroupPlanCodeUserProperty)(userId, callback)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ )
|
|
|
+}
|
|
|
|
|
|
- removeUserFromGroups(filter, userId, callback) {
|
|
|
- const removeOperation = { $pull: { member_ids: userId } }
|
|
|
- Subscription.updateMany(filter, removeOperation, function (err) {
|
|
|
- if (err != null) {
|
|
|
- OError.tag(err, 'error removing user from groups', {
|
|
|
- filter,
|
|
|
- removeOperation,
|
|
|
+function removeUserFromGroup(subscriptionId, userId, callback) {
|
|
|
+ Subscription.updateOne(
|
|
|
+ { _id: subscriptionId },
|
|
|
+ { $pull: { member_ids: userId } },
|
|
|
+ function (error) {
|
|
|
+ if (error) {
|
|
|
+ OError.tag(error, 'error removing user from group', {
|
|
|
+ subscriptionId,
|
|
|
+ userId,
|
|
|
})
|
|
|
- return callback(err)
|
|
|
+ return callback(error)
|
|
|
}
|
|
|
UserGetter.getUser(userId, function (error, user) {
|
|
|
if (error) {
|
|
|
@@ -124,230 +114,288 @@ const SubscriptionUpdater = {
|
|
|
}
|
|
|
FeaturesUpdater.refreshFeatures(
|
|
|
userId,
|
|
|
- 'remove-user-from-groups',
|
|
|
- callback
|
|
|
+ 'remove-user-from-group',
|
|
|
+ function () {
|
|
|
+ callbackify(_sendUserGroupPlanCodeUserProperty)(userId, callback)
|
|
|
+ }
|
|
|
)
|
|
|
})
|
|
|
- })
|
|
|
- },
|
|
|
-
|
|
|
- removeUserFromGroup(subscriptionId, userId, callback) {
|
|
|
- SubscriptionUpdater.removeUserFromGroups(
|
|
|
- { _id: subscriptionId },
|
|
|
- userId,
|
|
|
- callback
|
|
|
- )
|
|
|
- },
|
|
|
-
|
|
|
- removeUserFromAllGroups(userId, callback) {
|
|
|
- SubscriptionLocator.getMemberSubscriptions(
|
|
|
- userId,
|
|
|
- function (error, subscriptions) {
|
|
|
- if (error) {
|
|
|
- return callback(error)
|
|
|
- }
|
|
|
- if (!subscriptions) {
|
|
|
- return callback()
|
|
|
- }
|
|
|
- const subscriptionIds = subscriptions.map(sub => sub._id)
|
|
|
- SubscriptionUpdater.removeUserFromGroups(
|
|
|
- { _id: subscriptionIds },
|
|
|
- userId,
|
|
|
- callback
|
|
|
- )
|
|
|
- }
|
|
|
- )
|
|
|
- },
|
|
|
-
|
|
|
- deleteWithV1Id(v1TeamId, callback) {
|
|
|
- Subscription.deleteOne({ 'overleaf.id': v1TeamId }, callback)
|
|
|
- },
|
|
|
-
|
|
|
- deleteSubscription(subscription, deleterData, callback) {
|
|
|
- if (callback == null) {
|
|
|
- callback = function () {}
|
|
|
}
|
|
|
- async.series(
|
|
|
- [
|
|
|
- cb =>
|
|
|
- // 1. create deletedSubscription
|
|
|
- SubscriptionUpdater._createDeletedSubscription(
|
|
|
- subscription,
|
|
|
- deleterData,
|
|
|
- cb
|
|
|
- ),
|
|
|
- cb =>
|
|
|
- // 2. remove subscription
|
|
|
- Subscription.deleteOne({ _id: subscription._id }, cb),
|
|
|
- cb =>
|
|
|
- // 3. refresh users features
|
|
|
- SubscriptionUpdater._refreshUsersFeatures(subscription, cb),
|
|
|
- ],
|
|
|
- callback
|
|
|
- )
|
|
|
- },
|
|
|
+ )
|
|
|
+}
|
|
|
|
|
|
- restoreSubscription(subscriptionId, callback) {
|
|
|
- SubscriptionLocator.getDeletedSubscription(
|
|
|
- subscriptionId,
|
|
|
- function (err, deletedSubscription) {
|
|
|
- if (err) {
|
|
|
- return callback(err)
|
|
|
- }
|
|
|
- const subscription = deletedSubscription.subscription
|
|
|
- async.series(
|
|
|
- [
|
|
|
- cb =>
|
|
|
- // 1. upsert subscription
|
|
|
- db.subscriptions.updateOne(
|
|
|
- { _id: subscription._id },
|
|
|
- subscription,
|
|
|
- { upsert: true },
|
|
|
- cb
|
|
|
- ),
|
|
|
- cb =>
|
|
|
- // 2. refresh users features. Do this before removing the
|
|
|
- // subscription so the restore can be retried if this fails
|
|
|
- SubscriptionUpdater._refreshUsersFeatures(subscription, cb),
|
|
|
- cb =>
|
|
|
- // 3. remove deleted subscription
|
|
|
- DeletedSubscription.deleteOne(
|
|
|
- { 'subscription._id': subscription._id },
|
|
|
- callback
|
|
|
- ),
|
|
|
- ],
|
|
|
- callback
|
|
|
- )
|
|
|
+function removeUserFromAllGroups(userId, callback) {
|
|
|
+ SubscriptionLocator.getMemberSubscriptions(
|
|
|
+ userId,
|
|
|
+ function (error, subscriptions) {
|
|
|
+ if (error) {
|
|
|
+ return callback(error)
|
|
|
}
|
|
|
- )
|
|
|
- },
|
|
|
-
|
|
|
- _refreshUsersFeatures(subscription, callback) {
|
|
|
- const userIds = [subscription.admin_id].concat(
|
|
|
- subscription.member_ids || []
|
|
|
- )
|
|
|
- async.mapSeries(
|
|
|
- userIds,
|
|
|
- function (userId, cb) {
|
|
|
- FeaturesUpdater.refreshFeatures(userId, 'subscription-updater', cb)
|
|
|
- },
|
|
|
- callback
|
|
|
- )
|
|
|
- },
|
|
|
-
|
|
|
- _createDeletedSubscription(subscription, deleterData, callback) {
|
|
|
- subscription.teamInvites = []
|
|
|
- subscription.invited_emails = []
|
|
|
- const filter = { 'subscription._id': subscription._id }
|
|
|
- const data = {
|
|
|
- deleterData: {
|
|
|
- deleterId: deleterData.id,
|
|
|
- deleterIpAddress: deleterData.ip,
|
|
|
- },
|
|
|
- subscription: subscription,
|
|
|
+ if (!subscriptions) {
|
|
|
+ return callback()
|
|
|
+ }
|
|
|
+ const subscriptionIds = subscriptions.map(sub => sub._id)
|
|
|
+ const removeOperation = { $pull: { member_ids: userId } }
|
|
|
+ Subscription.updateMany(
|
|
|
+ { _id: subscriptionIds },
|
|
|
+ removeOperation,
|
|
|
+ function (error) {
|
|
|
+ if (error) {
|
|
|
+ OError.tag(error, 'error removing user from groups', {
|
|
|
+ userId,
|
|
|
+ subscriptionIds,
|
|
|
+ })
|
|
|
+ return callback(error)
|
|
|
+ }
|
|
|
+ UserGetter.getUser(userId, function (error, user) {
|
|
|
+ if (error) {
|
|
|
+ return callback(error)
|
|
|
+ }
|
|
|
+ FeaturesUpdater.refreshFeatures(
|
|
|
+ userId,
|
|
|
+ 'remove-user-from-groups',
|
|
|
+ function () {
|
|
|
+ callbackify(_sendUserGroupPlanCodeUserProperty)(
|
|
|
+ userId,
|
|
|
+ callback
|
|
|
+ )
|
|
|
+ }
|
|
|
+ )
|
|
|
+ })
|
|
|
+ }
|
|
|
+ )
|
|
|
}
|
|
|
- const options = { upsert: true, new: true, setDefaultsOnInsert: true }
|
|
|
- DeletedSubscription.findOneAndUpdate(filter, data, options, callback)
|
|
|
- },
|
|
|
+ )
|
|
|
+}
|
|
|
|
|
|
- _createNewSubscription(adminUserId, callback) {
|
|
|
- const subscription = new Subscription({
|
|
|
- admin_id: adminUserId,
|
|
|
- manager_ids: [adminUserId],
|
|
|
- })
|
|
|
- subscription.save(err => callback(err, subscription))
|
|
|
- },
|
|
|
+function deleteWithV1Id(v1TeamId, callback) {
|
|
|
+ Subscription.deleteOne({ 'overleaf.id': v1TeamId }, callback)
|
|
|
+}
|
|
|
|
|
|
- _deleteAndReplaceSubscriptionFromRecurly(
|
|
|
- recurlySubscription,
|
|
|
- subscription,
|
|
|
- requesterData,
|
|
|
+function deleteSubscription(subscription, deleterData, callback) {
|
|
|
+ if (callback == null) {
|
|
|
+ callback = function () {}
|
|
|
+ }
|
|
|
+ async.series(
|
|
|
+ [
|
|
|
+ cb =>
|
|
|
+ // 1. create deletedSubscription
|
|
|
+ createDeletedSubscription(subscription, deleterData, cb),
|
|
|
+ cb =>
|
|
|
+ // 2. remove subscription
|
|
|
+ Subscription.deleteOne({ _id: subscription._id }, cb),
|
|
|
+ cb =>
|
|
|
+ // 3. refresh users features
|
|
|
+ refreshUsersFeatures(subscription, cb),
|
|
|
+ ],
|
|
|
callback
|
|
|
- ) {
|
|
|
- const adminUserId = subscription.admin_id
|
|
|
- SubscriptionUpdater.deleteSubscription(subscription, requesterData, err => {
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function restoreSubscription(subscriptionId, callback) {
|
|
|
+ SubscriptionLocator.getDeletedSubscription(
|
|
|
+ subscriptionId,
|
|
|
+ function (err, deletedSubscription) {
|
|
|
if (err) {
|
|
|
return callback(err)
|
|
|
}
|
|
|
- SubscriptionUpdater._createNewSubscription(
|
|
|
- adminUserId,
|
|
|
- (err, newSubscription) => {
|
|
|
- if (err) {
|
|
|
- return callback(err)
|
|
|
- }
|
|
|
- SubscriptionUpdater._updateSubscriptionFromRecurly(
|
|
|
- recurlySubscription,
|
|
|
- newSubscription,
|
|
|
- requesterData,
|
|
|
- callback
|
|
|
- )
|
|
|
- }
|
|
|
- )
|
|
|
- })
|
|
|
- },
|
|
|
-
|
|
|
- _updateSubscriptionFromRecurly(
|
|
|
- recurlySubscription,
|
|
|
- subscription,
|
|
|
- requesterData,
|
|
|
- callback
|
|
|
- ) {
|
|
|
- if (recurlySubscription.state === 'expired') {
|
|
|
- return SubscriptionUpdater.deleteSubscription(
|
|
|
- subscription,
|
|
|
- requesterData,
|
|
|
+ const subscription = deletedSubscription.subscription
|
|
|
+ async.series(
|
|
|
+ [
|
|
|
+ cb =>
|
|
|
+ // 1. upsert subscription
|
|
|
+ db.subscriptions.updateOne(
|
|
|
+ { _id: subscription._id },
|
|
|
+ subscription,
|
|
|
+ { upsert: true },
|
|
|
+ cb
|
|
|
+ ),
|
|
|
+ cb =>
|
|
|
+ // 2. refresh users features. Do this before removing the
|
|
|
+ // subscription so the restore can be retried if this fails
|
|
|
+ refreshUsersFeatures(subscription, cb),
|
|
|
+ cb =>
|
|
|
+ // 3. remove deleted subscription
|
|
|
+ DeletedSubscription.deleteOne(
|
|
|
+ { 'subscription._id': subscription._id },
|
|
|
+ callback
|
|
|
+ ),
|
|
|
+ ],
|
|
|
callback
|
|
|
)
|
|
|
}
|
|
|
- const updatedPlanCode = recurlySubscription.plan.plan_code
|
|
|
- const plan = PlansLocator.findLocalPlanInSettings(updatedPlanCode)
|
|
|
+ )
|
|
|
+}
|
|
|
|
|
|
- if (plan == null) {
|
|
|
- return callback(new Error(`plan code not found: ${updatedPlanCode}`))
|
|
|
+function refreshUsersFeatures(subscription, callback) {
|
|
|
+ const userIds = [subscription.admin_id].concat(subscription.member_ids || [])
|
|
|
+ async.mapSeries(
|
|
|
+ userIds,
|
|
|
+ function (userId, cb) {
|
|
|
+ FeaturesUpdater.refreshFeatures(userId, 'subscription-updater', cb)
|
|
|
+ },
|
|
|
+ callback
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function createDeletedSubscription(subscription, deleterData, callback) {
|
|
|
+ subscription.teamInvites = []
|
|
|
+ subscription.invited_emails = []
|
|
|
+ const filter = { 'subscription._id': subscription._id }
|
|
|
+ const data = {
|
|
|
+ deleterData: {
|
|
|
+ deleterId: deleterData.id,
|
|
|
+ deleterIpAddress: deleterData.ip,
|
|
|
+ },
|
|
|
+ subscription: subscription,
|
|
|
+ }
|
|
|
+ const options = { upsert: true, new: true, setDefaultsOnInsert: true }
|
|
|
+ DeletedSubscription.findOneAndUpdate(filter, data, options, callback)
|
|
|
+}
|
|
|
+
|
|
|
+function _createNewSubscription(adminUserId, callback) {
|
|
|
+ const subscription = new Subscription({
|
|
|
+ admin_id: adminUserId,
|
|
|
+ manager_ids: [adminUserId],
|
|
|
+ })
|
|
|
+ subscription.save(err => callback(err, subscription))
|
|
|
+}
|
|
|
+
|
|
|
+function _deleteAndReplaceSubscriptionFromRecurly(
|
|
|
+ recurlySubscription,
|
|
|
+ subscription,
|
|
|
+ requesterData,
|
|
|
+ callback
|
|
|
+) {
|
|
|
+ const adminUserId = subscription.admin_id
|
|
|
+ deleteSubscription(subscription, requesterData, err => {
|
|
|
+ if (err) {
|
|
|
+ return callback(err)
|
|
|
}
|
|
|
- if (!plan.groupPlan && subscription.groupPlan) {
|
|
|
- // If downgrading from group to individual plan, delete group sub and create a new one
|
|
|
- return SubscriptionUpdater._deleteAndReplaceSubscriptionFromRecurly(
|
|
|
+ _createNewSubscription(adminUserId, (err, newSubscription) => {
|
|
|
+ if (err) {
|
|
|
+ return callback(err)
|
|
|
+ }
|
|
|
+ updateSubscriptionFromRecurly(
|
|
|
recurlySubscription,
|
|
|
- subscription,
|
|
|
+ newSubscription,
|
|
|
requesterData,
|
|
|
callback
|
|
|
)
|
|
|
- }
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- subscription.recurlySubscription_id = recurlySubscription.uuid
|
|
|
- subscription.planCode = updatedPlanCode
|
|
|
+function updateSubscriptionFromRecurly(
|
|
|
+ recurlySubscription,
|
|
|
+ subscription,
|
|
|
+ requesterData,
|
|
|
+ callback
|
|
|
+) {
|
|
|
+ if (recurlySubscription.state === 'expired') {
|
|
|
+ return deleteSubscription(subscription, requesterData, callback)
|
|
|
+ }
|
|
|
+ const updatedPlanCode = recurlySubscription.plan.plan_code
|
|
|
+ const plan = PlansLocator.findLocalPlanInSettings(updatedPlanCode)
|
|
|
|
|
|
- if (plan.groupPlan) {
|
|
|
- if (!subscription.groupPlan) {
|
|
|
- subscription.member_ids = subscription.member_ids || []
|
|
|
- subscription.member_ids.push(subscription.admin_id)
|
|
|
- }
|
|
|
+ if (plan == null) {
|
|
|
+ return callback(new Error(`plan code not found: ${updatedPlanCode}`))
|
|
|
+ }
|
|
|
+ if (!plan.groupPlan && subscription.groupPlan) {
|
|
|
+ // If downgrading from group to individual plan, delete group sub and create a new one
|
|
|
+ return _deleteAndReplaceSubscriptionFromRecurly(
|
|
|
+ recurlySubscription,
|
|
|
+ subscription,
|
|
|
+ requesterData,
|
|
|
+ callback
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ subscription.recurlySubscription_id = recurlySubscription.uuid
|
|
|
+ subscription.planCode = updatedPlanCode
|
|
|
+
|
|
|
+ if (plan.groupPlan) {
|
|
|
+ if (!subscription.groupPlan) {
|
|
|
+ subscription.member_ids = subscription.member_ids || []
|
|
|
+ subscription.member_ids.push(subscription.admin_id)
|
|
|
+ }
|
|
|
|
|
|
- subscription.groupPlan = true
|
|
|
- subscription.membersLimit = plan.membersLimit
|
|
|
+ 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.
|
|
|
+ // 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) {
|
|
|
+ return callback(error)
|
|
|
+ }
|
|
|
+ refreshUsersFeatures(subscription, callback)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+async function _sendUserGroupPlanCodeUserProperty(userId) {
|
|
|
+ try {
|
|
|
+ const subscriptions =
|
|
|
+ (await SubscriptionLocator.promises.getMemberSubscriptions(userId)) || []
|
|
|
+ let bestPlanCode = null
|
|
|
+ let bestFeatures = {}
|
|
|
+ for (const subscription of subscriptions) {
|
|
|
+ const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
|
|
|
if (
|
|
|
- plan.membersLimitAddOn &&
|
|
|
- Array.isArray(recurlySubscription.subscription_add_ons)
|
|
|
+ plan &&
|
|
|
+ FeaturesUpdater.isFeatureSetBetter(plan.features, bestFeatures)
|
|
|
) {
|
|
|
- recurlySubscription.subscription_add_ons.forEach(addOn => {
|
|
|
- if (addOn.add_on_code === plan.membersLimitAddOn) {
|
|
|
- subscription.membersLimit += addOn.quantity
|
|
|
- }
|
|
|
- })
|
|
|
+ bestPlanCode = plan.planCode
|
|
|
+ bestFeatures = plan.features
|
|
|
}
|
|
|
}
|
|
|
- subscription.save(function (error) {
|
|
|
- if (error) {
|
|
|
- return callback(error)
|
|
|
- }
|
|
|
- SubscriptionUpdater._refreshUsersFeatures(subscription, callback)
|
|
|
- })
|
|
|
- },
|
|
|
+ AnalyticsManager.setUserProperty(
|
|
|
+ userId,
|
|
|
+ 'group-subscription-plan-code',
|
|
|
+ bestPlanCode
|
|
|
+ )
|
|
|
+ } catch (error) {
|
|
|
+ logger.error(
|
|
|
+ { err: error },
|
|
|
+ `Failed to update group-subscription-plan-code property for user ${userId}`
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-SubscriptionUpdater.promises = promisifyAll(SubscriptionUpdater)
|
|
|
-module.exports = SubscriptionUpdater
|
|
|
+module.exports = {
|
|
|
+ updateAdmin,
|
|
|
+ syncSubscription,
|
|
|
+ deleteSubscription,
|
|
|
+ createDeletedSubscription,
|
|
|
+ addUserToGroup,
|
|
|
+ refreshUsersFeatures,
|
|
|
+ removeUserFromGroup,
|
|
|
+ removeUserFromAllGroups,
|
|
|
+ deleteWithV1Id,
|
|
|
+ restoreSubscription,
|
|
|
+ updateSubscriptionFromRecurly,
|
|
|
+ promises: {
|
|
|
+ updateAdmin: promisify(updateAdmin),
|
|
|
+ syncSubscription: promisify(syncSubscription),
|
|
|
+ addUserToGroup: promisify(addUserToGroup),
|
|
|
+ refreshUsersFeatures: promisify(refreshUsersFeatures),
|
|
|
+ removeUserFromGroup: promisify(removeUserFromGroup),
|
|
|
+ removeUserFromAllGroups: promisify(removeUserFromAllGroups),
|
|
|
+ deleteSubscription: promisify(deleteSubscription),
|
|
|
+ createDeletedSubscription: promisify(createDeletedSubscription),
|
|
|
+ deleteWithV1Id: promisify(deleteWithV1Id),
|
|
|
+ restoreSubscription: promisify(restoreSubscription),
|
|
|
+ updateSubscriptionFromRecurly: promisify(updateSubscriptionFromRecurly),
|
|
|
+ },
|
|
|
+}
|