API Name: global.UserSubscriptionHelper

var UserSubscriptionHelper = Class.create();

UserSubscriptionHelper.prototype = {
initialize: function() {

this.LICENSE_STATUS_ASSIGNED = 'assigned';
this.LICENSE_STATUS_APPROVAL_REQUIRED = 'approval_required';

this.LICENSE_SOURCE_USERSET = 'userset';

// table names
this.LICENSE_EXCLUSION_TABLE = 'sys_user_license_exclude';
this.USER_HAS_LICENSE_TABLE = 'sys_user_has_license';
this.USER_PENDING_LICENSE_TABLE = 'sys_user_pending_license';
this.LICENSE_SOURCE_TABLE = 'sys_user_license_source';

this.auditHelper = new UserSubscriptionAuditHelper();
},

doesUserHaveLicense: function(userSysID, licenseSysID) {
// This function checks if the user has license (is user a licensed user)
var userLicenseGR = new GlideRecord(this.USER_HAS_LICENSE_TABLE);
userLicenseGR.addQuery('user', userSysID);
userLicenseGR.addQuery('license', licenseSysID);
userLicenseGR.query();

return userLicenseGR.hasNext();
},

isUserAddedToLicense: function(userSysID, licenseSysID) {
// This function only checks if the user, license pair exists in the
// sys_user_has_license table
var userLicenseGR = this._getUserAddedToLicense(userSysID, licenseSysID);
return userLicenseGR.hasNext();
},

_getUserAddedToLicense: function(userSysID, licenseSysID) {
var userLicenseGR = new GlideRecord(this.USER_HAS_LICENSE_TABLE);
userLicenseGR.addQuery('user', userSysID);
userLicenseGR.addQuery('license', licenseSysID);
userLicenseGR.query();
return userLicenseGR;
},

isUserAddedToPendingLicense: function(userSysID, licenseSysID) {
// This function only checks if the user, license pair exists in the
// sys_user_pending_license table
var userLicenseGR = new GlideRecord(this.USER_PENDING_LICENSE_TABLE);
userLicenseGR.addQuery('user', userSysID);
userLicenseGR.addQuery('license', licenseSysID);
userLicenseGR.query();

return userLicenseGR.hasNext();
},

addUserToLicense: function (userSysID, licenseSysID, userSetSysID) {
if (this.isUserAddedToLicense(userSysID, licenseSysID)) {
this.addToLicenseSource(userSysID, licenseSysID, userSetSysID, false);
return;
}

var userLicenseStatus = this.canUserBeAssignedToLicense(licenseSysID, userSysID);

if (userLicenseStatus.canBeAdded) {
this.addUserToLicenseWithStatus(userSysID, licenseSysID, userSetSysID, userLicenseStatus.assignStatus);
}
},

addUserToLicenseWithStatus: function(userSysID, licenseSysID, userSetSysID, status) {

var isUserSubscribed = false;
var isUserAdded = false;

if (this.isUserAddedToLicense(userSysID, licenseSysID)) {
this.addToLicenseSource(userSysID, licenseSysID, userSetSysID, false);
return isUserAdded;
}

if (this._isUserExcludedFromLicense(userSysID, licenseSysID))
return isUserAdded;

isUserSubscribed = this._allocateUserToLicense(userSysID, licenseSysID, userSetSysID);

isUserAdded = isUserSubscribed;

if (isUserAdded)
this.addToLicenseSource(userSysID, licenseSysID, userSetSysID, false);

if (isUserSubscribed)
this._logChange(userSysID, licenseSysID, isUserSubscribed, false, false);

return isUserAdded;
},

_allocateUserToLicense: function(userSysID, licenseSysID, userSetSysID) {

var userLicenseGR = new GlideRecord(this.USER_HAS_LICENSE_TABLE);
userLicenseGR.initialize();
userLicenseGR.setValue('user', userSysID);
userLicenseGR.setValue('license', licenseSysID);

if (JSUtil.notNil(userSetSysID))
userLicenseGR.setValue('source', this.LICENSE_SOURCE_USERSET);

return (JSUtil.notNil(userLicenseGR.insert()))? true : false;

},

canUserBeAssignedToLicense: function (licenseSysID, userSysID) {

var returnObj = {}; // will contain 2 keys canBeAdded and assignStatus

if (JSUtil.nil(licenseSysID)) {
returnObj.canBeAdded = false;
return returnObj;
}

var licenseGR = this._fetchLicenseBySysID(licenseSysID);

// If not a valid license or if the user is excluded from the license
if (!licenseGR.isValidRecord() || this._isUserExcludedFromLicense(userSysID, licenseSysID)) {
returnObj.canBeAdded = false;
return returnObj;
}


// can be assigned to the license
returnObj.canBeAdded = true;
returnObj.assignStatus = this.LICENSE_STATUS_ASSIGNED;

return returnObj;
},

canUserBeSubscribedToLicense: function(licenseSysID, userSysID) {

var result = this.canUserBeAssignedToLicense(licenseSysID, userSysID);
return ((result.canBeAdded == true) && (result.assignStatus == this.LICENSE_STATUS_ASSIGNED));
},

doesUserFailEnforcementLimit: function (pendingSysIdList) {

if (JSUtil.nil(pendingSysIdList))
return false;

var pendingGR = new GlideRecord('sys_user_pending_license');
pendingGR.addQuery('sys_id', pendingSysIdList[0]);
pendingGR.query();

if (pendingGR.next()) {
var licGR = new GlideRecord('license_details');
licGR.addQuery('sys_id', pendingGR.getValue('license'));
licGR.query();

if (licGR.next()) {

var allocated = parseInt(licGR.getValue("allocated"));
var purchased = parseInt(licGR.getValue("count"));
var toBeAllocated = pendingSysIdList.length;

var slotsAvailable = purchased - allocated;

var isCapped = licGR.getValue('is_capped') == '1';
var isLicenseOverflown = allocated >= purchased;
var willLicenseOverflow = toBeAllocated > slotsAvailable;
// If user has enforcement limit turned ON
// AND allocation count is equal to or more than purchased,
// OR no. of users to be subscripbed is more than available slots,
// he is not complying with the enforcement limit of the subscription.
return (isCapped && (isLicenseOverflown || willLicenseOverflow));
}
}

return false;
},

doesAddingAListOfUsersExceedPurchasedLimit: function(/* array */userList, licenseSysID) {
var licGR = new GlideRecord('license_details');
licGR.addQuery('sys_id', licenseSysID);
licGR.query();

if (licGR.next()) {
var allocated = parseInt(licGR.getValue("allocated"));
var purchased = parseInt(licGR.getValue("count"));
var toBeAllocated = userList.length;
var slotsAvailable = purchased - allocated;

return (toBeAllocated > slotsAvailable);
}
return false;
},

_fetchLicenseBySysID: function(licSysID) {
var licGR = new GlideRecord('license_details');
licGR.get(licSysID);

return licGR;

},

addToLicenseSource: function(userSysID, licenseSysID, userSetSysID, isDirect) {

if (this._doesLicenseSourceExist(userSysID, licenseSysID, userSetSysID, isDirect)) {
return;
}

var userLicenseSourceGR = new GlideRecord(this.LICENSE_SOURCE_TABLE);
userLicenseSourceGR.initialize();
userLicenseSourceGR.setValue('user', userSysID);
userLicenseSourceGR.setValue('license', licenseSysID);

if (isDirect) {
userLicenseSourceGR.setValue('is_direct', 1);
} else {
userLicenseSourceGR.setValue('user_set_source', userSetSysID);
}

userLicenseSourceGR.insert();

},

_doesLicenseSourceExist: function(userSysID, licenseSysID, userSetSysID, isDirect) {

var userLicenseSourceGR = new GlideRecord(this.LICENSE_SOURCE_TABLE);
userLicenseSourceGR.initialize();
userLicenseSourceGR.addQuery('user', userSysID);
userLicenseSourceGR.addQuery('license', licenseSysID);

if (isDirect) {
userLicenseSourceGR.addQuery('is_direct', 1);
} else {
userLicenseSourceGR.addQuery('user_set_source', userSetSysID);
}

userLicenseSourceGR.query();

return userLicenseSourceGR.hasNext();

},

movePendingUserHasLicenseRecToSubscribed: function(pendingLicSysId) {

var skipSourceDelete = true;
var userSysId = "";
var licSysId = "";
var source = "";

var pendingLicGR = new GlideRecord(this.USER_PENDING_LICENSE_TABLE);
pendingLicGR.addQuery('sys_id', pendingLicSysId);
pendingLicGR.query();

if(pendingLicGR.next()) {
userSysId = pendingLicGR.getValue('user');
licSysId = pendingLicGR.getValue('license');
source = pendingLicGR.getValue('source');

if (JSUtil.notNil(userSysId) && JSUtil.notNil(licSysId)) {
var userLicenseGR = new GlideRecord(this.USER_HAS_LICENSE_TABLE);
userLicenseGR.initialize();
userLicenseGR.setValue('user', userSysId);
userLicenseGR.setValue('license', licSysId);
userLicenseGR.setValue('source', source);
userLicenseGR.setWorkflow(false);
userLicenseGR.insert();

this._logChange(userSysId, licSysId, true, false, false);
}

this._deleteUserFromPendingLicenseBySysId(pendingLicSysId, skipSourceDelete);
}
},

unSubscribeEligibleUser: function (userSysID, licenseSysID) {
// Un-Subscribe users with reference count == 1. DO NOT delete the reference otherwise

var isUserRemoved = false;

if ( this._getNumLicenseSourcesForAUserAndLicense(userSysID, licenseSysID) === 1) {

var isUserSubscribed = this.isUserAddedToLicense(userSysID, licenseSysID);

if (isUserSubscribed) {
this._logChange(userSysID, licenseSysID, false, true, false);
this.removeAllSourcesForUserLicense(userSysID, licenseSysID);
this._deleteUserFromLicense(userSysID, licenseSysID);
isUserRemoved = true;
}
}

return isUserRemoved;
},

dismissEligibleUserFromPending: function (userSysID, licenseSysID) {

// Dismiss pending users with reference count == 1. DO NOT delete the reference otherwise

var isUserRemoved = false;


if ( this._getNumLicenseSourcesForAUserAndLicense(userSysID, licenseSysID) === 1) {

var isUserInPending = this._isUserPendingLicense(userSysID, licenseSysID);

if (isUserInPending) {
this.removeAllSourcesForUserLicense(userSysID, licenseSysID);
this._deleteUserFromPendingLicense(userSysID, licenseSysID);
isUserRemoved = true;
}
}

return isUserRemoved;
},

removeUserFromLicense: function(userSysID, licenseSysID, removedUserSetSysID) {
// User set disassociated from the license, attempt to remove all the users
// who came to the license from this user set
return this._removeAUserFromLicense(userSysID, licenseSysID, false, false, removedUserSetSysID);
},

removeUserDirectlyFromLicense: function(userSysID, licenseSysID) {
// Called when user is removed directly removed from the UI
return this._removeAUserFromLicense(userSysID, licenseSysID, true, false, '');
},

removeAndExcludeUserFromLicense: function(userSysID, licenseSysID) {
// Removes a user from license and also excludes him from the license
return this._removeAUserFromLicense(userSysID, licenseSysID, true, true, '');
},

removeAndExcludeUserFromPendingLicense: function(userSysID, licenseSysID) {
// Removes a user from license and also excludes him from the pending license
return this._removeAUserFromPendingLicense(userSysID, licenseSysID, true, true);
},

removeUserFromPendingLicense: function(userSysID, licenseSysID) {
// Removes user from pending licene without eligibility check
return this._removeAUserFromPendingLicense(userSysID, licenseSysID, true, false);
},

_removeAUserFromPendingLicense: function(userSysID, licenseSysID, wasRemovedFromUI, excludeUser) {
// There is 1 way in which a user could be removed from a pending license
// Directly selecting the record from the sys_user_pending_license table and deleting it

var isUserRemoved = false;

if (wasRemovedFromUI) {
// Removed directly from UI - log the action, remove all the license sources, add to the exclusion table
// Finally delete the user license association
this._logChange(userSysID, licenseSysID, false, false, excludeUser);
this.removeAllSourcesForUserLicense(userSysID, licenseSysID);
this._deleteUserFromPendingLicense(userSysID, licenseSysID);
isUserRemoved = true;

if (excludeUser)
this._addUserToLicenseExclusionList(userSysID, licenseSysID);
}

return isUserRemoved;
},

_removeAUserFromLicense: function(userSysID, licenseSysID, wasRemovedFromUI, excludeUser, removedUserSetSysID) {
// There are only 2 ways in which a user could be removed from a license
// Either directly selecting the record from the sys_user_has_license table and deleting it, or by removing
// a user set associated to a license

var isUserRemoved = false;

if (wasRemovedFromUI) {
// Removed directly from UI - log the action, remove all the license sources, add to the exclusion table
// Finally delete the user license association
this._logChange(userSysID, licenseSysID, false, true, excludeUser);
this.removeAllSourcesForUserLicense(userSysID, licenseSysID);
this._deleteUserFromLicense(userSysID, licenseSysID);
isUserRemoved = true;

if (excludeUser)
this._addUserToLicenseExclusionList(userSysID, licenseSysID);

return isUserRemoved;
}


// User set dis-associated from the license
// retrieve all the sources for this user and license,
// if the only source is this userset, remove this license user association
if (this._getNumLicenseSourcesForAUserAndLicense(userSysID, licenseSysID) === 1) {

var isUserInPending = this._isUserPendingLicense(userSysID, licenseSysID);

this._logChange(userSysID, licenseSysID, false, !isUserInPending, excludeUser);

this.removeAllSourcesForUserLicense(userSysID, licenseSysID);

if (isUserInPending) {

this._deleteUserFromPendingLicense(userSysID, licenseSysID);
isUserRemoved = true;

}
else {
this._deleteUserFromLicense(userSysID, licenseSysID);
isUserRemoved = true;
}

} else {
// He has this license from multiple sources, only disassociate this source
this.removeUserSetSourceForUserLicense(userSysID, licenseSysID, removedUserSetSysID);
}

return isUserRemoved;
},

_isUserPendingLicense: function(userSysID, licenseSysID) {
var pendingLicGR = new GlideRecord(this.USER_PENDING_LICENSE_TABLE);
pendingLicGR.addQuery('user', userSysID);
pendingLicGR.addQuery('license', licenseSysID);
pendingLicGR.query();

return pendingLicGR.hasNext();
},

_deleteUserFromPendingLicense: function(userSysID, licenseSysID) {

if (!this._isUserPendingLicense(userSysID, licenseSysID))
return;

// Deletes a user from a pending license.
var usrPendingLicGR = new GlideRecord(this.USER_PENDING_LICENSE_TABLE);
usrPendingLicGR.addQuery('user', userSysID);
usrPendingLicGR.addQuery('license', licenseSysID);
usrPendingLicGR.query();

if (usrPendingLicGR.next())
usrPendingLicGR.deleteRecord();
},

_deleteUserFromPendingLicenseBySysId: function(pendingLicSysId, skipSourceDelete) {
// Deletes a user from a pending license.
var usrPendingLicGR = new GlideRecord(this.USER_PENDING_LICENSE_TABLE);
usrPendingLicGR.addQuery('sys_id', pendingLicSysId);
usrPendingLicGR.query();

if (skipSourceDelete)
usrPendingLicGR.setWorkflow(false);

if (usrPendingLicGR.next())
usrPendingLicGR.deleteRecord();
},

_getNumLicenseSourcesForAUserAndLicense: function(userSysID, licenseSysID) {
var userLicenseSourceGR = new GlideRecord(this.LICENSE_SOURCE_TABLE);
userLicenseSourceGR.addQuery('user', userSysID);
userLicenseSourceGR.addQuery('license', licenseSysID);
userLicenseSourceGR.query();

return userLicenseSourceGR.getRowCount();
},

removeUserSetSourceForUserLicense: function(userSysID, licenseSysID, sourceUserSetSysID) {
var userLicenseSourceGR = new GlideRecord(this.LICENSE_SOURCE_TABLE);
userLicenseSourceGR.addQuery('user', userSysID);
userLicenseSourceGR.addQuery('license', licenseSysID);
userLicenseSourceGR.addQuery('user_set_source', sourceUserSetSysID);
userLicenseSourceGR.query();

if (userLicenseSourceGR.next())
userLicenseSourceGR.deleteRecord();
},

removeDirectSourceForUserLicense: function(userSysID, licenseSysID, licenseSource) {
var userLicenseSourceGR = new GlideRecord(this.LICENSE_SOURCE_TABLE);
userLicenseSourceGR.addQuery('user', userSysID);
userLicenseSourceGR.addQuery('license', licenseSysID);
userLicenseSourceGR.addQuery('is_direct', '1');
userLicenseSourceGR.query();

if (userLicenseSourceGR.next())
userLicenseSourceGR.deleteRecord();
},

removeAllSourcesForUserLicense: function(userSysID, licenseSysID) {
// Removes all the licenses sources for this user and license
var userLicenseSourceGR = new GlideRecord(this.LICENSE_SOURCE_TABLE);
userLicenseSourceGR.addQuery('user', userSysID);
userLicenseSourceGR.addQuery('license', licenseSysID);
userLicenseSourceGR.query();

while (userLicenseSourceGR.next()) {
userLicenseSourceGR.deleteRecord();
}

},

_addUserToLicenseExclusionList: function(userSysID, licenseSysID) {
var userExcludeGR = new GlideRecord(this.LICENSE_EXCLUSION_TABLE);
userExcludeGR.initialize();
userExcludeGR.setValue('user', userSysID);
userExcludeGR.setValue('license', licenseSysID);
userExcludeGR.setWorkflow(false);
userExcludeGR.insert();
},

removeUserFromLicenseExclusionList: function(userSysID, licenseSysID) {

if (!this._isUserExcludedFromLicense(userSysID, licenseSysID))
return;

var userExcludeGR = new GlideRecord(this.LICENSE_EXCLUSION_TABLE);
userExcludeGR.addQuery('user', userSysID);
userExcludeGR.addQuery('license', licenseSysID);
userExcludeGR.query();

if (userExcludeGR.next())
userExcludeGR.deleteRecord();
},

_isUserExcludedFromLicense: function(userSysID, licenseSysID) {
var userExcludeGR = new GlideRecord(this.LICENSE_EXCLUSION_TABLE);
userExcludeGR.addQuery('user', userSysID);
userExcludeGR.addQuery('license', licenseSysID);
userExcludeGR.query();

return userExcludeGR.hasNext();
},

getExcludedUsersForLicense: function(licenseSysID) {
var excludedUserSysIds = [];

var userExcludeGR = new GlideRecord(this.LICENSE_EXCLUSION_TABLE);

userExcludeGR.addQuery('license', licenseSysID);
userExcludeGR.query();

while (userExcludeGR.next()) {
excludedUserSysIds.push(userExcludeGR.getValue('user'));
}

return excludedUserSysIds;
},

getUsersAllocatedForLicense: function(licenseSysID) {
var subscribedUserSysIds = [];

var subscribedUserGR = new GlideRecord(this.USER_HAS_LICENSE_TABLE);
subscribedUserGR.addQuery('license', licenseSysID);
subscribedUserGR.query();

while (subscribedUserGR.next())
subscribedUserSysIds.push(subscribedUserGR.getValue('user'));

return subscribedUserSysIds;
},

_deleteUserFromLicense: function(userSysID, licenseSysID) {
// Deletes a user from a license, does not matter what state he is in
var userLicenseGR = new GlideRecord(this.USER_HAS_LICENSE_TABLE);
userLicenseGR.addQuery('user', userSysID);
userLicenseGR.addQuery('license', licenseSysID);
userLicenseGR.addQuery('source', '!=', 'sys_entl');
userLicenseGR.query();

if (userLicenseGR.next())
userLicenseGR.deleteRecord();
},

canUserBeRemovedFromSubscription: function(userSysID, licenseSysID) {
var userLicenseGR = this._getUserAddedToLicense(userSysID, licenseSysID);
// Allowed to add a user to the excluded list if we don't have any records already...
if(!userLicenseGR.hasNext())
return true;

// users added to license with System Entitlement source should never be deleted
// if multiple source found, non System Entitlement users can be removed
var isSystemEntitlement = false;
var hasMultipleSource = false;
while (userLicenseGR.next()) {
if(userLicenseGR.getValue('source') == 'sys_entl')
isSystemEntitlement = true;
else
hasMultipleSource = true;
}
if (isSystemEntitlement && !hasMultipleSource)
return false;

return true;
},

_logChange: function(userSysId, licSysId, isAdd, isRemove, isExclude){

if (isAdd)
this.auditHelper.recordSubscribed(userSysId, licSysId);
else if (isRemove && !isExclude)
this.auditHelper.recordUnSubscribed(userSysId, licSysId);
else if (!isRemove && isExclude)
this.auditHelper.recordExcluded(userSysId, licSysId);
else if (isRemove && isExclude){
this.auditHelper.recordUnSubscribed(userSysId, licSysId);
this.auditHelper.recordExcluded(userSysId, licSysId);
}
},

type: 'UserSubscriptionHelper'
};