Move AI related functions from PaymentProviderEntities to AiHelper (#26956)

* Move AI related functions from PaymentProviderEntities to AiHelper

* added @ts-check

GitOrigin-RevId: 8c8eec334b40a7f8f8533f6d5194f428112f68f9
This commit is contained in:
Domagoj Kriskovic
2025-07-09 15:59:42 +02:00
committed by Copybot
parent 5aa9d03622
commit a9e47f043a
12 changed files with 65 additions and 64 deletions

View File

@@ -46,9 +46,7 @@ const TutorialHandler = require('../Tutorial/TutorialHandler')
const UserUpdater = require('../User/UserUpdater')
const Modules = require('../../infrastructure/Modules')
const UserGetter = require('../User/UserGetter')
const {
isStandaloneAiAddOnPlanCode,
} = require('../Subscription/PaymentProviderEntities')
const { isStandaloneAiAddOnPlanCode } = require('../Subscription/AiHelper')
const SubscriptionController = require('../Subscription/SubscriptionController.js')
const { formatCurrency } = require('../../util/currency')

View File

@@ -0,0 +1,46 @@
// @ts-check
// Initially, this functions lived in PaymentProviderEntities.js,
// but it was moved to this file to prevent circular dependency issue
const AI_ASSIST_STANDALONE_MONTHLY_PLAN_CODE = 'assistant'
const AI_ASSIST_STANDALONE_ANNUAL_PLAN_CODE = 'assistant-annual'
const AI_ADD_ON_CODE = 'assistant'
/**
* Returns whether the given plan code is a standalone AI plan
*
* @param {string} planCode
* @return {boolean}
*/
function isStandaloneAiAddOnPlanCode(planCode) {
return (
planCode === AI_ASSIST_STANDALONE_MONTHLY_PLAN_CODE ||
planCode === AI_ASSIST_STANDALONE_ANNUAL_PLAN_CODE
)
}
/**
* Returns whether subscription change will have have the ai bundle once the change is processed
*
* @param {Object} subscriptionChange The subscription change object coming from payment provider
* type should be PaymentProviderSubscriptionChange but if imported here, it creates a circular dependency
* TODO: fix this when moved to es modules
*
* @return {boolean}
*/
function subscriptionChangeIsAiAssistUpgrade(subscriptionChange) {
return Boolean(
isStandaloneAiAddOnPlanCode(subscriptionChange.nextPlanCode) ||
subscriptionChange.nextAddOns?.some(
addOn => addOn.code === AI_ADD_ON_CODE
)
)
}
module.exports = {
AI_ADD_ON_CODE,
AI_ASSIST_STANDALONE_MONTHLY_PLAN_CODE,
AI_ASSIST_STANDALONE_ANNUAL_PLAN_CODE,
isStandaloneAiAddOnPlanCode,
subscriptionChangeIsAiAssistUpgrade,
}

View File

@@ -15,7 +15,7 @@ const UserGetter = require('../User/UserGetter')
const AnalyticsManager = require('../Analytics/AnalyticsManager')
const Queues = require('../../infrastructure/Queues')
const Modules = require('../../infrastructure/Modules')
const { AI_ADD_ON_CODE } = require('./PaymentProviderEntities')
const { AI_ADD_ON_CODE } = require('./AiHelper')
/**
* Enqueue a job for refreshing features for the given user

View File

@@ -9,13 +9,9 @@
const OError = require('@overleaf/o-error')
const { DuplicateAddOnError, AddOnNotPresentError } = require('./Errors')
const PlansLocator = require('./PlansLocator')
let SubscriptionHelper = null // Work around circular import (loaded at the bottom of the file)
const SubscriptionHelper = require('./SubscriptionHelper')
const { AI_ADD_ON_CODE, isStandaloneAiAddOnPlanCode } = require('./AiHelper')
const MEMBERS_LIMIT_ADD_ON_CODE = 'additional-license'
const AI_ASSIST_STANDALONE_MONTHLY_PLAN_CODE = 'assistant'
const AI_ASSIST_STANDALONE_ANNUAL_PLAN_CODE = 'assistant-annual'
const AI_ADD_ON_CODE = 'assistant'
class PaymentProviderSubscription {
/**
@@ -588,18 +584,6 @@ class PaymentProviderAccount {
}
}
/**
* Returns whether the given plan code is a standalone AI plan
*
* @param {string} planCode
*/
function isStandaloneAiAddOnPlanCode(planCode) {
return (
planCode === AI_ASSIST_STANDALONE_MONTHLY_PLAN_CODE ||
planCode === AI_ASSIST_STANDALONE_ANNUAL_PLAN_CODE
)
}
/**
* Returns whether the given plan code is a group plan
*
@@ -609,27 +593,8 @@ function isGroupPlanCode(planCode) {
return planCode.includes('group')
}
/**
* Returns whether subscription change will have have the ai bundle once the change is processed
*
* @param {PaymentProviderSubscriptionChange} subscriptionChange The subscription change object coming from payment provider
*
* @return {boolean}
*/
function subscriptionChangeIsAiAssistUpgrade(subscriptionChange) {
return Boolean(
isStandaloneAiAddOnPlanCode(subscriptionChange.nextPlanCode) ||
subscriptionChange.nextAddOns?.some(
addOn => addOn.code === AI_ADD_ON_CODE
)
)
}
module.exports = {
AI_ADD_ON_CODE,
MEMBERS_LIMIT_ADD_ON_CODE,
AI_ASSIST_STANDALONE_MONTHLY_PLAN_CODE,
AI_ASSIST_STANDALONE_ANNUAL_PLAN_CODE,
PaymentProviderSubscription,
PaymentProviderSubscriptionAddOn,
PaymentProviderSubscriptionChange,
@@ -643,9 +608,5 @@ module.exports = {
PaymentProviderCoupon,
PaymentProviderAccount,
isGroupPlanCode,
isStandaloneAiAddOnPlanCode,
subscriptionChangeIsAiAssistUpgrade,
PaymentProviderImmediateCharge,
}
SubscriptionHelper = require('./SubscriptionHelper')

View File

@@ -1,7 +1,7 @@
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
const AnalyticsManager = require('../Analytics/AnalyticsManager')
const SubscriptionEmailHandler = require('./SubscriptionEmailHandler')
const { AI_ADD_ON_CODE } = require('./PaymentProviderEntities')
const { AI_ADD_ON_CODE } = require('./AiHelper')
const { ObjectId } = require('mongodb-legacy')
const INVOICE_SUBSCRIPTION_LIMIT = 10

View File

@@ -27,9 +27,11 @@ const Modules = require('../../infrastructure/Modules')
const async = require('async')
const HttpErrorHandler = require('../Errors/HttpErrorHandler')
const RecurlyClient = require('./RecurlyClient')
const { AI_ADD_ON_CODE } = require('./PaymentProviderEntities')
const {
AI_ADD_ON_CODE,
subscriptionChangeIsAiAssistUpgrade,
} = require('./AiHelper')
const PlansLocator = require('./PlansLocator')
const PaymentProviderEntities = require('./PaymentProviderEntities')
const { User } = require('../../models/User')
const UserGetter = require('../User/UserGetter')
const PermissionsManager = require('../Authorization/PermissionsManager')
@@ -377,10 +379,7 @@ async function previewAddonPurchase(req, res) {
const { isPremium: hasAiAssistViaWritefull } =
await UserGetter.promises.getWritefullData(userId)
const isAiUpgrade =
PaymentProviderEntities.subscriptionChangeIsAiAssistUpgrade(
subscriptionChange
)
const isAiUpgrade = subscriptionChangeIsAiAssistUpgrade(subscriptionChange)
if (hasAiAssistViaWritefull && isAiUpgrade) {
return res.redirect(
'/user/subscription?redirect-reason=writefull-entitled'

View File

@@ -1,6 +1,6 @@
const { formatCurrency } = require('../../util/currency')
const GroupPlansData = require('./GroupPlansData')
const { isStandaloneAiAddOnPlanCode } = require('./PaymentProviderEntities')
const { isStandaloneAiAddOnPlanCode } = require('./AiHelper')
/**
* If the user changes to a less expensive plan, we shouldn't apply the change immediately.

View File

@@ -6,10 +6,7 @@ const { callbackifyAll } = require('@overleaf/promise-utils')
const { Subscription } = require('../../models/Subscription')
const { DeletedSubscription } = require('../../models/DeletedSubscription')
const logger = require('@overleaf/logger')
const {
AI_ADD_ON_CODE,
isStandaloneAiAddOnPlanCode,
} = require('./PaymentProviderEntities')
const { AI_ADD_ON_CODE, isStandaloneAiAddOnPlanCode } = require('./AiHelper')
require('./GroupPlansData') // make sure dynamic group plans are loaded
const SubscriptionLocator = {

View File

@@ -1,10 +1,8 @@
// ts-check
const Settings = require('@overleaf/settings')
const PlansLocator = require('./PlansLocator')
const {
isStandaloneAiAddOnPlanCode,
MEMBERS_LIMIT_ADD_ON_CODE,
} = require('./PaymentProviderEntities')
const { isStandaloneAiAddOnPlanCode } = require('./AiHelper')
const { MEMBERS_LIMIT_ADD_ON_CODE } = require('./PaymentProviderEntities')
const SubscriptionFormatters = require('./SubscriptionFormatters')
const SubscriptionLocator = require('./SubscriptionLocator')
const InstitutionsGetter = require('../Institutions/InstitutionsGetter')

View File

@@ -4,7 +4,7 @@ const sinon = require('sinon')
const { ObjectId } = require('mongodb-legacy')
const {
AI_ADD_ON_CODE,
} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities')
} = require('../../../../app/src/Features/Subscription/AiHelper')
const MODULE_PATH = '../../../../app/src/Features/Subscription/FeaturesUpdater'

View File

@@ -4,13 +4,15 @@ const SandboxedModule = require('sandboxed-module')
const { expect } = require('chai')
const Errors = require('../../../../app/src/Features/Subscription/Errors')
const {
AI_ADD_ON_CODE,
PaymentProviderSubscriptionChangeRequest,
PaymentProviderSubscriptionUpdateRequest,
PaymentProviderSubscriptionChange,
PaymentProviderSubscription,
PaymentProviderSubscriptionAddOnUpdate,
} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities')
const {
AI_ADD_ON_CODE,
} = require('../../../../app/src/Features/Subscription/AiHelper')
const SubscriptionHelper = require('../../../../app/src/Features/Subscription/SubscriptionHelper')
const MODULE_PATH =

View File

@@ -9,7 +9,7 @@ const SubscriptionErrors = require('../../../../app/src/Features/Subscription/Er
const SubscriptionHelper = require('../../../../app/src/Features/Subscription/SubscriptionHelper')
const {
AI_ADD_ON_CODE,
} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities')
} = require('../../../../app/src/Features/Subscription/AiHelper')
const mockSubscriptions = {
'subscription-123-active': {