Apex Coding Interview Challenge #3

Coding question asked by Google

Develop a Trigger on asset to count the amount of assets for a specific account. The value of the asset should increate on insert or undeleteand decrease on delete or reparent to a new account.

Assumption: an asset cannot have a null AccountId, validation rule will fail and not save the record. Asset should always have a lookup to an account.

Apex Asset Trigger

trigger AssetTrigger on Asset (after insert, after update, after delete, after undelete) {
    
    Integer incDecVal = 1;
    if (Trigger.isDelete){
        incDecVal = -1;
    }
    
    AssetHelper.countAccountAssets(trigger.newMap, trigger.oldMap, incDecVal);
}

Apex asset helper class

public with sharing class AssetHelper {
 
     public static void countAccountAssets(Map<Id, Asset> newAssets, Map<Id, Asset> oldAssets, Integer incrementDecrementVal){
         Set<Id> accountIds = new Set<Id>();
         
         if (newAssets!=null){
            for (Asset newAsset : newAssets.values())
                 accountIds.add(newAsset.AccountId);
            
         }
         
         if (oldAssets!=null){
            for (Asset oldAsset : oldAssets.values())
               accountIds.add(oldAsset.AccountId);
         }
             
         Map<Id, Account> accountMap = new Map<Id, Account>([Select Id, Total_Assets__c from Account where Id IN :accountIds]);
         
         Set<Account> updateAccountSet = new Set<Account>();
         
         if (newAssets!=null){
           for (Id newAssetId : newAssets.keySet()){
             Asset newAsset = newAssets.get(newAssetId);
             if (accountMap.containsKey(newAsset.AccountId)){
                 Account updateAccount = accountMap.get(newAsset.AccountId);
                 updateAccount.Total_Assets__c += incrementDecrementVal;
                 
                 if (updateAccount.Total_Assets__c < 0){
                  updateAccount.Total_Assets__c = 0;
                 }
                 
                 updateAccountSet.add(updateAccount);
             }
           }
         }
         
    
         if (oldAssets!=null){
           for (Id oldAssetId : oldAssets.keySet()){
             Asset oldAsset = oldAssets.get(oldAssetId);
             
             if (accountMap.containsKey(oldAsset.AccountId)){
                 Account updateAccount = accountMap.get(oldAsset.AccountId);
                 if (newAssets!=null && newAssets.containsKey(oldAssetId)){
                     Asset newAsset = newAssets.get(oldAssetId);
                     if (oldAsset.AccountId!=newAsset.AccountId){
                         updateAccount.Total_Assets__c -= incrementDecrementVal;
                     }
                 } else {
                     updateAccount.Total_Assets__c += incrementDecrementVal;
                 }
                 
                 if (updateAccount.Total_Assets__c < 0){
                  updateAccount.Total_Assets__c = 0;
                 }
                 
                 updateAccountSet.add(updateAccount);
             }
           }
         } 
        
         Savepoint sp = Database.setSavePoint();
         try{
             Database.update(new List<Account>(updateAccountSet));
         } catch(DMLException ex){
             Database.rollback(sp);
         }
     }
}

Apex asset helper test class

@isTest public class AssetHelperTest {

    @TestSetup static void setupAssetAccount(){
        Account setupAccount = new Account(Name='Test Account', Total_Assets__c=0);
        insert setupAccount;
        
        Asset setupAsset = new Asset(AccountId=setupAccount.Id, Name='Test Asset Setup');
        insert setupAsset;
    }
    
    @isTest static void testInsertAsset(){
        System.assertEquals([Select Total_Assets__c from Account][0].Total_Assets__c, 1);
    }
    
    @isTest static void testDeleteAsset(){
        System.assertEquals([Select Total_Assets__c from Account][0].Total_Assets__c, 1);
        Test.startTest();
            delete [Select Id from Asset][0];
        Test.stopTest();
        System.assertEquals([Select Total_Assets__c from Account][0].Total_Assets__c, 0);
    }
    
    @isTest static void testUnDeleteAsset(){
        System.assertEquals([Select Total_Assets__c from Account][0].Total_Assets__c, 1);
        Asset deleteAsset = [Select Id from Asset][0];
        delete deleteAsset;
        System.assertEquals([Select Total_Assets__c from Account][0].Total_Assets__c, 0);
        Test.startTest();
            undelete deleteAsset;
        Test.stopTest();
        System.assertEquals([Select Total_Assets__c from Account][0].Total_Assets__c, 1);
    }
    
    @isTest static void testReparent(){
        System.assertEquals([Select Total_Assets__c from Account][0].Total_Assets__c, 1);
        
        Account newAccount = new Account(Name='New Account', Total_Assets__c=0);
        insert newAccount;
        
        Asset updateAsset = [Select Id, AccountId from Asset][0];
        updateAsset.AccountId = newAccount.Id;
        
        Test.startTest();
            update updateAsset;
        Test.stopTest();
        
        System.assertEquals([Select Total_Assets__c from Account][0].Total_Assets__c, 0);
        System.assertEquals([Select Total_Assets__c from Account where Id=:newAccount.Id][0].Total_Assets__c, 1);
    }
}

Apex FeedItem Trigger Share to Community

When uploading a new FeedItem we want to share it to a specific community. First thing we need to do is share the FeedItem to a community by sharing it to the User. As the documentation states: Only feed items with a Group or User parent can set a NetworkId or a null value for NetworkScope.

https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_feeditem.htm

NetworkId—The ID of the community in which the FeedItem is available. If left empty, the feed item is only available in the default community.

FeedItemTrigger Trigger

trigger FeedItemTrigger on FeedItem (after insert, before insert) {
    fflib_SObjectDomain.triggerHandler(App_Domain_FeedItem.class);
}

FeedItemTrigger Domain Class

public class App_Domain_FeedItem extends fflib_SObjectDomain {

	public override void onBeforeInsert(){
		List<ContentDocumentLink> contentDocumentLinksList = new List<ContentDocumentLink>();
		for(FeedItem record : (List<FeedItem>) Records){
			record.Body = record.ParentId;
			record.NetworkScope = {NetworkId};
			record.ParentId = UserInfo.getUserId();
			record.Visibility='AllUsers';
		}
	}
}

Now that the file is shared with the user we can give View sharing back to the original object using ContentDocumentLink by using the RelatedRecordId

public class App_Domain_FeedItem extends fflib_SObjectDomain {
public override void onAfterInsert(){
		List<ContentDocumentLink> contentDocumentLinksList = new List<ContentDocumentLink>();
		List<Documents__c> bpDocumentsList = new List<Documents__c>();

		Map<Id, Id> feedItemToRelatedRecordIdMap = new Map<Id, Id>();
		for(FeedItem record : (List<FeedItem>) Records){
			feedItemToRelatedRecordIdMap.put(record.Id, record.RelatedRecordId);
		}

		Map<Id, Id> mapOfRelatedRecordContentVersionMap = new Map<Id, Id>();
		List<ContentVersion> contentDocumentVersions = [Select Id, ContentDocumentId from ContentVersion where id IN :feedItemToRelatedRecordIdMap.values()];
		for (ContentVersion contentDocumentVersion : contentDocumentVersions){
			mapOfRelatedRecordContentVersionMap.put(contentDocumentVersion.Id, contentDocumentVersion.ContentDocumentId);
		}

		for(FeedItem record : (List<FeedItem>) Records){
			String bpDocumentId = record.Body;

			Id relatedRecordId = feedItemToRelatedRecordIdMap.get(record.Id);
			Id contentDocumentId = null;
			if (mapOfRelatedRecordContentVersionMap.containsKey(relatedRecordId)){
				 contentDocumentId = mapOfRelatedRecordContentVersionMap.get(relatedRecordId);
			}

			if (contentDocumentId!=null){
				ContentDocumentLink cdl = new ContentDocumentLink(LinkedEntityId = bpDocumentId , ContentDocumentId=contentDocumentId	, shareType = 'V');
				contentDocumentLinksList.add(cdl);
			}

			Documents__c updateDocument = new Documents__c(Id=bpDocumentId);
			updateDocument.Document_Status__c='Downloaded';
			String formatDownloadUrl = '/CommunityApi/sfc/servlet.shepherd/version/download/{0}?asPdf=false&operationContext=CHATTER';
			updateDocument.Download_Url__c = String.format(formatDownloadUrl, new List<String>{contentDocumentId});
			updateDocument.File_Id__c = contentDocumentId;
			updateDocument.File_Name__c = record.Title;
			updateDocument.File_Size__c = record.ContentSize;
			updateDocument.File_Type__c = record.ContentType!=null ? mimeTypeMap.containsKey(record.ContentType.toLowerCase()) ? mimeTypeMap.get(record.ContentType.toLowerCase()) : record.ContentType  : record.ContentType;
			updateDocument.File_Permission__c='R';

			bpDocumentsList.add(updateDocument);
		}

		if (!contentDocumentLinksList.isEmpty()){
			insert contentDocumentLinksList;
		}

		if (!bpDocumentsList.isEmpty()){
			update bpDocumentsList;
		}
	}
}

Now the Feeditem is shared both to the user and the object. It can be downloaded by the logged in user.

Apex Trigger for Sales Rep Scoring

This trigger is used to track and score sales rep activities as they complete Account fields.

Account Trigger Handler to Track specific field updates by sales rep and give points for based on which field was completed.

trigger AccountTrigger on Account (after delete, after insert, after undelete, after update, before delete, before insert, before update) {
    
    if (!System.isFuture() && !System.isBatch()){
      AccountTriggerHandler handler = new AccountTriggerHandler(Trigger.isExecuting, Trigger.size);

      if(Trigger.isUpdate && Trigger.isBefore){
          handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);
          AccountTriggerHandler.firstRun=false;
      }
}

Account Trigger Handler calls the SalesRepActivityScoring class to loop through all the accounts see which fields have been completed and assign points accordingly.

public with sharing class AccountTriggerHandler {
	
 	public static boolean firstRun = true; 

	private Boolean m_isExecuting = false;
    private Integer batchSize = 0;

	public AccountTriggerHandler(boolean isExecuting, integer size) {
		this.m_isExecuting = isExecuting;
        this.batchSize = size;
	}

	public void onBeforeUpdate(List<Account> oldAccounts, List<Account> updatedAccounts, Map<Id, Account> accountMap){
		SalesRepActivityScoring salesRepScoring = new SalesRepActivityScoring();
		salesRepScoring.insertSalesRepActivity(oldAccounts, updatedAccounts, accountMap);
	}
}

SalesRepActivityScoring class loops through accounts and checks if field not null and assign score. If field goes from filled in to null we subtract or points.

public with sharing class SalesRepActivityScoring {
	
	private static final Integer POTENTIALTOINVEST=4;
	private static final Integer LIQUIDASSETS=1;
	private static final Integer CONTEXTOFLC=1;
	private static final Integer GOALFORFUNDS=1;
	private static final Integer OCCUPATION=1;
	private static final Integer ACCREDIDTEDINVESTOR=1;
	private static final Integer HEADABOUTUS =1;

	private static final String ExecWithDialerProfileId = [Select Id from Profile where Name='Exec Standard user with dialer'].Id;
	private static final String AssociateWithDialerProfileId = [Select Id from Profile where Name='Associate Standard User With Dialer'].Id;

	public void insertSalesRepActivity(List<Account> oldAccounts, List<Account> updatedAccounts, Map<Id, Account> accountMap){
		List<SalesRep_Scoring__c> updateSalesRepScoringRecords = new List<SalesRep_Scoring__c>();

		Integer counter = 0;
		for (Account updatedAccount : updatedAccounts){
			if (updatedAccount.Account_Actor_ID__c!=null){
				if (UserInfo.getUserId().equals(updatedAccount.Lead_Assigned_To__c) && (UserInfo.getProfileId().equals(ExecWithDialerProfileId)) || UserInfo.getProfileId().equals(AssociateWithDialerProfileId)){
			
					SalesRep_Scoring__c salesRepScoring = new SalesRep_Scoring__c();
					salesRepScoring.Account__c = updatedAccount.Id;
					salesRepScoring.Account_Number__c = updatedAccount.Account_Actor_ID__c;
					salesRepScoring.Sales_Rep__c = UserInfo.getUserId();

					Boolean flag = false;

					if (updatedAccount.Accredited_Investor__c!=null){
						salesRepScoring.Accredited_Investor__c = ACCREDIDTEDINVESTOR;
						flag=true;
					} else if (oldAccounts.get(counter).Accredited_Investor__c!=null && updatedAccount.Accredited_Investor__c==null){
						salesRepScoring.Accredited_Investor__c = 0;
						flag=true;
					}

					if (updatedAccount.Context_of_LC_in_portfolio__c!=null){
						salesRepScoring.Context_of_LC__c = CONTEXTOFLC;
						flag=true;
					}else if (oldAccounts.get(counter).Context_of_LC_in_portfolio__c!=null && updatedAccount.Context_of_LC_in_portfolio__c==null) {
						salesRepScoring.Context_of_LC__c = 0;
						flag=true;
					}

					if (updatedAccount.Goal_for_Funds_at_LC__c!=null){
						salesRepScoring.Goal_for_funds__c = GOALFORFUNDS;
						flag=true;
					} else if (oldAccounts.get(counter).Context_of_LC_in_portfolio__c!=null && updatedAccount.Goal_for_Funds_at_LC__c==null){
						salesRepScoring.Goal_for_funds__c = 0;
						flag=true;
					}

					if (updatedAccount.How_did_you_hear_about_us__c!=null){
						salesRepScoring.How_did_you_hear_about_us__c = HEADABOUTUS;
						flag=true;
					} else if (oldAccounts.get(counter).How_did_you_hear_about_us__c!=null && updatedAccount.How_did_you_hear_about_us__c==null) {
						salesRepScoring.How_did_you_hear_about_us__c = 0;
						flag=true;
					}

					if (updatedAccount.Liquid_assets_client_stated__c!=null){
						salesRepScoring.Liquid_Assets__c = LIQUIDASSETS;
						flag=true;
					} else if (oldAccounts.get(counter).Liquid_assets_client_stated__c!=null && updatedAccount.Liquid_assets_client_stated__c==null){
						salesRepScoring.Liquid_Assets__c = 0;
						flag=true;
					}

					if (updatedAccount.Head_of_household_Occupation__c!=null){
						salesRepScoring.Occupation__c=OCCUPATION;
						flag=true;
					} else if (oldAccounts.get(counter).Head_of_household_Occupation__c!=null && updatedAccount.Head_of_household_Occupation__c==null){
						salesRepScoring.Occupation__c=0;
						flag=true;
					}

					if (updatedAccount.Potential_to_invest__c!=null){
						salesRepScoring.Potential_to_Invest__c=POTENTIALTOINVEST;
						flag=true;
					} else if (oldAccounts.get(counter).Potential_to_invest__c!=null && updatedAccount.Potential_to_invest__c==null) {
						salesRepScoring.Potential_to_Invest__c=0;
						flag=true;
					}

					if (flag){
						updateSalesRepScoringRecords.add(salesRepScoring);
					}
				}	
			}
			counter++;
		}
		upsert updateSalesRepScoringRecords Account_Number__c;
	}
}

SalesRepActivityScoringTest Class (100% code coverage). The first test method will test that the scoring only works for specific profiles. Second and third test method test scoring when fields completed for selected profiles. The last method will test if the scores is subtracted when fields goes from filled in to blank.

@isTest
private class SalesRepActivityScoringTest {
	

	public Id refProfileIdByName(String profileName){
        Profile getprofileId = [SELECT id FROM Profile WHERE name= :profileName];
        return getProfileId.id;
    }
    public User createAssociateSalesRepByProfile(String profileName){   
        User toUser = new User(alias = 'lcrep', 
        email='salesrep@lendingclub.com', 
        emailencodingkey='UTF-8',
        firstname='Associate', 
        lastname='Rep', 
        languagelocalekey='en_US', 
        localesidkey='en_US', 
        profileid = refProfileIdByName(profileName), 
        timezonesidkey='America/Los_Angeles', 
        username='assosciatesalesrep@lendingclub.com',
        Record_Count__c = 0,
        Assigned_Record_Count__c=10,
        Record_Tier__c='1;2;3;4;5;6;7;8;9;10',
        isActive=true);
        
        return toUser;
    }

    private Account createDummyAccount(User newUser, Decimal actorId){
    	Account newAccount = new Account();
		newAccount.Name='[TEST]AccountRepActivity';
		newAccount.Lead_Assigned_To__c=newUser.Id;
		newAccount.OwnerId= newUser.Id;
		newAccount.Account_Actor_ID__c=actorId;
		insert newAccount;

	
		return newAccount;
    }

	@isTest(SeeAllData=true) static void testSalesRepActivityScoringNotAddingForStandardRep() {
		SalesRepActivityScoringTest sra = new SalesRepActivityScoringTest();
		User standardUser = sra.createAssociateSalesRepByProfile('Standard User');
		insert standardUser;
		Account newAccount = sra.createDummyAccount(standardUser, 1111.0);

		Test.startTest();
			update newAccount;
		Test.stopTest();

		System.assertEquals([Select id from SalesRep_Scoring__c where Account__c=:newAccount.Id].size(), 0);
	}
	
	@isTest static void testSalesRepActivityScoringAddingForExecRep() {
		SalesRepActivityScoringTest sra = new SalesRepActivityScoringTest();
		User execUser = sra.createAssociateSalesRepByProfile('Exec Standard user with dialer');
		insert execUser;
		System.runAs(execUser){
			Account newAccount = sra.createDummyAccount(execUser, 1234.0);

			newAccount.Account_Actor_ID__c=1234.0;
			newAccount.Accredited_Investor__c='Not Accredited';
			newAccount.Context_of_LC_in_portfolio__c='a';
			newAccount.Goal_for_Funds_at_LC__c='a';
			newAccount.How_did_you_hear_about_us__c='a';
			newAccount.Liquid_assets_client_stated__c='a';
			newAccount.Head_of_household_Occupation__c='a';
			newAccount.Potential_to_invest__c='a';

			Test.startTest();
				update newAccount;
			Test.stopTest();

			SalesRep_Scoring__c salesRepScoring = [select Goal_for_funds__c,Account__c, Account_Number__c, Accredited_Investor__c, Context_of_LC__c, CreatedById, CreatedDate, How_did_you_hear_about_us__c, Id, IsDeleted, LastActivityDate, LastModifiedById, LastModifiedDate, Liquid_Assets__c, Name, Occupation__c, OwnerId, Potential_to_Invest__c, Sales_Rep__c, SystemModstamp from SalesRep_Scoring__c where Account__c=:newAccount.Id][0];

			System.assertEquals(salesRepScoring.Account__c, newAccount.Id);
			System.assertEquals(salesRepScoring.Sales_Rep__c, execUser.Id);
			System.assertEquals(salesRepScoring.Accredited_Investor__c, 1);
			System.assertEquals(salesRepScoring.Context_of_LC__c, 1);
			System.assertEquals(salesRepScoring.Goal_for_funds__c, 1);
			System.assertEquals(salesRepScoring.How_did_you_hear_about_us__c, 1);
			System.assertEquals(salesRepScoring.Liquid_Assets__c, 1);
			System.assertEquals(salesRepScoring.Occupation__c, 1);
			System.assertEquals(salesRepScoring.Potential_to_Invest__c, 4);
		}
	}

	@isTest(SeeAllData=true) static void testSalesRepActivityScoringAddingForAssociateRep() {
		SalesRepActivityScoringTest sra = new SalesRepActivityScoringTest();
		User associate = sra.createAssociateSalesRepByProfile('Associate Standard User With Dialer');
		insert associate;
		System.runAs(associate){
			
			Account newAccount = sra.createDummyAccount(associate, 1235.0);

			newAccount.Account_Actor_ID__c=1235.0;
			newAccount.Accredited_Investor__c='Not Accredited';
			newAccount.Context_of_LC_in_portfolio__c='a';
			newAccount.Goal_for_Funds_at_LC__c='a';
			newAccount.How_did_you_hear_about_us__c='a';
			newAccount.Liquid_assets_client_stated__c='a';
			newAccount.Head_of_household_Occupation__c='a';
			newAccount.Potential_to_invest__c='a';

			Test.startTest();
				update newAccount;
			Test.stopTest();

			SalesRep_Scoring__c salesRepScoring = [select Goal_for_funds__c,Account__c, Account_Number__c, Accredited_Investor__c, Context_of_LC__c, CreatedById, CreatedDate, How_did_you_hear_about_us__c, Id, IsDeleted, LastActivityDate, LastModifiedById, LastModifiedDate, Liquid_Assets__c, Name, Occupation__c, OwnerId, Potential_to_Invest__c, Sales_Rep__c, SystemModstamp from SalesRep_Scoring__c where Account__c=:newAccount.Id][0];

			System.assertEquals(salesRepScoring.Account__c, newAccount.Id);
			System.assertEquals(salesRepScoring.Sales_Rep__c, associate.Id);
			System.assertEquals(salesRepScoring.Accredited_Investor__c, 1);
			System.assertEquals(salesRepScoring.Context_of_LC__c, 1);
			System.assertEquals(salesRepScoring.Goal_for_funds__c, 1);
			System.assertEquals(salesRepScoring.How_did_you_hear_about_us__c, 1);
			System.assertEquals(salesRepScoring.Liquid_Assets__c, 1);
			System.assertEquals(salesRepScoring.Occupation__c, 1);
			System.assertEquals(salesRepScoring.Potential_to_Invest__c, 4);

		}
	}

	@isTest(SeeAllData=true)  static void testSalesRepActivityResetNullResetScore(){
		SalesRepActivityScoringTest sra = new SalesRepActivityScoringTest();
		User execUser = sra.createAssociateSalesRepByProfile('Exec Standard user with dialer');
		insert execUser;
		System.runAs(execUser){
			Account newAccount = sra.createDummyAccount(execUser, 1234.0);

			newAccount.Account_Actor_ID__c=1234.0;
			newAccount.Accredited_Investor__c='Not Accredited';
			newAccount.Context_of_LC_in_portfolio__c='a';
			newAccount.Goal_for_Funds_at_LC__c='a';
			newAccount.How_did_you_hear_about_us__c='a';
			newAccount.Liquid_assets_client_stated__c='a';
			newAccount.Head_of_household_Occupation__c='a';
			newAccount.Potential_to_invest__c='a';

			update newAccount;

			SalesRep_Scoring__c salesRepScoring = [select Goal_for_funds__c,Account__c, Account_Number__c, Accredited_Investor__c, Context_of_LC__c, CreatedById, CreatedDate, How_did_you_hear_about_us__c, Id, IsDeleted, LastActivityDate, LastModifiedById, LastModifiedDate, Liquid_Assets__c, Name, Occupation__c, OwnerId, Potential_to_Invest__c, Sales_Rep__c, SystemModstamp from SalesRep_Scoring__c where Account__c=:newAccount.Id][0];

			System.assertEquals(salesRepScoring.Account__c, newAccount.Id);
			System.assertEquals(salesRepScoring.Sales_Rep__c, execUser.Id);
			System.assertEquals(salesRepScoring.Accredited_Investor__c, 1);
			System.assertEquals(salesRepScoring.Context_of_LC__c, 1);
			System.assertEquals(salesRepScoring.Goal_for_funds__c, 1);
			System.assertEquals(salesRepScoring.How_did_you_hear_about_us__c, 1);
			System.assertEquals(salesRepScoring.Liquid_Assets__c, 1);
			System.assertEquals(salesRepScoring.Occupation__c, 1);
			System.assertEquals(salesRepScoring.Potential_to_Invest__c, 4);

			newAccount.Accredited_Investor__c=null;
			newAccount.Context_of_LC_in_portfolio__c=null;
			newAccount.Goal_for_Funds_at_LC__c=null;
			newAccount.How_did_you_hear_about_us__c=null;
			newAccount.Liquid_assets_client_stated__c=null;
			newAccount.Head_of_household_Occupation__c=null;
			newAccount.Potential_to_invest__c=null;

			Test.startTest();
				update newAccount;
			Test.stopTest();

			SalesRep_Scoring__c salesRepScoringReset = [select Goal_for_funds__c,Account__c, Account_Number__c, Accredited_Investor__c, Context_of_LC__c, CreatedById, CreatedDate, How_did_you_hear_about_us__c, Id, IsDeleted, LastActivityDate, LastModifiedById, LastModifiedDate, Liquid_Assets__c, Name, Occupation__c, OwnerId, Potential_to_Invest__c, Sales_Rep__c, SystemModstamp from SalesRep_Scoring__c where Account__c=:newAccount.Id][0];

			System.assertEquals(salesRepScoringReset.Account__c, newAccount.Id);
			System.assertEquals(salesRepScoringReset.Sales_Rep__c, execUser.Id);
			System.assertEquals(salesRepScoringReset.Accredited_Investor__c, 0);
			System.assertEquals(salesRepScoringReset.Context_of_LC__c, 0);
			System.assertEquals(salesRepScoringReset.Goal_for_funds__c, 0);
			System.assertEquals(salesRepScoringReset.How_did_you_hear_about_us__c, 0);
			System.assertEquals(salesRepScoringReset.Liquid_Assets__c, 0);
			System.assertEquals(salesRepScoringReset.Occupation__c, 0);
			System.assertEquals(salesRepScoringReset.Potential_to_Invest__c, 0);
		}
	}
}

Salesforce Update Product Schedule from Opportunity Product using Apex Trigger

Update the Product ScheduleDate when the Opportunity Product ServiceDate changes. First we create an OpportunityProduct Trigger to see when ServiceDate changes and parse the amount of days difference between the old and new date.

trigger ProductDateUpdate on OpportunityLineItem (after update) {
   
    for (OpportunityLineItem oli: Trigger.new) {
        OpportunityLineItem oldCloseDate = Trigger.oldMap.get(oli.ID);
        if (oli.ServiceDate != oldCloseDate.ServiceDate) {
            Integer dateval = oldCloseDate.ServiceDate.daysBetween(oli.ServiceDate);
            ProductScheduleUpdate.ScheduleProductUpdate(oli.id, dateval);  
        }
    }
}

The Trigger class then updates all the Product Schedule Dates by the amount of days.

public class ProductScheduleUpdate
{
    public static void ScheduleProductUpdate(String oid, Integer dateval)
    {
           List datelist;
           datelist = [SELECT ScheduleDate FROM OpportunityLineItemSchedule WHERE OpportunityLineItemId =:oid];
           for (Integer k = 0; k < datelist.size(); k++)
               {
               System.debug('DateList = ' + datelist[k]);
               Date mydate=Date.newInstance(datelist[k].ScheduleDate.Year(),datelist[k].ScheduleDate.Month(),datelist[k].ScheduleDate.Day());
               mydate=mydate.addDays(dateval);
               datelist[k].ScheduleDate = mydate;
               update datelist;
               //date datediff = date.parse(datelist[k]);
               }
        
    }
}

Salesforce Update Product Schedule from Opportunity using Apex Trigger

The tutorial below shows how you can use Salesforce Triggers to update the CloseDate of an opportunity and the associated Product Schedule.

First thing is we must create a product Trigger, below is the code for the product trigger:

trigger OpportunityScheduleUpdate on Opportunity (after update) {
  
  for (Opportunity o: Trigger.new) {
        Opportunity oldCloseDate = Trigger.oldMap.get(o.ID);
        if (o.CloseDate != oldCloseDate.CloseDate) {
            Integer dateval = oldCloseDate.CloseDate.daysBetween(o.CloseDate);
            OppScheduleUpdate.ScheduleDateUpdate(o.id, dateval);  
        }
    }
}

The trigger class updates all the Product ScheduleDate by the amount of the days the Opportunity Close Date has changed:

public class OppScheduleUpdate
{
    public static void ScheduleDateUpdate(String oppid, Integer dateval) 
    {
       List idval = [SELECT id FROM OpportunityLineItem WHERE OpportunityId=:oppid];
       List datelist;
       for (Integer i = 0; i < idval.size(); i++)
       {
           datelist = [SELECT ScheduleDate FROM OpportunityLineItemSchedule WHERE OpportunityLineItemId =:idval[i].id];
           for (Integer k = 0; k < datelist.size(); k++)
               {
               System.debug('DateList = ' + datelist[k]);
               Date mydate=Date.newInstance(datelist[k].ScheduleDate.Year(),datelist[k].ScheduleDate.Month(),datelist[k].ScheduleDate.Day());
               mydate=mydate.addDays(dateval);
               datelist[k].ScheduleDate = mydate;
               update datelist;
               //date datediff = date.parse(datelist[k]);
               }
       }      
    }
}