function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Mony Gueorguiev 11Mony Gueorguiev 11 

Help! - Trigger for Portal Deactivation based on Field Update!

So I have tried and tried...everything from the process builder to being on the phone with SF support. Nothing has worked, but yet I need to figure something out.

I want to set up a trigger that does the following:

On an Account, say Company XYZ, we have a field called Type. If we set that to Ex-Customer I would like to have every contact that has portal access under that Account to have their portal access deactivated or in general their SF profile frozen/deactivated. Whatever functionality is easier, it doesnt matter to me. I cannot seem to make this happen! This would help us immensely! 

This is the trigger I have so far...

for(User u : [Select u.Id, u.IsActive, u.IsPortalEnabled from User u where u.ContactId in :cIds]){ if(u.IsActive || u.IsPortalEnabled ){ u.IsActive = false; u.IsPortalEnabled = false; usersToUpdate.add(u); } }

Please let me know what else I need to add in order to make the Ex-Customer connection and the deactivation of the user/profile/portal.

Thank you
Best Answer chosen by Mony Gueorguiev 11
Austin MuellerAustin Mueller
You will have to use a trigger and an apex class, this will allow you to update the user records.

Apex Class
public class AccountUpdater {
    
    @future
    public static void exCustomers( List<Id> accts ) {

        List< Contact > c = [SELECT Id FROM Contact WHERE AccountId IN :accts];

        if ( !c.isEmpty() ) {

            List< User > usersToUpdate = new List< User >();

            for ( User u : [SELECT Id FROM User WHERE ContactId IN :c AND ( IsActive = true )] ) {

                u.IsActive = false;

                usersToUpdate.add( u );

            }

            if ( !usersToUpdate.isEmpty() ) update usersToUpdate;

        }

    }

}

Apex Trigger
trigger ExCustomers on Account ( after update ) {

    List< Id > accts = new List< Id > ();

    for ( Account a : Trigger.new ) {

        Account oldAcct = Trigger.oldMap.get( a.Id );

        if ( a.Type == 'Ex-Customer' && oldAcct.Type != 'Ex-Customer' ) {

            accts.add( a.Id );

        }

    }

    if( !accts.isEmpty() ) {

        AccountUpdater.exCustomers( accts );

    }

}


 

All Answers

Austin MuellerAustin Mueller
What issue are you having when you try to inactivate the accounts? 

Something like this might work
List<User> usersToUpdate = new List<User>();

for( User u : [SELECT Id FROM User WHERE ContactId IN :cIds AND (IsActive = true OR IsPortalEnabled = true)] ) {
    
    u.IsPortalEnabled = false;
    u.IsActive = false;

    usersToUpdate.add(u);

}

if( !usersToUpdate.isEmpty() ) {
    
    update usersToUpdate;

}

 
Mony Gueorguiev 11Mony Gueorguiev 11
There is no issue with the triggers its just not making a connection between setting the Account Type field to Ex-Customer and then deactivating everyone who is under the Account as a Contact. 

Using the process builder, I get a lot of error emails and also the deactivation simply does not happen. 

Does the trigger that you posted account for changing the Account Type that the User/Contact belongs to, to Ex-Customer? 
Austin MuellerAustin Mueller
Just to make sure, are you changing the Account Record Type or the Account Type picklist value?
Mony Gueorguiev 11Mony Gueorguiev 11
When I tried impleming this trigger to my User object just now, I recieved this error....

Error: Compile Error: unexpected token: List at line 1 column 0
Austin MuellerAustin Mueller
This could work if I understand what you are trying to do correctly.
 
trigger ExCustomers on Account ( after update ) {

    List< Id > accts = new List< Id > ();

    for ( Account a : Trigger.new ) {

        Account oldAcct = Trigger.oldMap.get( a.Id );

        if ( a.Type == 'Ex-Customer' && oldAcct.Type != 'Ex-Customer' ) {

            accts.add( a.Id );

        }

    }

    if( !accts.isEmpty() ) {

        List< Contact > c = [SELECT Id FROM Contact WHERE AccountId IN :accts];

        if ( !c.isEmpty() ) {

            List< User > usersToUpdate = new List< User >();

            for ( User u : [SELECT Id FROM User WHERE ContactId IN :c AND ( IsActive = true )] ) {

                u.IsActive = false;

                usersToUpdate.add( u );

            }

            if ( !usersToUpdate.isEmpty() ) update usersToUpdate;

        }

    }

}

 
Mony Gueorguiev 11Mony Gueorguiev 11
So i just ran this trigger on my Account object. When I tested it in Sandbox, with a dummy Account set up and set it to Ex-Customer I recieved the following message:

Error:Apex trigger ExCustomers caused an unexpected exception, contact your administrator: ExCustomers: execution of AfterUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 00521000000Zgg0AAC; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Account: []: ()


Which I have never seen before.
Austin MuellerAustin Mueller
You will have to use a trigger and an apex class, this will allow you to update the user records.

Apex Class
public class AccountUpdater {
    
    @future
    public static void exCustomers( List<Id> accts ) {

        List< Contact > c = [SELECT Id FROM Contact WHERE AccountId IN :accts];

        if ( !c.isEmpty() ) {

            List< User > usersToUpdate = new List< User >();

            for ( User u : [SELECT Id FROM User WHERE ContactId IN :c AND ( IsActive = true )] ) {

                u.IsActive = false;

                usersToUpdate.add( u );

            }

            if ( !usersToUpdate.isEmpty() ) update usersToUpdate;

        }

    }

}

Apex Trigger
trigger ExCustomers on Account ( after update ) {

    List< Id > accts = new List< Id > ();

    for ( Account a : Trigger.new ) {

        Account oldAcct = Trigger.oldMap.get( a.Id );

        if ( a.Type == 'Ex-Customer' && oldAcct.Type != 'Ex-Customer' ) {

            accts.add( a.Id );

        }

    }

    if( !accts.isEmpty() ) {

        AccountUpdater.exCustomers( accts );

    }

}


 
This was selected as the best answer
Mony Gueorguiev 11Mony Gueorguiev 11
Ah I see, I just started looking into the error. 

So I wouldnt create this from Settings - Customize - Account - Trigger....this would be from the Developer Console and I would need to create an Apex Class and an Apex Trigger from the console right?
Austin MuellerAustin Mueller
No, you can update the trigger the same way you are already doing it.

For the Apex Class, you can just go to Develop - Apex Classes - New
Mony Gueorguiev 11Mony Gueorguiev 11
Ok so the only error I get now is this.... 

Error: Compile Error: Variable does not exist: AccountUpdater at line 19 column 9
Austin MuellerAustin Mueller
Replace
 
if( !accts.isEmpty() ) {

        AccountUpdater.exCustomers( accts );

}


Update it with this
if( !accts.isEmpty() ) {

        AccountUpdater updater = new AccountUpdater();

        updater.exCustomers( accts );

 }

 
Mony Gueorguiev 11Mony Gueorguiev 11
I still get this error, Error: Compile Error: Invalid type: AccountUpdater at line 19 column 38

Ive attached a screenshot of what I have.

Error
Austin MuellerAustin Mueller
Have you created the Apex Class?
Mony Gueorguiev 11Mony Gueorguiev 11
Yes i did. Through the Developer Console. 

This is what I get now when I tried the trigger in the Account object, with the Class already created...User-added image
Mony Gueorguiev 11Mony Gueorguiev 11
Everything should be written out just like you have it too
Austin MuellerAustin Mueller
I changed it back to and it saved without issue
 
if( !accts.isEmpty() ) {

        AccountUpdater.exCustomers( accts );

}

 
Mony Gueorguiev 11Mony Gueorguiev 11
Ok, so this is what I savedUser-added image
Mony Gueorguiev 11Mony Gueorguiev 11
I think that actually worked! at least in Sanbox! Which is absolutely exciting for me!! 

How would I write the test class for this thing so i can push it out to production??!
Mony Gueorguiev 11Mony Gueorguiev 11
Or is the test class the Apex class you wrote above for the trigger?
Austin MuellerAustin Mueller
No, a test class will need to be made! I can work on something so you can test it.
Mony Gueorguiev 11Mony Gueorguiev 11
Oh ok, sorry im totally new to this! Im still trying to figure out how to write these things out but this has been a long time in the making and no one has helped as far as you have!!
Austin MuellerAustin Mueller
Try this test class and let me know if it works for ya!
 
@isTest
private class AccountUpdaterTest {

    @isTest(SeeAllData=true)
    static void testExCustomer() {
        
        List< Account > a = [SELECT Id FROM Account WHERE Type != 'Ex-Customer' LIMIT 1];
        
        if( !a.isEmpty() ) {
        
            a[0].Type = 'Ex-Customer';
            update a;
            
        }
    
    }

}

 
Mony Gueorguiev 11Mony Gueorguiev 11
Hey thats awesome! Thank you. 

I got 100% coverage. 

When i tried to deploy this in production through change sets, it got the following error (attached).

I pushed over both the Apex Class with the test and the Apex Trigger.User-added image
Mony Gueorguiev 11Mony Gueorguiev 11
@isTest

public class AccountUpdaterTest {

    @isTest(SeeAllData=true)

    static void testExCustomer() {

        List< Account > a = [SELECT Id FROM Account WHERE Type != 'Ex-Customer' LIMIT 1];

        if( !a.isEmpty() ) {

            a[0].Type = 'Ex-Customer';
            
            update a;

        }

    }

}


This is what i pasted for the Apex Class test class in the developer console
Austin MuellerAustin Mueller
What items are in the change set?
Mony Gueorguiev 11Mony Gueorguiev 11
Hey, I used ExCustomer and AccountUpdaterTest

Ive attached an imageUser-added image
Austin MuellerAustin Mueller
You will need to include the following:

Apex Classes:
AccountUpdater
AccountUpdaterTest

Apex Triggers:
ExCustomer
Mony Gueorguiev 11Mony Gueorguiev 11
AH! Almost! Only 74% got covered in deployment!

I dont know why. The error is very long.... I've attached a screenshot...so close!!!

User-added image
Mony Gueorguiev 11Mony Gueorguiev 11
This is after including all of the required Classes and the Trigger.
Austin MuellerAustin Mueller
When deploying, instead of running all of the test classes, just run AccountUpdaterTest as the test class.
Austin MuellerAustin Mueller
Just a reminder, this is using the data that is already exisiting. It will choose an account that is not equal to 'Ex-Customer' for the test data. If the account does not have a contact record or no users are linked to those contacts, it may fall below the test coverage.
Mony Gueorguiev 11Mony Gueorguiev 11
Ok so just to make sure I'm understanding this right...I'm pushing everything through from Sandbox to Production...ExCustomers, AccountUpdaterTest, and AccountUpdater but only deploying ExCustomers and AccountUpdaterTest in production? In other words, it wont be a default deployment, I'll have to select which ones to do? 

I thoroughly appreciate your help
Mony Gueorguiev 11Mony Gueorguiev 11
Im not sure what other way I can select only 2 of the 3 things to deploy...I've never had to do that before
Austin MuellerAustin Mueller
All 3 items in the change set will be deployed, instead of running all default tests, only run AccountUpdaterTest. It will use that test class as the one needed to check code coverage instead of having to run through all of your test classes.
Mony Gueorguiev 11Mony Gueorguiev 11
well basically I just deployed the AccountUpdaterTest and ExCustomer but uploaded all 3. I just used a custom deployment and selected only those 2. ....I think it works. Deployment was successful actually and I just tested this on our dummy company with contacts and it all flowed perfectly. Im going to test it again because I just cant believe its working!!!!!!
Austin MuellerAustin Mueller
That's awesome, let me know if you run into any issues!
Mony Gueorguiev 11Mony Gueorguiev 11
Absolutely, thank you! I appreciate all of the help, this will save us a ton of time now.