• Haseeb Ahmad 9
  • NEWBIE
  • 30 Points
  • Member since 2020

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 21
    Questions
  • 26
    Replies
Hello all,

I have the apex class which not getting any code coverage would you able to help me fix it or what is wrong here, thank you. 

Class: 
 
public class ChangePassword 
{
    @InvocableMethod(
        label = 'User Password Reset'
        description = 'User Password Reset'
    )
    
    public static List <String> getUserId (List <ID> ids){
        List<String> userId = new List <String> ();
        List<User> userList = [SELECT Id from User WHERE 
                                        IsActive = true AND 
                                        CreatedDate = TODAY AND 
                                        LastLoginDate = NULL AND
                                        LastPasswordChangeDate = NULL AND
                                        Id in :ids 
                                        LIMIT 100];   
        for(User u : userList)
                {
                      system.resetPassword(u.Id, true);
                      System.debug('DONE: ' + u.Id);
                }
        return userId;
    }
}

test:
 
@isTest
private class ChangePasswordTest {

    @testSetup
    static void ChangePassword () {

        Profile p = [ SELECT id FROM Profile WHERE name = 'Standard User' ];

        Test.startTest();   
        User u = new User ();
        u.firstName = 'test1';
        u.lastName = 'test2';
        u.ProfileId = p.Id ;
        u.Username = 'test@email4x.com' ;
        u.Email = 'test@email.com';
        u.Alias = 't1';
        u.EmailEncodingKey = 'ISO-8859-1';
        u.LocaleSidKey = 'en_US' ;
        u.TimeZoneSidKey = 'America/Los_Angeles';
        u.LanguageLocaleKey = 'en_US' ;
        Insert u ;
        system.resetPassword(u.Id, true);
        System.debug('DONE: ' + u.Id);
        Test.stopTest();

    }

}

​​​​​​​

 
Hello,

Case object have lookup relation to opportunity (OppToCase__c) what I want if an opportunity already has an open case with record type ('PS Only') related,  then don't let user create another case with that same record type.

How I will do that? any idea?  thank you
I am getting this ERROR System.QueryException: List has no rows for assignment to SObject on the following lines of code:
 
public static void insertCase(Case caseData) {
        insert caseData;
        Opportunity unitOpportunity = [select Id from Opportunity where Id =: caseData.OppToCase__c limit 1];  
        unitOpportunity.Case__c = caseData.Id;
        update unitOpportunity; 
    }

I tried to use try and catch but then I was not able to link the case back to opportunity, probably I am missing something very small but not able to figure this one out, can someone help, thank you. 
Hi Everyone,
Trying to send email from apex and receiving error:

System.EmailException: SendEmail failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Email body is required.: []

Apex Class: 
public with sharing class EmailUtils 
{
    public static void sendEmail(List<String> toAddresses, String senderDisplayName, String subject, String body)
    {       
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        mail.settoAddresses(toAddresses);
        mail.setSenderDisplayName(senderDisplayName);
        mail.setSubject(subject);
        mail.setPlainTextBody(body);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
Test: 
@isTest
private class EmailUtilsTest 
{
    @isTest
    static void testEmailUtils()
    {
        Test.StartTest();
        EmailUtils.sendEmail(new List<String>{'test@test.com'}, 'Unit Test Name', 'Unit Test X', 'Unit Test');
        Integer invocations = Limits.getEmailInvocations();
        Test.stopTest();
 
        System.assertEquals(1, invocations, 'An email has not been sent');
    }
It has 100% code coverage but when I am trying to deploy in production getting that above error. please help, thank you.
​​​​​​​
 
Hello,

We've used Process Builder to set up a process that copies the owner ID from the Account and to the owner of an opportunity. Normally it works perfectly and we don't have any issues. We've set up some APEX code and are running test classes that are failing because of the process. The code creates a new account and opportunity which meets the criteria of the process but we're still getting this error: INVALID_CROSS_REFERENCE_KEY: Owner ID: owner cannot be blank.

Doesn't the owner auto set to be the user creating the account/running the test class? Is there something we need to include in the class in regards to the account owner? 
Hello,

I have a related list on account object called " Billing Accounts " (zuora__customeraccount__c).

One account record can have multiple billing accounts records.

On the order object, we have a field called "billing instance" (Billing_Account_Instance__c) which is a lookup to the "billing account" (zuora__customeraccount__c) object.

Currently, users have to populate this field once the order is created.

Now what I am trying to solve is if one account has one billing account populate that billing account in that field once the order is created. 

But if multiple billing accounts exist then not do populate anything so our billing team can choose the most appropriate billing instance for that order.

Can I get some guidance on how I can do that, thank you. 
 
Hello I need help to figure out when the VF page is not able to call the Lightning component correctly 

Following this article: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/components_visualforce.htm

https://developer.salesforce.com/forums/?id=9060G0000005jWmQAI

When I make a call I am able to launch the lightning component in the VF page but if I click any button it Is not working and I am getting this error.

User-added image
When I click the cancel button it should redirect me back to the opportunity record page but it not happening.

If I use that lightning component in a quote action it works perfectly but not when I call from the VF page. 

any thought? 

Hello everyone,

I have this validation rule which stops users to put the project on hold and presents them the error if their profile is not Admin or billing. which works great. 

This is the formula. 

if(
(Date_of_Hold__c < LatestHold__c &&
(ISPICKVAL(accorto__Status__c, 'On Hold') ||
Date_of_Hold__c < LatestHold__c &&
ISPICKVAL(accorto__Progress__c, 'On Hold'))),

if(
$Profile.Name == 'System Administrator' ||
$Profile.Name == 'Zuora Billing Team',
false,
true),
false)

The problem is now those users not able to update project which is already on hold. 

what I really want is to make sure, 1st, they cannot put the project on hold, 2nd,  if the project is already on hold they should be able to make changes to all other fields except the status field.

which is currently not happening and they getting an error that the project is on hold even making changes to other fields.

Any idea how can I able to modify this validation rule? thank you. 
Hi,

I have an issue where flow works but now when I enter all the inputs screen components and click next and finish.

Instead of redirecting me back to the current opportunity page, I am staying on 1st screen component like the process is starting again.

here is my code do you do what is the problem in this:
 
<apex:page controller="EngageDealSupportButtonClass" lightningstylesheets="true">
    <apex:param name="opportunityId" value="{!$CurrentPage.parameters.opportunityId}"/>
    <html>
        <head>
            <apex:includeLightning />
        </head>
        <body class="slds-scope">
            <div id="flowContainer" />
            <script>
            var statusChange = function (event) {
                if(event.getParam("status") === "FINISHED") {
                    // Control what happens when the interview finishes
                    
                    var outputVariables = event.getParam("outputVariables");
                    var outputVar;
                    for(var i = 0; i < outputVariables.length; i++) {
                        outputVar = outputVariables[i];
                        if(outputVar.name === "redirect") {
                            var urlEvent = $A.get("e.force:navigateToSObject");
                            urlEvent.setParams({
                                "recordId": outputVar.value,
                                "isredirect": "true"
                            });
                            urlEvent.fire();
                        }
                    }
                }
            };
            $Lightning.use("c:lightflowApp", function() {
                // Create the flow component and set the onstatuschange attribute
                $Lightning.createComponent("lightning:flow", {"onstatuschange":statusChange},
                                           "flowContainer",
                                           function (component) {
                                               // Set the input variables
                                               var inputVariables = [
                                                   {
                                                       name : 'opportunityId',
                                                       type : 'SObject',
                                                       value : component.get ("v.recordId")
                                                   }
                                               ];
                                               
                                               // Start an interview in the flowContainer div, and 
                                               // initializes the input variables.
                                               component.startFlow("pservicerequest", inputVariables);
                                           }
                                          );
            });
            </script>
        </body>
    </html>
</apex:page>
any thoughts on how can I resolve this? thank you.
Hello,

I am getting this error when calling lightning flow from a VF page. It s a screen flow and when I click on next I get this error.

Any thoughts? 
Hello,

I have a flow that has some components that can only run on lightning runtime.

This the original VF I have. 
 
<apex:page controller="EngageDealSupportButtonClass" lightningstylesheets="true">
<apex:includeLightning />
<flow:interview name="pservicerequest" buttonLocation="bottom" finishLocation="/{!$CurrentPage.parameters.opportunityId}">
<apex:param name="opportunityId" value="{!$CurrentPage.parameters.opportunityId}"/>
</flow:interview>
</apex:page>

even I have lightningstylesheets="true" and <apex:includeLightning />

This still did not help so I looked around and found this help article:

https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_flows_lightningruntime.htm

Hence I created a lightning app by going to the developer console. Here is the code for that:
 
<aura:application access="global" extends="ltng:outApp" >
   <aura:dependency resource="lightning:flow"/>
</aura:application>

Then I went to the visualforce page and added the following code: the flow API name is pservicerequest and Appname is lightflowApp.
 
<apex:page controller="EngageDealSupportButtonClass" lightningstylesheets="true">
    <apex:param name="opportunityId" value="{!$CurrentPage.parameters.opportunityId}"/>
    <html>
        <head>
            <apex:includeLightning />
        </head>
        <body class="slds-scope">
            <div id="flowContainer" />
            <script>
            var statusChange = function (event) {
                if(event.getParam("status") === "FINISHED") {
                    // Control what happens when the interview finishes
                    
                    var outputVariables = event.getParam("outputVariables");
                    var key;
                    for(key in outputVariables) {
                        if(outputVariables[key].name === "myOutput") {
                            // Do something with an output variable
                        }
                    }
                }
            };
            $Lightning.use("c:lightflowApp", function() {
                // Create the flow component and set the onstatuschange attribute
                $Lightning.createComponent("lightning:flow", {"onstatuschange":statusChange},
                                           "flowContainer",
                                           function (component) {
                                               // Set the input variables
                                               
                                               
                                               // Start an interview in the flowContainer div, and 
                                               // initializes the input variables.
                                               component.startFlow("pservicerequest");
                                           }
                                          );
            });
            </script>
        </body>
    </html>
</apex:page>

with that now I can able to run flow in lightning flow runtime but when I click next on the screen it gives me error:
 
An unhandled fault has occurred in this flow
An unhandled fault has occurred while processing the flow. Please contact your system administrator for more information.

​​​​​​​With the Original VF page, I am not getting errors or If I run flow in debug it works very well so the problem must be in this new VF page. 

At this point, I am not sure what else I can do I have been trying to find an answer online but no luck if anyone able to help with this that would be great. thank you. 
Hi Everyone,

Looking for some help on how to fix this test class and code coverage issue as I am getting System.NullPointerException: Attempt to de-reference a null object on line 33.

Apex Class:
 
public class MqlService {
    public void setMqlDate(List<sObject> newRecords, Map<Id, sObject> oldRecordsById) {
        List<sObject> records = getOpenRecords(newRecords, oldRecordsById);
        if (records.isEmpty()) {
            return;
        }
        
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours'];
        for (sObject record : records) {
            // using this BusinessHours method will set the time to NOW if it is within the business hours
            // otherwise it will set the time to the time the business hours start next
            record.put('Mql_Date__c', BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()));
        }
    }
    
    // returns a list of records that are either inserted as 'Open', or updated to 'Open'
    private List<sObject> getOpenRecords(List<sObject> newRecords, Map<Id, sObject> oldRecordsById) {
        List<sObject> filteredRecords = new List<sObject>();
        for (sObject newRecord : newRecords) {
            if (!recordIsOpen(newRecord)) {
                continue;
            }
            
            if (isInsert(newRecord, oldRecordsById)) {
                filteredRecords.add(newRecord);
                continue;
            }
            
            if (oldRecordsById != null) {
                sObject oldRecord = oldRecordsById.get((Id)newRecord.get('id'));                
                if (oldRecord != null && !recordIsOpen(oldRecord)) {
                    filteredRecords.add(newRecord);                    
                }
            }
        }
        
        return filteredRecords;
    }
    
    private boolean isInsert(sObject newRecord, Map<Id, sObject> oldRecordsById) {
        return oldRecordsById == null || !oldRecordsById.containsKey((Id)newRecord.get('id'));
    }
                
    private boolean recordIsOpen(sObject record) {
        if (record.getSObjectType() == Lead.getSObjectType()) {
            return record.get('Status') == 'Open';
        } else if (record.getSObjectType() == Contact.getSObjectType()) {
            return record.get('Lead_Status__c') == 'Open';
        } else {
            return false;
        }
    }      
}

Test Class:
 
@isTest
public class MqlServiceTest {
    public static testMethod void testSetMqlDate() {
        // retrieve business hours
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours'];
        
        // account for contact
        Account account = new Account(name = 'test');
        insert account;

		// lead / contact to update as Open
		Lead updateLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Pre-MQL');
        Contact updateContact = new Contact(lastName = 'test', accountId = account.id, lead_status__c = 'Pre-MQL');
        insert new List<sObject>{updateLead, updateContact};
            
        // update lead and contact
        updateLead.status = 'Open';
        updateContact.lead_status__c = 'Open';
            
        // lead / contact to insert as Open
        Lead insertLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Open');
        Contact insertContact = new Contact(lastName = 'test', accountId = account.id, lead_status__c = 'Open');            
            
        // capture the current time to check against business hours, set the time in time utils
        TimeUtils.setNow(Datetime.now());            
        
        // upsert list of leads / contacts to insert / update
        upsert new List<Lead>{insertLead, updateLead};
        upsert new List<Contact>{insertContact, updateContact};
        
        // assert the hour / minutes are correct, since the BusinessHours.nextStartDate doesn't use seconds
        for (Lead lead : [select mql_date__c from lead]) {
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).hour(), lead.mql_date__c.hour());
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).minute(), lead.mql_date__c.minute());            
        }
        
        for (Contact contact : [select mql_date__c from contact]) {
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).hour(), contact.mql_date__c.hour());
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).minute(), contact.mql_date__c.minute());            
        }
    }
}

Because of line 33 assert method filing calls is not getting any code coverage as well.

Thank you for your help. ​​​​​​​
Hi everyone,

I have this validation rule which only allows admins and superusers to change account owners but I need to add one more condition if the user role contains any of these words.

RVP, ES AE, CS AE, Sales Director

then DO NOT let them change account owners everyone else should be able to change the account owner.

Here is my validation rule:
 
AND(
/* profile exceptions */
$Profile.Name <> 'System Administrator',
$Profile.Name <> 'Super User',


/* account type is Sales */
ISPICKVAL(Account_Type__c, 'Sales'),

ISCHANGED(OwnerId)
)

How can I make this adjustment, thank you for any help.
Hi Everyone,

Can someone help me figure out why this class not getting any code coverage?
 
public class CampaignSourceBatch implements database.Batchable<sObject>, database.stateful {
    
    private final String query;
    private String errorMessage = '';    
    
    // custom query
    public CampaignSourceBatch(String query) {
        this.query = query;
    }

    public Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(query);
    }
    
    public void execute(Database.BatchableContext bc, List<sObject> parentRecords) {
        DiagnosticService.push('CampaignSourceBatch.execute');
        DiagnosticService.debug('parentRecords: ' + parentRecords);
        
        // error for single batch
		String batchError = '';
		        
        CampaignSourceService service = new CampaignSourceService();
        Set<Id> parentIds = new Map<Id, sObject>(parentRecords).keySet();
        batchError += service.setMQLCampaignSource(parentIds);
        batchError += service.setPrimaryCampaignSource(parentIds);
        
        if (!String.isBlank(batchError)) {
            DiagnosticService.debug('batchError: ' + batchError);     
            errorMessage += batchError;
        }
        DiagnosticService.pop();
    }
    
    public void finish(Database.BatchableContext bc) {
        DiagnosticService.push('CampaignSourceBatch.finish');        
        if (!String.isBlank(errorMessage)) {
            DiagnosticService.debug('errorMessage: ' + errorMessage);            
			String subject = 'Error(s) in CampaignSourceBatch ' + Date.today();
            Utils.sendEmailToAdmin(subject, errorMessage);
        }
        DiagnosticService.pop();
    }
}

Test Class:
 
@isTest
public class CampaignSourceServiceTest {
	@testSetup
    public static void setup() {
        
        // create leads / contacts with status = 'Open'
		List<Lead> leads = new List<Lead>();
        List<Contact> contacts = new List<Contact>();
        for (Integer i=0; i<10; i++) {
            leads.add(new Lead(lastName = 'test lead ' + i, email = 'testLead' + i + '@test.com', company = 'test ' + i,  mql_date__c = Datetime.now()));
            contacts.add(new Contact(lastName = 'test contact ' + i, email = 'testContact' + i + '@test.com', mql_date__c = Datetime.now()));
        }
        
        insert leads;
        insert contacts;
        
        List<Campaign> campaigns = new List<Campaign>();
        campaigns.add(new Campaign(name = 'campaign 1'));
        campaigns.add(new Campaign(name = 'campaign 2'));
        campaigns.add(new Campaign(name = 'campaign 3'));
        insert campaigns;
    }
    
    public static testMethod void testCampaignMember_Insert() {
        // retrieve campaigns
   		List<Campaign> campaigns = getCampaigns();
        Campaign c1 = campaigns[0];
        campaign c2 = campaigns[1];
        campaign c3 = campaigns[2];

        // retrieve leads and contacts
        List<Lead> leads = getLeads().values();
        List<Contact> contacts = getContacts().values();        
        
        // create campaign members for each parent record, using the first two campaigns        
        List<CampaignMember> campaignMembers = new List<CampaignMember>();
        for (Lead lead : leads) {
            campaignMembers.add(new CampaignMember(leadId = lead.Id, campaignId = c1.id, status = 'Sent'));
            campaignMembers.add(new CampaignMember(leadId = lead.Id, campaignId = c2.id, status = 'Responded'));            
        }
        for (Contact contact : contacts) {
            campaignMembers.add(new CampaignMember(contactId = contact.Id, campaignId = c1.id, status = 'Sent'));
            campaignMembers.add(new CampaignMember(contactId = contact.Id, campaignId = c2.id, status = 'Responded'));            
        }   
        insert campaignMembers;
        
        
        // assert that the campaign source values were set appropriately 
        // - mql is the first campaign after the mql date on the parent
        // - primary is the most recent 'responded' campaign
        for (Lead lead : getLeads().values()) {
            system.assertEquals(c1.id, lead.mql_campaign_source__c);
            system.assertEquals(c2.id, lead.Primary_Campaign_Source__c);
        }
        
        for (Contact contact : getContacts().values()) {
            system.assertEquals(c1.id, contact.mql_campaign_source__c);
            system.assertEquals(c2.id, contact.Primary_Campaign_Source__c);
        }      
        
        // add third campaign membership as 'responded' to ensure that the MQL Campaign Source stays the same, and that the Primary Campaign Source changes
        campaignMembers.clear();
        for (Lead lead : leads) {
            campaignMembers.add(new CampaignMember(leadId = lead.Id, campaignId = c3.id, status = 'Responded'));            
        }
        for (Contact contact : contacts) {
            campaignMembers.add(new CampaignMember(contactId = contact.Id, campaignId = c3.id, status = 'Responded'));            
        }   
        
        Test.startTest();
        insert campaignMembers;
        Test.stopTest();
        
        // assert that MQL source stayed the same, but that Primary source updated
        for (Lead lead : getLeads().values()) {
            system.assertEquals(c1.id, lead.mql_campaign_source__c);
            system.assertEquals(c3.id, lead.Primary_Campaign_Source__c);
        }
        
        for (Contact contact : getContacts().values()) {
            system.assertEquals(c1.id, contact.mql_campaign_source__c);
            system.assertEquals(c3.id, contact.Primary_Campaign_Source__c);
        }          
    }
    
    // NEED TO REVIEW TEST FAILURES - only fails during deployment in production 
    public static testMethod void testCampaignMemberUpdate() {
        // retrieve campaigns
   		List<Campaign> campaigns = getCampaigns();
        Campaign c1 = campaigns[0];
        campaign c2 = campaigns[1];

        // retrieve leads and contacts
        List<Lead> leads = getLeads().values();
        List<Contact> contacts = getContacts().values();        
        
        // create campaign members for each parent record, using the first two campaigns        
        List<CampaignMember> campaignMembers = new List<CampaignMember>();
        for (Lead lead : leads) {
            campaignMembers.add(new CampaignMember(leadId = lead.Id, campaignId = c1.id, status = 'Sent'));
            campaignMembers.add(new CampaignMember(leadId = lead.Id, campaignId = c2.id, status = 'Responded'));            
        }
        for (Contact contact : contacts) {
            campaignMembers.add(new CampaignMember(contactId = contact.Id, campaignId = c1.id, status = 'Sent'));
            campaignMembers.add(new CampaignMember(contactId = contact.Id, campaignId = c2.id, status = 'Responded'));            
        }   
        insert campaignMembers;
  
        // flip all 'c1' campaign members to 'Responded'
        for (CampaignMember member : campaignMembers) {
            if (member.campaignId == c1.id) {
                member.status = 'Responded';
            }
        }        
        
        Test.startTest();
        update campaignmembers;
        Test.stopTest();
        
        // assert that primary source updated since the c1 membership changed to 'responded'
        for (Lead lead : getLeads().values()) {
            system.assertEquals(c1.id, lead.Primary_Campaign_Source__c);
        }
        
        for (Contact contact : getContacts().values()) {
            system.assertEquals(c1.id, contact.Primary_Campaign_Source__c);
        }          
    }
	
    
    private static List<Campaign> getCampaigns() {
        return [select id from campaign];
    }
    
    private static Map<Id, Lead> getLeads() {
        return new Map<Id, Lead>([select id, mql_campaign_source__c, primary_campaign_source__c from lead]);
    }
    
    private static Map<Id, Contact> getContacts() {
        return new Map<Id, Contact>([select id, mql_campaign_source__c, primary_campaign_source__c from contact]);
    }
}

Thank you for your help. ​​​​​​​
Hi Everyone,

Having some issues creating a test class for the controller.
 
public without sharing class DeleteFilesCtrl {
    @AuraEnabled
    public static List<FilesWrap> getFiles(string objectId){
        List<FilesWrap> wrapList = new List<FilesWrap>();
    	List<ContentDocumentLink> cdList = [SELECT ContentDocument.Title,ContentDocument.FileType,ContentDocument.LastModifiedDate , ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId =:objectId];
        for(ContentDocumentLink cd : cdList){
            if(cd.ContentDocument.FileType != 'SNOTE'){
        		wrapList.add(new FilesWrap(cd));
            }
        }
        System.debug('---'+wrapList);
        return wrapList;
    }
    @AuraEnabled
    public static string deleteFiles(string objectId,string contentDocumentId){
    	List<ContentDocumentLink> cdlList = [SELECT ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE ContentDocumentId=:contentDocumentId];
        try{    
        	if(cdlList.size() <= 2){
               delete [SELECT Id FROM ContentDocument WHERE Id = :contentDocumentId];
            }else{
                delete [SELECT ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId=:objectId AND ContentDocumentId=:contentDocumentId];    
            }
        }catch(Exception ex){
            return ex.getMessage();
        }
        return 'Success';
    }    
    public class FilesWrap{
        @AuraEnabled public String title;
        @AuraEnabled public String fileType;
        @AuraEnabled public String id;
        @AuraEnabled public DateTime lastModifiedDate;
        
        public FilesWrap(ContentDocumentLink cd){
            title = cd.ContentDocument.Title;
            id = cd.ContentDocumentId;
            fileType = cd.ContentDocument.FileType;
            lastModifiedDate= cd.ContentDocument.LastModifiedDate;
        }
    }
}

I am have tried many ways but either I am not getting any coverages or getting errors. Would love some helpful insight on how to create a test for this. thank you. 
 
Hi Everyone, 

I have this Apex class which creates SOW and attached to "Notes & Attachments"  and I want to change that so when we generate SOW it attach to files instead.
 
/**
* This controller will generate a Quote PDF and attach it to the Quote's parent Opportunity.
*/
global class ZuoraDocumentGenerator {
    @testVisible private static final string noAmountResponse = 'Please add ACV to the opportunity before generating an SOW';
    @testVisible private static final string noOpportunitiesResponse = 'No opportunities found.';
    @testVisible private static final string notCorrectStage= 'You cannot generate an SOW before Stage 4, please fill out the required integration fields and move your Opportunity to Stage 4 to continue generating the SOW.';
    @testVisible private static final string oppOverThresholdResponse = 'Please chatter <a href="/_ui/core/chatter/groups/GroupProfilePage?g=0F90d0000008YEA" target="_blank">@Professional Services</a> in order to get an SOW generated';
    @testVisible private static final string noTemplateFoundResponse = 'No SOW templates are setup.';
    @testVisible private static final string errorContactingZuoraResponse = 'Error contacting Zuora';
    @testVisible private static integer testStatusCode;
    @testVisible private static string testSuccessMessage;
    
    // Generates PDF and attaches to Quote's parent Opportunity Object.
    @AuraEnabled
    webservice static String generateSOW(Id quoteId, String docType) {
        // query for quote to pull opportunity ID
        System.debug('** quoteid: '+ quoteId);
        System.debug('** doctype = '+ docType);
        List<zqu__quote__c> quotes = [select zqu__opportunity__c from zqu__quote__c where id = :quoteId limit 1];
        if (quotes.isEmpty()) {
            return 'No quotes found.';
        }
        
        // query for opp to pull licenses cost
        List<Opportunity> opps = [select name,Custom_SOW__c,Number_of_Seats__c, StageName, Record_Type_Name__c, sales_engineer__r.email, bundles__c,SOWException__c, account.billingCountry,rvpe__RVAccount__r.Name, Use_Case__c from opportunity where id = :quotes[0].zqu__opportunity__c limit 1];
        if (opps.isEmpty()) {
            return noOpportunitiesResponse;
        }
        if (opps[0].Custom_SOW__c == true) {
            return 'Unable to process Autogen request due to Custom SOW already generated. Please send a Chatter message to @proserv for assistance.';
        }
        
        //if the opportunity stage is not at least stage 4 throw this error.
        List<String> stageList = new List<String>{'Stage 4-Shortlist', 'Stage 5-Verbal', 'Stage 6-Legal / Contracting','Stage 7-Closed Won','Stage 8-Closed Won: Finance'};  
            if ((!stageList.contains(opps[0].StageName)) && opps[0].Record_Type_Name__c =='New Business'){
                return notCorrectStage;
            }
        
        // if there is no amount return error
        if (opps[0].Number_of_Seats__c == null) {
            return noAmountResponse;
        }
        
        // get template name based on amount and bundles;  if no template is returned, it is above the threshold --> return over threshold response
        String templateName = getTemplateName(opps[0]);        
        if (templateName == null) {
            return oppOverThresholdResponse;
        }
        
        // query for template using name, get the ID
        List<zqu__Quote_Template__c> quoteTemplateList = [select zqu__Template_Id__c from zqu__Quote_Template__c where name = :templateName limit 1];
        if (quoteTemplateList.isEmpty()) {
            return noTemplateFoundResponse;
        }
        
        // make HTTP call to zuora to generate the document
        HttpResponse res = generateSOW(quoteTemplateList, quoteId, docType);
       
        if (res.getStatusCode() != 200) {
            return errorContactingZuoraResponse;
        }
        
        String zuoraResponse = res.getBody();
        
        if(Test.isRunningTest()){
            zuoraResponse = testSuccessMessage != null ? testSuccessMessage : 'Quote PDF document has been successfully AttachmentID: 10101';
        }
        
        // if response is successful, update the attachment name and response for SOW template
        String successMessage = 'document has been successfully';
        
        // list of objects to update (opp and attachment)
        List<sObject> recordsForUpdate = new List<sObject>();
        
        // keep track of any dml errors
        String dmlErrors = '';
        
        if (zuoraResponse.contains(successMessage)) {
            
            // replace 'Quote' with 'SOW'
            zuoraResponse = zuoraResponse.replace('Quote', 'SOW');
            
            // update opportunity with 'SOW Generated' = true
            recordsForUpdate.add(updateOpportunity(opps[0]));
            
            // update attachment name
            Attachment attachment = updateAttachment(zuoraResponse, opps[0], docType);
            if (attachment != null) {
                recordsForUpdate.add(attachment);
            } else {
                dmlErrors += 'No attachment found for update.';                
            }
            
            // send email to solution engineer
            notifySolutionEngineer(opps[0]);
        }
        
        if (!recordsForUpdate.isEmpty()) {
            List<Database.saveResult> results = Utils.saveRecords(recordsForUpdate, 'Update');
            dmlErrors += Utils.getResultErrorString(results);
        }
        
        // if there were DML errors, send email to admin
        if (String.isNotBlank(dmlErrors)) {
            String subject = 'Error(s) in ZuoraDocumentGenerator ' + Date.today();
            Utils.sendEmailToAdmin(subject, dmlErrors);
        }
        
        return zuoraResponse;
    }
    
    private static String getTemplateName(Opportunity opp) {
        String results = null;
        Account account = [select id, billingCountry from Account where id =:opp.AccountId];
        if (opp.Number_of_Seats__c > 50 ) {
            return results;
        }
        Set<String>useCase = new Set<String>();
        useCase.addAll(opp.Use_Case__c.split(';'));
        boolean isSales = false;
        boolean isSupport = false;
        for(String st : useCase){
            System.debug('*** st ='+st);
            if(useCase.contains('Sales')){
                isSales = true;
            }
            if(useCase.contains('Support')){
                isSupport = true;
            }
        }
        if(opp.rvpe__RVAccount__r.Name !=null && opp.rvpe__RVAccount__r.Name.contains('Mitel')){
            if (opp.Use_Case__c == 'Sales'|| (isSales && isSupport)) {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'SOW 1';           
                }
                else{
                    results =  'SOW 1';
                }
            } 
        }     
        if(account.billingCountry == 'United States' || account.billingCountry == 'USA' || account.billingCountry == 'US' || account.billingCountry == 'United States of America' || account.billingCountry == 'Canada' || account.billingCountry == 'CAN'){
            // Sales use case      
            if (opp.Use_Case__c == 'Sales'|| (isSales && isSupport)) {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'PPT SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'PPT SOW 1';           
                }
                else{
                    results =  'PPT SOW 2';
                }
            }           
            // Support use case
            else if(opp.Use_Case__c == 'Support') {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'PPT SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'PPT SOW 1';          
                }
                else{
                    results =  'PPT SOW 1';
                }        
            }
        }//this will be called if the billing country is not the US or Canada
        else{            
            if (opp.Use_Case__c == 'Sales'|| (isSales && isSupport)) {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'SOW 1';           
                }
                else{
                    results =  'SOW 1';
                }
            } 
            
            // Support use case
            else if(opp.Use_Case__c == 'Support') {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'SOW 1';          
                }
                else{
                    results =  'SOW 1';
                }        
            }
        }
        return results;  
    }
    
    public class SessionId {
        public String sessionId;
    }
    
    private static String getUserSessionId() {
        SessionId sessionJson = new SessionId();
        if(!Test.isRunningTest()) {
            sessionJson = (SessionId)JSON.deserialize(Page.ZuoraGenerateSOW.getContent().toString(), SessionId.class);
        }
        
        return sessionJson.sessionId;
    }
    
    private static HttpResponse generateSOW(List<zqu__Quote_Template__c> quoteTemplateList, Id quoteId, String docType) {
        // Generate Quote and attach to Opportunity
        Map<String,Object> zuoraConfigInfo = zqu.zQuoteUtil.getZuoraConfigInformation();
        
        Zuora.ZApi zApi = new Zuora.ZApi();
        Zuora.ZApi.LoginResult loginResult = new Zuora.ZApi.LoginResult();
        
        if(!Test.isRunningTest()){
            loginResult = zApi.zLogin();
        } else {
            loginResult.serverUrl = 'apisandbox';
        }
        
        String quoteServletUrl = loginResult.serverUrl.contains('apisandbox') ?
            'https://apisandbox.zuora.com/apps/servlet/GenerateQuote' :
        'https://zuora.com/apps/servlet/GenerateQuote';
        String sessionId = UserInfo.getSessionId();
        
        String sfdcUrl = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/u/10.0/' + UserInfo.getOrganizationId();
        
        PageReference generatePage = new PageReference(quoteServletUrl);
        generatePage.getParameters().put('templateId', quoteTemplateList[0].zqu__Template_Id__c);
        generatePage.getParameters().put('serverUrl', sfdcUrl);
        generatePage.getParameters().put('sessionId', getUserSessionId());
        generatePage.getParameters().put('quoteId', quoteId);
        generatePage.getParameters().put('attachToOpportunity', 'true');
        generatePage.getParameters().put('format', docType);
        generatePage.getParameters().put('ZSession', loginResult.session);
        generatePage.getParameters().put('useSFDCLocale', '1');
        generatePage.getParameters().put('locale', UserInfo.getLocale());
        
        // Zuora handles the attaching it to the opportunity through the https callout.         
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(generatePage.getUrl());
        req.setMethod('GET');
        req.setTimeout(30000);
        
        if (!Test.isRunningTest()) {
            HttpResponse response = h.send(req);
            System.debug('response>>>' + response);
            
            return response;
        } else {
            HttpResponse res = new HttpResponse();
            Integer statusCode = testStatusCode != null ? testStatusCode : 200;
            res.setStatusCode(statusCode);
            return res;
        }
    }
    
    private static Opportunity updateOpportunity(Opportunity opportunity) {
        // update boolean on opportunity to indicate an SOW was generated
        opportunity.SOW_Generated__c = true;
        opportunity.Bypass_Opportunity_Validation__c = true;
        return opportunity;
    }
    
    private static Attachment updateAttachment(String zuoraResponse, Opportunity opportunity, String docType) {
        String errorMessage = '';
        
        String attachmentId = zuoraResponse.split('AttachmentID:', 0)[1].normalizeSpace();          
        List<Attachment> attachments = [select id, name from attachment where id = :attachmentId limit 1];
        if (!attachments.isEmpty()) {
            attachments[0].name = 'SOW for ' + opportunity.name + '.' + docType;
            return attachments[0];
        } else {
            return null;
        }
    }
    
    private static void notifySolutionEngineer(Opportunity opportunity) {
        List<String> toAddresses = new List<String>();
        if (opportunity.Sales_Engineer__c != null) {
            toAddresses.add(opportunity.Sales_Engineer__r.email);
        } else {
            toAddresses.add();
            toAddresses.add();
        }
        
        String subject = 'An SOW has been generated for ' + opportunity.name;
        String baseURL = URL.getSalesforceBaseUrl().toExternalForm();        
        String body = subject + '.\n\nHere is a link to the opportunity:  ' + baseURL + '/' + opportunity.id;
        
        Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
        message.setToAddresses(toAddresses);
        message.setSubject(subject);
        message.setPlainTextBody(body);
        message.setSaveAsActivity(false);        
        EmailUtils.sendEmails(new List<Messaging.SingleEmailMessage>{message}, false);
    }
}

This is Apex class which generate and attached SOW, how can change this to it be attached to Files. Thank you for your help. 

 
Hi Everyone,

I need some help and guidance to increase code coverage for this trigger as I am not able to deeply this in production. 

Trigger on cases:
 
trigger CaseTrigger on Case(after insert, after update,before insert, before update) {
    
  Profile userProfile = [SELECT Id FROM Profile WHERE Name='Professional Services']; 
if(userProfile.Id != UserInfo.getProfileId()) 
return;
    
    switch on Trigger.operationType {
        when BEFORE_INSERT {
            CaseHelper.checkCaseAssignedRulesInsert(trigger.new);
        }
        when BEFORE_UPDATE{
            CaseHelper.checkCaseAssignedRulesInsert(trigger.new);
            CaseHelper.timestamp(trigger.new);
        }
        when AFTER_UPDATE {
            CaseHelper.checkCasePostUpdatesData(Trigger.oldMap, trigger.new);
        }
    }
}
Helper class:
global with sharing class CaseHelper {
    
    public static void checkCaseAssignedRulesInsert(List < Case > lstMap) {
        List<Id> mapOppCase = new List<Id>();

        for (Case unitCase: lstMap) {
            mapOppCase.add(unitCase.OppToCase__c);
        }

        Id caseRecordId = SobjectType.Case.getRecordTypeInfosByName().get('PS Only').getRecordTypeId();
List<Case> listCase = [SELECT Id,CaseNumber,Status From Case WHERE Status != 'Closed' AND OppToCase__c  in: mapOppCase and Id not in: lstMap and RecordTypeId =: caseRecordId ];
        for (Case unitCase: listCase) {
            AuraHandledException e = new AuraHandledException(null);
            e.setMessage('There is already a case assigned to this Opportunity,Case Number:' + unitCase.CaseNumber +'Only one case can be active at a time, please chatter @ProServ for additional requests.');
            throw e;
        }
    }
    
    public static void timestamp (List<Case> timestampcases){
        for (Case unitCase: timestampcases) {
            if(unitCase.Status == 'In Progress') unitCase.Time_Spent_in_New_Status__c = System.now();
            if(unitCase.Status == 'Additional Information Required') unitCase.Time_Spent_in_Progress__c = System.now();
            if(unitCase.Status == 'Waiting for Scoping Call') unitCase.Time_Spent_Additional_information_Requir__c = System.now();
            if(unitCase.Status == 'Closed') unitCase.Time_Spent_Waiting_for_Scoping_Call__c = System.now();
        }
    }

    public static void checkCasePostUpdatesData(Map < ID, Case > oldMap, List < Case > newMap) {
        Map < String, Schema.SObjectField > schemaFieldMap = Schema.SObjectType.Case.fields.getMap();
        List < ConnectApi.BatchInput > batchInputs = new List < ConnectApi.BatchInput > ();
        List < String > fieldToSkip = new List < String > {
             'Status'      
        };
   
        for (Case newCase: newMap) {
            system.debug('status'+newCase);
            String caseStatus = newCase.Status;
            if (newCase.OppToCase__c != null && oldMap != null) {
                Case oldCase = oldMap.get(newCase.Id);
                for (String fieldName: schemaFieldMap.keySet()) {
                    String fieldLabel = schemaFieldMap.get(fieldName).getDescribe().getLabel();
                    if (newCase.get(fieldName) != oldCase.get(fieldName) && fieldToSkip.contains(fieldLabel)) {
                        batchInputs.add(postOnOpportunityChatter(fieldLabel, String.valueOf(newCase.get(fieldName)), String.valueOf(oldCase.get(fieldName)), newCase.OppToCase__c, newcase.CaseNumber, String.valueOf(newcase.Request_Type__c),caseStatus));
                    }
                }
            }
        }
        if (!batchInputs.isEmpty()) {
            if(!Test.isRunningTest()) ConnectApi.ChatterFeeds.postFeedElementBatch(Network.getNetworkId(), batchInputs);
        }
    }

    public static ConnectApi.BatchInput postOnOpportunityChatter(String fieldLabel, String fieldNewValue, String fieldOldValue, Id opportunityID,String caseID,String caseType, String caseStatus) {

        List<Opportunity> Oppstatus = [SELECT StageName FROM Opportunity WHERE Id =: opportunityID];

        String closedWon = '';
        for(Opportunity pathStatus: Oppstatus){
            closedWon = pathStatus.StageName;
        }

        ConnectApi.FeedItemInput feedItemInput = new ConnectApi.FeedItemInput();
        ConnectApi.MentionSegmentInput mentionSegmentInput = new ConnectApi.MentionSegmentInput();
        ConnectApi.MessageBodyInput messageBodyInput = new ConnectApi.MessageBodyInput();
        ConnectApi.TextSegmentInput textSegmentInput = new ConnectApi.TextSegmentInput();
        messageBodyInput.messageSegments = new List < ConnectApi.MessageSegmentInput > ();

        ConnectApi.MarkupBeginSegmentInput markupBeginSegment = new ConnectApi.MarkupBeginSegmentInput();
        markupBeginSegment.markupType = ConnectApi.MarkupType.Bold;
        messageBodyInput.messageSegments.add(markupBeginSegment);

        textSegmentInput = new ConnectApi.TextSegmentInput();
        textSegmentInput.text = 'Scoping request '+ fieldLabel;
        messageBodyInput.messageSegments.add(textSegmentInput);

        ConnectApi.MarkupEndSegmentInput markupEndSegment = new ConnectApi.MarkupEndSegmentInput();
        markupEndSegment.markupType = ConnectApi.MarkupType.Bold;
        messageBodyInput.messageSegments.add(markupEndSegment);

        textSegmentInput = new ConnectApi.TextSegmentInput();
        textSegmentInput.text = ' was changed from ' + fieldOldValue + ' to ' + fieldNewValue+','+' Case ID:'+caseID+','+' Case Type:'+caseType ;
        if (caseStatus == 'Closed' && closedWon == 'Stage 8-Closed Won: Finance')textSegmentInput.text += ' Please submit your feedback about the Scoping Process here: https://www.surveymonkey.com/r/P99DSJW';
        messageBodyInput.messageSegments.add(textSegmentInput);
        feedItemInput.body = messageBodyInput;
        feedItemInput.feedElementType = ConnectApi.FeedElementType.FeedItem;
        feedItemInput.subjectId = opportunityID;
        return new ConnectApi.BatchInput(feedItemInput);
    }

}
Trigger Test Class:
@IsTest
public class CaseTriggerTest {
    
    @IsTest
    public static void insertCase () {
        Test.startTest();
        Account account1 = new Account(name = 'test');
        insert account1;
        Case case1 = new Case(Status = 'New', Origin = 'Phone', Priority = 'Medium', Request_Type__c = 'Redlines', Accurate_zQuote__c = true, SQD_Attached__c = true);
        Opportunity opp2 = new Opportunity(accountId = account1.Id, closeDate = Date.today(), stageName = 'Stage 1-Identified Opp', forecastCategoryName = 'Probable', name = 'test opp 1');
        case1.Status = 'Closed';
        opp2.Case__c = case1.Id;
        case1.OppToCase__c = opp2.Id;
        insert opp2;
        update opp2;
        insert case1;
        update case1;
        Test.stopTest();
    }
}

I am only getting 27% coverage and when I am trying to deploy it am getting an error I need 75%. Cam someone help thank you. 
 
Hi There,

I am looking for some help to create Territory Name Field in account object which lookup to standard Territory object and gets the name of that Territory. 

User-added image
Both of these objects have no relationship and I do not seem to find a way with either custom lookup field or process builder or flows, Territory object just doesn't show up. 

Anyone have any idea how can I able to create a relationship between these 2? thank you. 
Hi everyone,

I am trying to add showing in this screenshot but I am getting this  Error: Compiled formula is too big to execute (6,985 characters). Maximum size is 5,000 characters.

User-added image

Both of these fields which I am trying to add are also formula fields (number). 

Account_Impact_Score__c
Risk_Impact_Score__c

Is there a way to fix this issue? Thank you for your help.
Hello everyone. 

I need your help/guidance for looking at the lightning action button as I am not able to figure out why the lightning action button not working when I click on options and I get this message but nothing happened.
User-added imageHere is the code in .HTML file inside lwc
 
<template>
    <!-- <div class="slds-text-heading_small">If your profile is either Solutions engineer, Professional Services or System Administrator
            you will have the option to create a Word or PDF SOW. If not the SOW will be saved as PDF.</div> -->
            <lightning-button variant="brand" label="Create PDF SOW" title="PDF SOW" onclick={createPdf} class="slds-var-m-left_x-small"></lightning-button>
    <template if:true={showSecondButton}>
        <lightning-button variant="brand" label="Create Doc SOW" title="PDF SOW" onclick={createDoc} class="slds-var-m-left_x-small"></lightning-button>
    </template>
    <br>
    <template if:true={error}>
            The following error has occured. {error}
            Please report error to salesops.
    </template> 
    <template if:true={results}>
           Results :{results}. please click the close button at the bottom of this dialog.
    </template>
    </template>

Here is the code I have in .JS file inside lwc
 
import { LightningElement, api, track } from 'lwc';
import getCurrentUserProfileName from '@salesforce/apex/LightningUtils.getCurrentUserProfileName';
import generateSOW from '@salesforce/apex/ZuoraDocumentGenerator.generateSOW';
export default class QuickActionDocGen extends LightningElement {
    @api recordId;
    @track showSecondButton = false;
    @api profileName;
    @api error;
    @api results;
    connectedCallback() {
       
        getCurrentUserProfileName()
        
            .then(result => {
                this.profileName = result;
                console.log('User Profile : ', result);
                // All the logic that we will use to show one or two buttons
                const profilesWithSecondButton = ['Solution Engineer','Professional Services','System Administrator'];
                this.showSecondButton = profilesWithSecondButton.includes(result);

            })
            .catch(error => {
                console.warn('Error Getting User Profile', error);
            })

    }
    createPdf () {
        this.results = generateSOW(this.recordId, 'PDF'); // calling apex method with record id and pdf
        //eval("$A.get('e.force:closeQuickAction').fire();");
        //this.dispatchEvent(closeclickedevt); 
        window.close();
    }
    createDoc () {
        this.results = generateSOW(this.recordId, 'DOC'); // calling apex method with record id and Doc
        window.close();
    }
}

Can Anyone please help me with this? thank you. 
Hello all,

I have the apex class which not getting any code coverage would you able to help me fix it or what is wrong here, thank you. 

Class: 
 
public class ChangePassword 
{
    @InvocableMethod(
        label = 'User Password Reset'
        description = 'User Password Reset'
    )
    
    public static List <String> getUserId (List <ID> ids){
        List<String> userId = new List <String> ();
        List<User> userList = [SELECT Id from User WHERE 
                                        IsActive = true AND 
                                        CreatedDate = TODAY AND 
                                        LastLoginDate = NULL AND
                                        LastPasswordChangeDate = NULL AND
                                        Id in :ids 
                                        LIMIT 100];   
        for(User u : userList)
                {
                      system.resetPassword(u.Id, true);
                      System.debug('DONE: ' + u.Id);
                }
        return userId;
    }
}

test:
 
@isTest
private class ChangePasswordTest {

    @testSetup
    static void ChangePassword () {

        Profile p = [ SELECT id FROM Profile WHERE name = 'Standard User' ];

        Test.startTest();   
        User u = new User ();
        u.firstName = 'test1';
        u.lastName = 'test2';
        u.ProfileId = p.Id ;
        u.Username = 'test@email4x.com' ;
        u.Email = 'test@email.com';
        u.Alias = 't1';
        u.EmailEncodingKey = 'ISO-8859-1';
        u.LocaleSidKey = 'en_US' ;
        u.TimeZoneSidKey = 'America/Los_Angeles';
        u.LanguageLocaleKey = 'en_US' ;
        Insert u ;
        system.resetPassword(u.Id, true);
        System.debug('DONE: ' + u.Id);
        Test.stopTest();

    }

}

​​​​​​​

 
I am getting this ERROR System.QueryException: List has no rows for assignment to SObject on the following lines of code:
 
public static void insertCase(Case caseData) {
        insert caseData;
        Opportunity unitOpportunity = [select Id from Opportunity where Id =: caseData.OppToCase__c limit 1];  
        unitOpportunity.Case__c = caseData.Id;
        update unitOpportunity; 
    }

I tried to use try and catch but then I was not able to link the case back to opportunity, probably I am missing something very small but not able to figure this one out, can someone help, thank you. 
Hello,

I have a related list on account object called " Billing Accounts " (zuora__customeraccount__c).

One account record can have multiple billing accounts records.

On the order object, we have a field called "billing instance" (Billing_Account_Instance__c) which is a lookup to the "billing account" (zuora__customeraccount__c) object.

Currently, users have to populate this field once the order is created.

Now what I am trying to solve is if one account has one billing account populate that billing account in that field once the order is created. 

But if multiple billing accounts exist then not do populate anything so our billing team can choose the most appropriate billing instance for that order.

Can I get some guidance on how I can do that, thank you. 
 
Hello I need help to figure out when the VF page is not able to call the Lightning component correctly 

Following this article: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/components_visualforce.htm

https://developer.salesforce.com/forums/?id=9060G0000005jWmQAI

When I make a call I am able to launch the lightning component in the VF page but if I click any button it Is not working and I am getting this error.

User-added image
When I click the cancel button it should redirect me back to the opportunity record page but it not happening.

If I use that lightning component in a quote action it works perfectly but not when I call from the VF page. 

any thought? 
Hi,

I have an issue where flow works but now when I enter all the inputs screen components and click next and finish.

Instead of redirecting me back to the current opportunity page, I am staying on 1st screen component like the process is starting again.

here is my code do you do what is the problem in this:
 
<apex:page controller="EngageDealSupportButtonClass" lightningstylesheets="true">
    <apex:param name="opportunityId" value="{!$CurrentPage.parameters.opportunityId}"/>
    <html>
        <head>
            <apex:includeLightning />
        </head>
        <body class="slds-scope">
            <div id="flowContainer" />
            <script>
            var statusChange = function (event) {
                if(event.getParam("status") === "FINISHED") {
                    // Control what happens when the interview finishes
                    
                    var outputVariables = event.getParam("outputVariables");
                    var outputVar;
                    for(var i = 0; i < outputVariables.length; i++) {
                        outputVar = outputVariables[i];
                        if(outputVar.name === "redirect") {
                            var urlEvent = $A.get("e.force:navigateToSObject");
                            urlEvent.setParams({
                                "recordId": outputVar.value,
                                "isredirect": "true"
                            });
                            urlEvent.fire();
                        }
                    }
                }
            };
            $Lightning.use("c:lightflowApp", function() {
                // Create the flow component and set the onstatuschange attribute
                $Lightning.createComponent("lightning:flow", {"onstatuschange":statusChange},
                                           "flowContainer",
                                           function (component) {
                                               // Set the input variables
                                               var inputVariables = [
                                                   {
                                                       name : 'opportunityId',
                                                       type : 'SObject',
                                                       value : component.get ("v.recordId")
                                                   }
                                               ];
                                               
                                               // Start an interview in the flowContainer div, and 
                                               // initializes the input variables.
                                               component.startFlow("pservicerequest", inputVariables);
                                           }
                                          );
            });
            </script>
        </body>
    </html>
</apex:page>
any thoughts on how can I resolve this? thank you.
Hello,

I am getting this error when calling lightning flow from a VF page. It s a screen flow and when I click on next I get this error.

Any thoughts? 
Hi Everyone,

Looking for some help on how to fix this test class and code coverage issue as I am getting System.NullPointerException: Attempt to de-reference a null object on line 33.

Apex Class:
 
public class MqlService {
    public void setMqlDate(List<sObject> newRecords, Map<Id, sObject> oldRecordsById) {
        List<sObject> records = getOpenRecords(newRecords, oldRecordsById);
        if (records.isEmpty()) {
            return;
        }
        
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours'];
        for (sObject record : records) {
            // using this BusinessHours method will set the time to NOW if it is within the business hours
            // otherwise it will set the time to the time the business hours start next
            record.put('Mql_Date__c', BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()));
        }
    }
    
    // returns a list of records that are either inserted as 'Open', or updated to 'Open'
    private List<sObject> getOpenRecords(List<sObject> newRecords, Map<Id, sObject> oldRecordsById) {
        List<sObject> filteredRecords = new List<sObject>();
        for (sObject newRecord : newRecords) {
            if (!recordIsOpen(newRecord)) {
                continue;
            }
            
            if (isInsert(newRecord, oldRecordsById)) {
                filteredRecords.add(newRecord);
                continue;
            }
            
            if (oldRecordsById != null) {
                sObject oldRecord = oldRecordsById.get((Id)newRecord.get('id'));                
                if (oldRecord != null && !recordIsOpen(oldRecord)) {
                    filteredRecords.add(newRecord);                    
                }
            }
        }
        
        return filteredRecords;
    }
    
    private boolean isInsert(sObject newRecord, Map<Id, sObject> oldRecordsById) {
        return oldRecordsById == null || !oldRecordsById.containsKey((Id)newRecord.get('id'));
    }
                
    private boolean recordIsOpen(sObject record) {
        if (record.getSObjectType() == Lead.getSObjectType()) {
            return record.get('Status') == 'Open';
        } else if (record.getSObjectType() == Contact.getSObjectType()) {
            return record.get('Lead_Status__c') == 'Open';
        } else {
            return false;
        }
    }      
}

Test Class:
 
@isTest
public class MqlServiceTest {
    public static testMethod void testSetMqlDate() {
        // retrieve business hours
        BusinessHours hours = [select id from BusinessHours where name = 'MQL Hours'];
        
        // account for contact
        Account account = new Account(name = 'test');
        insert account;

		// lead / contact to update as Open
		Lead updateLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Pre-MQL');
        Contact updateContact = new Contact(lastName = 'test', accountId = account.id, lead_status__c = 'Pre-MQL');
        insert new List<sObject>{updateLead, updateContact};
            
        // update lead and contact
        updateLead.status = 'Open';
        updateContact.lead_status__c = 'Open';
            
        // lead / contact to insert as Open
        Lead insertLead = new Lead(lastName = 'test', company = 'test', email = 'test@test.com', status = 'Open');
        Contact insertContact = new Contact(lastName = 'test', accountId = account.id, lead_status__c = 'Open');            
            
        // capture the current time to check against business hours, set the time in time utils
        TimeUtils.setNow(Datetime.now());            
        
        // upsert list of leads / contacts to insert / update
        upsert new List<Lead>{insertLead, updateLead};
        upsert new List<Contact>{insertContact, updateContact};
        
        // assert the hour / minutes are correct, since the BusinessHours.nextStartDate doesn't use seconds
        for (Lead lead : [select mql_date__c from lead]) {
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).hour(), lead.mql_date__c.hour());
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).minute(), lead.mql_date__c.minute());            
        }
        
        for (Contact contact : [select mql_date__c from contact]) {
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).hour(), contact.mql_date__c.hour());
        	system.assertEquals(BusinessHours.nextStartDate(hours.id, TimeUtils.getNow()).minute(), contact.mql_date__c.minute());            
        }
    }
}

Because of line 33 assert method filing calls is not getting any code coverage as well.

Thank you for your help. ​​​​​​​
Hi everyone,

I have this validation rule which only allows admins and superusers to change account owners but I need to add one more condition if the user role contains any of these words.

RVP, ES AE, CS AE, Sales Director

then DO NOT let them change account owners everyone else should be able to change the account owner.

Here is my validation rule:
 
AND(
/* profile exceptions */
$Profile.Name <> 'System Administrator',
$Profile.Name <> 'Super User',


/* account type is Sales */
ISPICKVAL(Account_Type__c, 'Sales'),

ISCHANGED(OwnerId)
)

How can I make this adjustment, thank you for any help.
Hi Everyone,

Having some issues creating a test class for the controller.
 
public without sharing class DeleteFilesCtrl {
    @AuraEnabled
    public static List<FilesWrap> getFiles(string objectId){
        List<FilesWrap> wrapList = new List<FilesWrap>();
    	List<ContentDocumentLink> cdList = [SELECT ContentDocument.Title,ContentDocument.FileType,ContentDocument.LastModifiedDate , ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId =:objectId];
        for(ContentDocumentLink cd : cdList){
            if(cd.ContentDocument.FileType != 'SNOTE'){
        		wrapList.add(new FilesWrap(cd));
            }
        }
        System.debug('---'+wrapList);
        return wrapList;
    }
    @AuraEnabled
    public static string deleteFiles(string objectId,string contentDocumentId){
    	List<ContentDocumentLink> cdlList = [SELECT ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE ContentDocumentId=:contentDocumentId];
        try{    
        	if(cdlList.size() <= 2){
               delete [SELECT Id FROM ContentDocument WHERE Id = :contentDocumentId];
            }else{
                delete [SELECT ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId=:objectId AND ContentDocumentId=:contentDocumentId];    
            }
        }catch(Exception ex){
            return ex.getMessage();
        }
        return 'Success';
    }    
    public class FilesWrap{
        @AuraEnabled public String title;
        @AuraEnabled public String fileType;
        @AuraEnabled public String id;
        @AuraEnabled public DateTime lastModifiedDate;
        
        public FilesWrap(ContentDocumentLink cd){
            title = cd.ContentDocument.Title;
            id = cd.ContentDocumentId;
            fileType = cd.ContentDocument.FileType;
            lastModifiedDate= cd.ContentDocument.LastModifiedDate;
        }
    }
}

I am have tried many ways but either I am not getting any coverages or getting errors. Would love some helpful insight on how to create a test for this. thank you. 
 
Hi Everyone, 

I have this Apex class which creates SOW and attached to "Notes & Attachments"  and I want to change that so when we generate SOW it attach to files instead.
 
/**
* This controller will generate a Quote PDF and attach it to the Quote's parent Opportunity.
*/
global class ZuoraDocumentGenerator {
    @testVisible private static final string noAmountResponse = 'Please add ACV to the opportunity before generating an SOW';
    @testVisible private static final string noOpportunitiesResponse = 'No opportunities found.';
    @testVisible private static final string notCorrectStage= 'You cannot generate an SOW before Stage 4, please fill out the required integration fields and move your Opportunity to Stage 4 to continue generating the SOW.';
    @testVisible private static final string oppOverThresholdResponse = 'Please chatter <a href="/_ui/core/chatter/groups/GroupProfilePage?g=0F90d0000008YEA" target="_blank">@Professional Services</a> in order to get an SOW generated';
    @testVisible private static final string noTemplateFoundResponse = 'No SOW templates are setup.';
    @testVisible private static final string errorContactingZuoraResponse = 'Error contacting Zuora';
    @testVisible private static integer testStatusCode;
    @testVisible private static string testSuccessMessage;
    
    // Generates PDF and attaches to Quote's parent Opportunity Object.
    @AuraEnabled
    webservice static String generateSOW(Id quoteId, String docType) {
        // query for quote to pull opportunity ID
        System.debug('** quoteid: '+ quoteId);
        System.debug('** doctype = '+ docType);
        List<zqu__quote__c> quotes = [select zqu__opportunity__c from zqu__quote__c where id = :quoteId limit 1];
        if (quotes.isEmpty()) {
            return 'No quotes found.';
        }
        
        // query for opp to pull licenses cost
        List<Opportunity> opps = [select name,Custom_SOW__c,Number_of_Seats__c, StageName, Record_Type_Name__c, sales_engineer__r.email, bundles__c,SOWException__c, account.billingCountry,rvpe__RVAccount__r.Name, Use_Case__c from opportunity where id = :quotes[0].zqu__opportunity__c limit 1];
        if (opps.isEmpty()) {
            return noOpportunitiesResponse;
        }
        if (opps[0].Custom_SOW__c == true) {
            return 'Unable to process Autogen request due to Custom SOW already generated. Please send a Chatter message to @proserv for assistance.';
        }
        
        //if the opportunity stage is not at least stage 4 throw this error.
        List<String> stageList = new List<String>{'Stage 4-Shortlist', 'Stage 5-Verbal', 'Stage 6-Legal / Contracting','Stage 7-Closed Won','Stage 8-Closed Won: Finance'};  
            if ((!stageList.contains(opps[0].StageName)) && opps[0].Record_Type_Name__c =='New Business'){
                return notCorrectStage;
            }
        
        // if there is no amount return error
        if (opps[0].Number_of_Seats__c == null) {
            return noAmountResponse;
        }
        
        // get template name based on amount and bundles;  if no template is returned, it is above the threshold --> return over threshold response
        String templateName = getTemplateName(opps[0]);        
        if (templateName == null) {
            return oppOverThresholdResponse;
        }
        
        // query for template using name, get the ID
        List<zqu__Quote_Template__c> quoteTemplateList = [select zqu__Template_Id__c from zqu__Quote_Template__c where name = :templateName limit 1];
        if (quoteTemplateList.isEmpty()) {
            return noTemplateFoundResponse;
        }
        
        // make HTTP call to zuora to generate the document
        HttpResponse res = generateSOW(quoteTemplateList, quoteId, docType);
       
        if (res.getStatusCode() != 200) {
            return errorContactingZuoraResponse;
        }
        
        String zuoraResponse = res.getBody();
        
        if(Test.isRunningTest()){
            zuoraResponse = testSuccessMessage != null ? testSuccessMessage : 'Quote PDF document has been successfully AttachmentID: 10101';
        }
        
        // if response is successful, update the attachment name and response for SOW template
        String successMessage = 'document has been successfully';
        
        // list of objects to update (opp and attachment)
        List<sObject> recordsForUpdate = new List<sObject>();
        
        // keep track of any dml errors
        String dmlErrors = '';
        
        if (zuoraResponse.contains(successMessage)) {
            
            // replace 'Quote' with 'SOW'
            zuoraResponse = zuoraResponse.replace('Quote', 'SOW');
            
            // update opportunity with 'SOW Generated' = true
            recordsForUpdate.add(updateOpportunity(opps[0]));
            
            // update attachment name
            Attachment attachment = updateAttachment(zuoraResponse, opps[0], docType);
            if (attachment != null) {
                recordsForUpdate.add(attachment);
            } else {
                dmlErrors += 'No attachment found for update.';                
            }
            
            // send email to solution engineer
            notifySolutionEngineer(opps[0]);
        }
        
        if (!recordsForUpdate.isEmpty()) {
            List<Database.saveResult> results = Utils.saveRecords(recordsForUpdate, 'Update');
            dmlErrors += Utils.getResultErrorString(results);
        }
        
        // if there were DML errors, send email to admin
        if (String.isNotBlank(dmlErrors)) {
            String subject = 'Error(s) in ZuoraDocumentGenerator ' + Date.today();
            Utils.sendEmailToAdmin(subject, dmlErrors);
        }
        
        return zuoraResponse;
    }
    
    private static String getTemplateName(Opportunity opp) {
        String results = null;
        Account account = [select id, billingCountry from Account where id =:opp.AccountId];
        if (opp.Number_of_Seats__c > 50 ) {
            return results;
        }
        Set<String>useCase = new Set<String>();
        useCase.addAll(opp.Use_Case__c.split(';'));
        boolean isSales = false;
        boolean isSupport = false;
        for(String st : useCase){
            System.debug('*** st ='+st);
            if(useCase.contains('Sales')){
                isSales = true;
            }
            if(useCase.contains('Support')){
                isSupport = true;
            }
        }
        if(opp.rvpe__RVAccount__r.Name !=null && opp.rvpe__RVAccount__r.Name.contains('Mitel')){
            if (opp.Use_Case__c == 'Sales'|| (isSales && isSupport)) {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'SOW 1';           
                }
                else{
                    results =  'SOW 1';
                }
            } 
        }     
        if(account.billingCountry == 'United States' || account.billingCountry == 'USA' || account.billingCountry == 'US' || account.billingCountry == 'United States of America' || account.billingCountry == 'Canada' || account.billingCountry == 'CAN'){
            // Sales use case      
            if (opp.Use_Case__c == 'Sales'|| (isSales && isSupport)) {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'PPT SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'PPT SOW 1';           
                }
                else{
                    results =  'PPT SOW 2';
                }
            }           
            // Support use case
            else if(opp.Use_Case__c == 'Support') {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'PPT SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'PPT SOW 1';          
                }
                else{
                    results =  'PPT SOW 1';
                }        
            }
        }//this will be called if the billing country is not the US or Canada
        else{            
            if (opp.Use_Case__c == 'Sales'|| (isSales && isSupport)) {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'SOW 1';           
                }
                else{
                    results =  'SOW 1';
                }
            } 
            
            // Support use case
            else if(opp.Use_Case__c == 'Support') {
                if (opp.Number_of_Seats__c > 15) {
                    results = 'SOW 2';
                } else if (opp.Number_of_Seats__c <= 14) {
                    results =  'SOW 1';          
                }
                else{
                    results =  'SOW 1';
                }        
            }
        }
        return results;  
    }
    
    public class SessionId {
        public String sessionId;
    }
    
    private static String getUserSessionId() {
        SessionId sessionJson = new SessionId();
        if(!Test.isRunningTest()) {
            sessionJson = (SessionId)JSON.deserialize(Page.ZuoraGenerateSOW.getContent().toString(), SessionId.class);
        }
        
        return sessionJson.sessionId;
    }
    
    private static HttpResponse generateSOW(List<zqu__Quote_Template__c> quoteTemplateList, Id quoteId, String docType) {
        // Generate Quote and attach to Opportunity
        Map<String,Object> zuoraConfigInfo = zqu.zQuoteUtil.getZuoraConfigInformation();
        
        Zuora.ZApi zApi = new Zuora.ZApi();
        Zuora.ZApi.LoginResult loginResult = new Zuora.ZApi.LoginResult();
        
        if(!Test.isRunningTest()){
            loginResult = zApi.zLogin();
        } else {
            loginResult.serverUrl = 'apisandbox';
        }
        
        String quoteServletUrl = loginResult.serverUrl.contains('apisandbox') ?
            'https://apisandbox.zuora.com/apps/servlet/GenerateQuote' :
        'https://zuora.com/apps/servlet/GenerateQuote';
        String sessionId = UserInfo.getSessionId();
        
        String sfdcUrl = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/u/10.0/' + UserInfo.getOrganizationId();
        
        PageReference generatePage = new PageReference(quoteServletUrl);
        generatePage.getParameters().put('templateId', quoteTemplateList[0].zqu__Template_Id__c);
        generatePage.getParameters().put('serverUrl', sfdcUrl);
        generatePage.getParameters().put('sessionId', getUserSessionId());
        generatePage.getParameters().put('quoteId', quoteId);
        generatePage.getParameters().put('attachToOpportunity', 'true');
        generatePage.getParameters().put('format', docType);
        generatePage.getParameters().put('ZSession', loginResult.session);
        generatePage.getParameters().put('useSFDCLocale', '1');
        generatePage.getParameters().put('locale', UserInfo.getLocale());
        
        // Zuora handles the attaching it to the opportunity through the https callout.         
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(generatePage.getUrl());
        req.setMethod('GET');
        req.setTimeout(30000);
        
        if (!Test.isRunningTest()) {
            HttpResponse response = h.send(req);
            System.debug('response>>>' + response);
            
            return response;
        } else {
            HttpResponse res = new HttpResponse();
            Integer statusCode = testStatusCode != null ? testStatusCode : 200;
            res.setStatusCode(statusCode);
            return res;
        }
    }
    
    private static Opportunity updateOpportunity(Opportunity opportunity) {
        // update boolean on opportunity to indicate an SOW was generated
        opportunity.SOW_Generated__c = true;
        opportunity.Bypass_Opportunity_Validation__c = true;
        return opportunity;
    }
    
    private static Attachment updateAttachment(String zuoraResponse, Opportunity opportunity, String docType) {
        String errorMessage = '';
        
        String attachmentId = zuoraResponse.split('AttachmentID:', 0)[1].normalizeSpace();          
        List<Attachment> attachments = [select id, name from attachment where id = :attachmentId limit 1];
        if (!attachments.isEmpty()) {
            attachments[0].name = 'SOW for ' + opportunity.name + '.' + docType;
            return attachments[0];
        } else {
            return null;
        }
    }
    
    private static void notifySolutionEngineer(Opportunity opportunity) {
        List<String> toAddresses = new List<String>();
        if (opportunity.Sales_Engineer__c != null) {
            toAddresses.add(opportunity.Sales_Engineer__r.email);
        } else {
            toAddresses.add();
            toAddresses.add();
        }
        
        String subject = 'An SOW has been generated for ' + opportunity.name;
        String baseURL = URL.getSalesforceBaseUrl().toExternalForm();        
        String body = subject + '.\n\nHere is a link to the opportunity:  ' + baseURL + '/' + opportunity.id;
        
        Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
        message.setToAddresses(toAddresses);
        message.setSubject(subject);
        message.setPlainTextBody(body);
        message.setSaveAsActivity(false);        
        EmailUtils.sendEmails(new List<Messaging.SingleEmailMessage>{message}, false);
    }
}

This is Apex class which generate and attached SOW, how can change this to it be attached to Files. Thank you for your help. 

 
Hi Everyone,

I need some help and guidance to increase code coverage for this trigger as I am not able to deeply this in production. 

Trigger on cases:
 
trigger CaseTrigger on Case(after insert, after update,before insert, before update) {
    
  Profile userProfile = [SELECT Id FROM Profile WHERE Name='Professional Services']; 
if(userProfile.Id != UserInfo.getProfileId()) 
return;
    
    switch on Trigger.operationType {
        when BEFORE_INSERT {
            CaseHelper.checkCaseAssignedRulesInsert(trigger.new);
        }
        when BEFORE_UPDATE{
            CaseHelper.checkCaseAssignedRulesInsert(trigger.new);
            CaseHelper.timestamp(trigger.new);
        }
        when AFTER_UPDATE {
            CaseHelper.checkCasePostUpdatesData(Trigger.oldMap, trigger.new);
        }
    }
}
Helper class:
global with sharing class CaseHelper {
    
    public static void checkCaseAssignedRulesInsert(List < Case > lstMap) {
        List<Id> mapOppCase = new List<Id>();

        for (Case unitCase: lstMap) {
            mapOppCase.add(unitCase.OppToCase__c);
        }

        Id caseRecordId = SobjectType.Case.getRecordTypeInfosByName().get('PS Only').getRecordTypeId();
List<Case> listCase = [SELECT Id,CaseNumber,Status From Case WHERE Status != 'Closed' AND OppToCase__c  in: mapOppCase and Id not in: lstMap and RecordTypeId =: caseRecordId ];
        for (Case unitCase: listCase) {
            AuraHandledException e = new AuraHandledException(null);
            e.setMessage('There is already a case assigned to this Opportunity,Case Number:' + unitCase.CaseNumber +'Only one case can be active at a time, please chatter @ProServ for additional requests.');
            throw e;
        }
    }
    
    public static void timestamp (List<Case> timestampcases){
        for (Case unitCase: timestampcases) {
            if(unitCase.Status == 'In Progress') unitCase.Time_Spent_in_New_Status__c = System.now();
            if(unitCase.Status == 'Additional Information Required') unitCase.Time_Spent_in_Progress__c = System.now();
            if(unitCase.Status == 'Waiting for Scoping Call') unitCase.Time_Spent_Additional_information_Requir__c = System.now();
            if(unitCase.Status == 'Closed') unitCase.Time_Spent_Waiting_for_Scoping_Call__c = System.now();
        }
    }

    public static void checkCasePostUpdatesData(Map < ID, Case > oldMap, List < Case > newMap) {
        Map < String, Schema.SObjectField > schemaFieldMap = Schema.SObjectType.Case.fields.getMap();
        List < ConnectApi.BatchInput > batchInputs = new List < ConnectApi.BatchInput > ();
        List < String > fieldToSkip = new List < String > {
             'Status'      
        };
   
        for (Case newCase: newMap) {
            system.debug('status'+newCase);
            String caseStatus = newCase.Status;
            if (newCase.OppToCase__c != null && oldMap != null) {
                Case oldCase = oldMap.get(newCase.Id);
                for (String fieldName: schemaFieldMap.keySet()) {
                    String fieldLabel = schemaFieldMap.get(fieldName).getDescribe().getLabel();
                    if (newCase.get(fieldName) != oldCase.get(fieldName) && fieldToSkip.contains(fieldLabel)) {
                        batchInputs.add(postOnOpportunityChatter(fieldLabel, String.valueOf(newCase.get(fieldName)), String.valueOf(oldCase.get(fieldName)), newCase.OppToCase__c, newcase.CaseNumber, String.valueOf(newcase.Request_Type__c),caseStatus));
                    }
                }
            }
        }
        if (!batchInputs.isEmpty()) {
            if(!Test.isRunningTest()) ConnectApi.ChatterFeeds.postFeedElementBatch(Network.getNetworkId(), batchInputs);
        }
    }

    public static ConnectApi.BatchInput postOnOpportunityChatter(String fieldLabel, String fieldNewValue, String fieldOldValue, Id opportunityID,String caseID,String caseType, String caseStatus) {

        List<Opportunity> Oppstatus = [SELECT StageName FROM Opportunity WHERE Id =: opportunityID];

        String closedWon = '';
        for(Opportunity pathStatus: Oppstatus){
            closedWon = pathStatus.StageName;
        }

        ConnectApi.FeedItemInput feedItemInput = new ConnectApi.FeedItemInput();
        ConnectApi.MentionSegmentInput mentionSegmentInput = new ConnectApi.MentionSegmentInput();
        ConnectApi.MessageBodyInput messageBodyInput = new ConnectApi.MessageBodyInput();
        ConnectApi.TextSegmentInput textSegmentInput = new ConnectApi.TextSegmentInput();
        messageBodyInput.messageSegments = new List < ConnectApi.MessageSegmentInput > ();

        ConnectApi.MarkupBeginSegmentInput markupBeginSegment = new ConnectApi.MarkupBeginSegmentInput();
        markupBeginSegment.markupType = ConnectApi.MarkupType.Bold;
        messageBodyInput.messageSegments.add(markupBeginSegment);

        textSegmentInput = new ConnectApi.TextSegmentInput();
        textSegmentInput.text = 'Scoping request '+ fieldLabel;
        messageBodyInput.messageSegments.add(textSegmentInput);

        ConnectApi.MarkupEndSegmentInput markupEndSegment = new ConnectApi.MarkupEndSegmentInput();
        markupEndSegment.markupType = ConnectApi.MarkupType.Bold;
        messageBodyInput.messageSegments.add(markupEndSegment);

        textSegmentInput = new ConnectApi.TextSegmentInput();
        textSegmentInput.text = ' was changed from ' + fieldOldValue + ' to ' + fieldNewValue+','+' Case ID:'+caseID+','+' Case Type:'+caseType ;
        if (caseStatus == 'Closed' && closedWon == 'Stage 8-Closed Won: Finance')textSegmentInput.text += ' Please submit your feedback about the Scoping Process here: https://www.surveymonkey.com/r/P99DSJW';
        messageBodyInput.messageSegments.add(textSegmentInput);
        feedItemInput.body = messageBodyInput;
        feedItemInput.feedElementType = ConnectApi.FeedElementType.FeedItem;
        feedItemInput.subjectId = opportunityID;
        return new ConnectApi.BatchInput(feedItemInput);
    }

}
Trigger Test Class:
@IsTest
public class CaseTriggerTest {
    
    @IsTest
    public static void insertCase () {
        Test.startTest();
        Account account1 = new Account(name = 'test');
        insert account1;
        Case case1 = new Case(Status = 'New', Origin = 'Phone', Priority = 'Medium', Request_Type__c = 'Redlines', Accurate_zQuote__c = true, SQD_Attached__c = true);
        Opportunity opp2 = new Opportunity(accountId = account1.Id, closeDate = Date.today(), stageName = 'Stage 1-Identified Opp', forecastCategoryName = 'Probable', name = 'test opp 1');
        case1.Status = 'Closed';
        opp2.Case__c = case1.Id;
        case1.OppToCase__c = opp2.Id;
        insert opp2;
        update opp2;
        insert case1;
        update case1;
        Test.stopTest();
    }
}

I am only getting 27% coverage and when I am trying to deploy it am getting an error I need 75%. Cam someone help thank you.