• lxch
  • NEWBIE
  • 50 Points
  • Member since 2017

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 10
    Questions
  • 16
    Replies
Hi all, I'm working on maintaining the Email Message Status. I appreciate if anyone knows how you can change New => Replied by apex.

I made a datatable of EmailMessages (works like email Inbox) and trigger onrowaction to update the email status from New => Read by Apex. However, I never know i we can change from New => Replied.

You can change manually press the button "Reply" or "Reply All" on each email record but this forces the users to leave the Email Mesage datatable and go to the Email Mesage record.

In this page, it's clealy said only users can change only the Status from New to Read by Apex or Flow. But is there any way to achieve to make the status from New => Replied.
https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_emailmessage.htm

Thanks!
  • October 17, 2021
  • Like
  • 0
Hi all, I'm working on Google Chat API. Using this example. Successfully posted into my Google Chat Room (via webhook) but failed to show the card. 
https://github.com/muenzpraeger/salesforce-google-hangouts-chat-apex-webhook
Anyone knows what I'm missing?

User-added image

But result is this...

User-added image
global class GoogleChatWebhook {
    
    global class GoogleChatData {
        @InvocableVariable
        public String webhookUrl;
        @InvocableVariable
        public Id accountId;
        @InvocableVariable
        public String accountName;
        @InvocableVariable
        public String accountNo;
    }
    
    @InvocableMethod
    public static void sendChat(List<GoogleChatData> chatData) {
        for (Integer i=0;i<chatData.size();i++) {
            sendOutputMessageToRoom(chatData.get(i).webhookUrl,
                                    chatData.get(i).accountId, 
                                    chatData.get(i).accountName,
                                    chatData.get(i).accountNo);
        }
    }
    
    @future(callout=true)
    public static void sendOutputMessageToRoom(String webhookUrl,
                                               String accountId,
                                               String accountName,
                                               String accountNo) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(webhookUrl);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json;charset=UTF-8');
        
        String url = System.Url.getSalesforceBaseUrl().toExternalForm();
        String accountUrl = url + '/lightning/r/Account/' + accountId + '/view'; // Using the new URL format!
        
        String message = '{"cards":[{"header":{"title":"Salesforce Account alert","subtitle":"via Webhooks"},"sections":[{"widgets":[{"keyValue":{"topLabel":"Account No.","content":"' + accountNo + '"}},{"keyValue":{"topLabel":"Account Name","content":"' + accountName + '"}}]},{"widgets":[{"buttons":[{"textButton":{"text":"Open Account record","onClick":{"openLink":{"url":"' + accountUrl + '"}}}}]}]}]}]}';
        
        req.setBody(message);
        system.debug(message);

        Http http = new Http();
        HttpResponse res = http.send(req);
        system.debug(res);
    }
    
}
  • August 23, 2021
  • Like
  • 0
Chatter on Files/ContentDocument in Lightning is not available UNLESS you upload a new version of the File. Does anyone know how to generate a new FeedItem with parentId is ContentDocumentId by flow/Apex?

FIle / ContentDocument with Chatter Feed
I tested as follows:

1) SUCCESS
Simple post a chatter post, successfully made a new FeedItem.
FeedItem fi = new FeedItem();
fi.Body = 'Hello There';
fi.Title = 'File Name';
fi.parentId = '00510000000me1VAAQ'; // UserId
insert fi;

2) FAILURE
Chatter post with ParentId with ContentDocumentId is failed.
FeedItem fi = new FeedItem();
fi.Body = 'Hello There';
fi.Title = 'File Name';
fi.parentId = '0691000000Krzl1AAB'; //ContentDocumentId
insert fi;
I analyzed the FeedItem by a new Version, it shows the FeedItem as follows:
Id (feedItemId) : 0D510000047U2i1CAC
ParentId (File) : 0691000000Krzl1AAB
Type : TrackedChange

This may be because that the FeedItem is automatically generated by the Type "TrackedChange" upon a new Version is inserted. But TrackedChanged can't be inserted directly by Apex

Type "TextPost" is no enabled for ContentDocument.

Thank you for your advice!
  • August 17, 2021
  • Like
  • 0
I have difficulty to set the boder 0 on <div force-alohapage_alohapage... >.
css doesn't work on this DOM, but in the Chrome Inspector, I can set the border 0 to hide the hair-thin white border...

Anyone any idea?

hair-thin-boarder-line
  • June 24, 2021
  • Like
  • 0
Hi all, I have visualforce and added a visualforce tab. I want to use the same background image of Home Page across the custom tabs.

It looks I need to use slds brandband.
Brand Band
https://www.lightningdesignsystem.com/components/brand-band/

However, I don't know how to make the exactly same background on visualforce tab page from page top to the bottom like this.

Thank you for your help!
Brand-Band-Image
  • June 21, 2021
  • Like
  • 0
Hi all, if anyone spot what is the cause of in the following code error?
debug log tells me that is "argument can not be null."

User-added image
and it seems it null pointer exception issue in the loop of for (Billing__c due : dueBillings) part, but not sure what can fix this.

The logic is Apex Controller is called by controller.js fetchDueBalance method, pass the return value to the controller.js, then set component dueBalance (Decimal) in the component.

Apex Controller
@AuraEnabled
    public static Decimal fetchDueBalance(Id recordId) {
        // check which object started the apex class
        Decimal dueBalance;
        String sObjName = recordId.getSObjectType().getDescribe().getName();
        List<Billing__c> dueBillings = new List<Billing__c>();
        System.debug('dueBlalance: initial point: ' + dueBalance); //passed

        if (sObjName == 'Billing__c') {
            Billing__c bl = [
            SELECT Id, Case__c
            FROM Billing__c 
            WHERE Id =:recordId
            LIMIT 1
            ];

            dueBillings = [
                SELECT Id, Due_Balance__c
                FROM Billing__c
                WHERE Case__c = :bl.Case__c AND Due_Balance__c !=0 AND User_Due_Date__c < :Date.today()
                ORDER BY User_Due_Date__c ASC
            ];
            System.debug('dueBlalance: list search: ' + dueBalance); // passed

            if (dueBillings != null){
                System.debug('dueBlalance: nullcheck: ' + dueBalance); // passed
                dueBalance = 0;

                for (Billing__c due: dueBillings) {
                    dueBalance += due.Due_Balance__c;

                    System.debug('dueBlalance: loop point: ' + dueBalance); /passed
                }
            }
        }     
    return dueBalance;
    }
It passes to the final debug log, but not returning the dueBalance value (Decimal) to the component via controller.

Component
<aura:component controller="BillingCheckController" implements="force:hasRecordId,force:appHostable,flexipage:availableForAllPageTypes">
   <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
   <aura:attribute name="recordId" type="Id" />
   <aura:attribute name="dueBalance" type="Decimal" />
   <aura:attribute name="billings" type="Billing__c[]" description="store accounts with there child contacts"/>
   
   <h1><lightning:formattedNumber value="{!v.dueBalance}"></lightning:formattedNumber></h1>
Controller.js
({
    doInit: function(component, event, helper) {
       //call apex class method
        var recordId = component.get("v.recordId");
        var action = component.get('c.fetchBillings');
        action.setParams({recordId :recordId});
        action.setCallback(this, function(response) {
            var state = response.getState();
                if (state === "SUCCESS") {
                    component.set('v.billings', response.getReturnValue());
                }
        });
        $A.enqueueAction(action);

        /* action1 for the sumup number of the overdue billings */
        var recordId = component.get("v.recordId");
        var action = component.get('c.fetchDueBalance');
        action.setParams({recordId :recordId});
        action.setCallback(this, function(response) {
            //store state of response
            var state = response.getState();
                if (state === "SUCCESS") {
                    component.set('v.dueBalance', response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
    },
})
Help me sleep...

 
  • February 28, 2021
  • Like
  • 0
Hi all, I'm working on get the current record with some fields (Name, Current_Balance__c) by <force:recordData /> in aura component. How do we get the value from Controller and update the record field?

Thanks!

Component
<aura:component controller="DueBalanceController" implements="force:lightningQuickAction,force:appHostable,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
   <aura:attribute name="accountRecord" type="Object"/>
   <aura:attribute name="sumBalance" type="Object"/>
   <aura:attribute name="today" type="Date" />
   <aura:handler name="init" value="{!this}" action="{!c.init}"/>
    
    <force:recordData aura:id="recordLoader"
                      recordId="{!v.recordId}"
                      fields="Name,Current_Balance__c"
                      targetFields="{!v.accountRecord}"
                      /> 
    <div>
        <lightning:card aura:id="warning-box" title="Billing So-far" >
			<lightning:layout>
               <lightning:layoutItem class="slds-p-around_x-small" size="12">
                       <lightning:formattedNumber value="{!v.accountRecord.Current_Balance__c}"/></div>
               </lightning:layoutItem>
            </lightning:layout>
        </lightning:card>
    </div>
</aura:component>
Contorller.js
({
    init: function(component, event, helper) {
        var today = $A.localizationService.formatDate(new Date(), "YYYY-MM-DD");
        component.set('v.today', today);
        var accountRecord = component.get("v.accountRecord");
        var recordId = component.get ("v.recordId");      
        var action = component.get("c.sumBalance");
        action.setParams({recordId:recordId});
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
            component.set("v.sumBalance",response.getReturnValue());
            component.set("v.accountRecord.Current_Balance__c",sumBalance.bl);
            component.find("recordLoader").reloadRecord();
            } else {
                console.log("Failed with state: " + state);
            }
        });
        $A.enqueueAction(action);
    },
})

Apex Contorller
public with sharing class DueBalanceController{
    @AuraEnabled
    public static Object sumBalance (String recordId){
        Date d_today = system.Date.today();
        AggregateResult[] groupedResults = [
            SELECT SUM(Balance__c)bl,SUM(Paid_Total__c)pt,SUM(Billing_Total__c)bt, Tenant__c
            FROM Billing__c 
            WHERE Tenant__c	= :recordId AND (User_Due_Date__c <= :d_today OR Paid_Date__c <=:d_today) AND (Status__c='Unpaid' OR Status__c='Paid' OR Status__c='Captured' OR Status__c ='Chasing Overdue')
            GROUP BY Tenant__c
        ];
        return groupedResults[0];
       
    }
}



 
  • February 01, 2021
  • Like
  • 0

Based on this project (https://trailhead.salesforce.com/ja/content/learn/projects/develop-account-geolocation-app-with-aura-components), I'm working on the aura components that lists the accounts and leads on google map and table compoents of those data, search with search terms and other parameters.

That works fine. 

I want to tweak when I click the "view detail" button then open in a new tab (or open in background tab) instead of refreshing the same tab I was looking at. If anyone has any idea how to open in a new tab, please adivice.

I sense the issue is in the bottom part of the AccountListController.js
Thanks!

AccountListController.js
https://gist.github.com/liuxiachanghong/a2675b44a3f55bdcf864c38787baf438
 

({
    onAccountsLoaded: function( component, event, helper ) {
        var accounts = event.getParam( 'accounts' );
        if (accounts != null) {
            component.set( 'v.iconName', 'standard:account' );
            component.set( 'v.listName', 'Account List' );
            component.set( 'v.rows', event.getParam( 'accounts' ) );
            var cols = [
                {
                    'label': 'Name',
                    'fieldName': 'Name',
                    'initialWidth': 320,
                    'type': 'text'
                },
                {
                    'label': 'Industry',
                    'fieldName': 'Industry',
                    'initialWidth': 190,
                    'type': 'text'
                },
                {
                    'label': 'Type',
                    'fieldName': 'Client_Type_new__c',
                    'initialWidth': 100,
                    'type': 'text'
                },
                {
                    'label': 'Owner',
                    'fieldName': 'Owner_Alias__c',
                    'initialWidth': 110,
                    'type': 'text'
                },
                {
                    'label': 'Last',
                    'fieldName': 'Last_Sales_Activity_By__c',
                    'initialWidth': 110,
                    'type': 'text'
                },
                {
                    'label': 'Last At',
                    'fieldName': 'Last_Sales_Activity__c',
                    'initialWidth': 110,
                    'type': 'date'
                },
                {
                    'label': 'Last Won At',
                    'fieldName': 'Recent_Closed_Won_Date__c',
                    'initialWidth': 110,
                    'type': 'date'
                }, 
                {
                    'label': 'Deal',
                    'fieldName': 'Related_Opportunities_y__c',
                    'type': 'currency'
                },
                {
                    'label': 'Action',
                    'type': 'button',
                    'typeAttributes': {
                        'label': 'Details',
                        'name': 'view_details'
                    }
                }
            ];
        } else {
            component.set( 'v.iconName', 'standard:lead' );
            component.set( 'v.listName', 'Lead List' );
            component.set( 'v.rows', event.getParam( 'leads' ) );          
            var cols = [
                {
                    'label': 'Name',
                    'fieldName': 'LastName',
                    'type': 'text'
                },
                {
                    'label': 'Company',
                    'fieldName': 'Company',
                    'initialWidth': 320,
                    'type': 'text'
                },
                {
                    'label': 'Prefecture',
                    'fieldName': 'State',
                    'type': 'text'
                },
                {
                    'label': 'City',
                    'fieldName': 'City',
                    'type': 'text'
                },
                {
                    'label': 'Street',
                    'fieldName': 'Street',
                    'initialWidth': 320,
                    'type': 'text'
                },
                {
                    'label': 'Type',
                    'fieldName': 'Client_Type_new__c',
                    'type': 'text'
                },
                {
                    'label': 'Owner',
                    'fieldName': 'Owner__c',
                    'type': 'text'
                },
                {
                    'label': 'Action',
                    'type': 'button',
                    'typeAttributes': {
                        'label': 'Details',
                        'name': 'view_details'
                    }
                }
            ];
        }   
        component.set( 'v.cols', cols );
    },
    
    onRowAction: function( component, event, helper ) {
        var accounts = event.getParam( 'accounts' );
        var action = event.getParam( 'action' );
        var row = event.getParam( 'row' );
        if (accounts != null) {
             if ( action.name == 'view_details' ) {
                var navigation = component.find( 'navigation' );
                navigation.navigate({
                    'type': 'standard__recordPage',
                    'attributes': {
                        'objectApiName': 'Account',
                        'recordId': row.Id,
                        'actionName': 'view'
                    }
                });
        	} 
        } else {
            if ( action.name == 'view_details' ) {
                var navigation = component.find( 'navigation' );
                navigation.navigate({
                    'type': 'standard__recordPage',
                    'attributes': {
                        'objectApiName': 'Lead',
                        'recordId': row.Id,
                        'actionName': 'view'
                    }
                });
            }           
        }
    }
})
 


 

AccountList.cmp

https://gist.github.com/liuxiachanghong/705799f378c180a664cd70d52a13b58f

AccountsLoaded.event
https://gist.github.com/liuxiachanghong/c56f9b120ab6c60510db33b473aaeac7


 

  • January 31, 2021
  • Like
  • 0
I'm working on Apex Test Class. If anyone knows the problem the below test code. Thanks!

The Class is returning Object sumBalance with SUM values (alias: bl, bt, pt) from SOQL and component receives {v.sumBalance.bl}, {v.sumBalance.bt},  {v.sumBalance.pt}. These are working fine. But can't figure out how to test this Class....

Class
public with sharing class DueBalanceController{
    @AuraEnabled
    public static Object sumBalance (String recordId){
        Date d_today = system.Date.today();
        AggregateResult[] groupedResults = [
            SELECT SUM(Balance__c)bl,SUM(Paid_Total__c)pt,SUM(Billing_Total__c)bt, Tenant__c
            FROM Billing__c 
            WHERE Tenant__c	= :recordId AND User_Due_Date__c <= :d_today AND (Status__c='Unpaid' OR Status__c='Paid' OR Status__c='Captured')
            GROUP BY Tenant__c
        ];
        Object sumAmount = groupedResults[0];
        return sumAmount;
    }
}

Test Class
@isTest
public class DueBalanceControllerTest {
        
    @isTest 
    public static void testDueBalanceController(){
        String recordTypeId  = Schema.getGlobalDescribe().get('Account').getDescribe().getRecordTypeInfosByName().get('Person Account').getRecordTypeId();
        Account acct= new Account(
          RecordTypeID = recordTypeId ,
          FirstName = 'Test FName',
          LastName='Test LName',
          PersonMailingStreet='test@yahoo.com',
          PersonMailingPostalCode='12345',
          PersonMailingCity='SFO',
          PersonEmail='test@yahoo.com',
          PersonHomePhone='1234567',
          PersonMobilePhone='12345678' 
        );
        insert acct;
        
        Case cs = new Case(
        	Subject='Test',
            Status ='Closed'
        );
        insert cs;
            
        Billing__c bl = new Billing__c(Name='Test');
        bl.Monthly_Rent__c = 200000;
        bl.Paid_Total__c = 100000;
        bl.Status__c = 'Paid';
        bl.User_Due_Date__c = system.Date.today()-20;
        bl.Tenant__c = acct.Id;
        bl.Case__c = cs.Id;
        insert bl;
    
    Test.StartTest();
		Object sumBalance = DueBalanceController.sumBalance(acct.Id);
        System.assertNotEquals(null,sumBalance);
    Test.StopTest();
        
	}
}


 
  • January 30, 2021
  • Like
  • 0
This whole works fine in sandbox but I couldn't figure out where to test to clear the test coverage. Thanks for your help!

ApexClass
public with sharing class AccountSearchController {
    @AuraEnabled
    public static List<Account> searchAccounts( String searchTerm, String searchOption, Integer searchLimit, Integer searchRadius ) {            
        List<Account> accounts = new List<Account>(); 
        if ( String.isNotBlank( searchTerm ) ) {
        	List<List<SObject>> results = [FIND :searchTerm IN ALL FIELDS
                    RETURNING Account(
                      Id, BillingLatitude, BillingLongitude
                      WHERE BillingLatitude != Null AND BillingLongitude !=null
                      LIMIT 1)
            ];
            Account [] tempAccts = (Account[])results[0];
            Account centerAcct = tempAccts.get(0);
            
            List<List<SObject>> searchResults = [FIND :searchTerm IN ALL FIELDS
                RETURNING Account(
                    Id,Name,BillingAddress,Recent_Closed_Won_Date__c,Last_Sales_Activity__c,Last_Sales_Activity_By__c,Related_Opportunities_y__c,Owner_Alias__c,Client_Type_new__c,Industry,BillingState,BillingCity,BillingStreet,BillingPostalCode,BillingLatitude,BillingLongitude
                	WHERE DISTANCE (BillingAddress, Geolocation(:centerAcct.BillingLatitude, :centerAcct.BillingLongitude),'km')<:searchRadius
                    ORDER BY Related_Opportunities_y__c DESC
                    LIMIT :searchLimit
                )
            ];
            accounts = searchResults[0];
       
        }  else {
                List<List<SObject>> searchResults = [
                FIND '東京都' IN ALL FIELDS
                RETURNING Account(
                    Id,Name,BillingAddress,Recent_Closed_Won_Date__c,Last_Sales_Activity__c,Last_Sales_Activity_By__c,Related_Opportunities_y__c,Owner_Alias__c,Client_Type_new__c,Industry,BillingState,BillingCity,BillingStreet,BillingPostalCode,BillingLatitude,BillingLongitude
                    ORDER BY Related_Opportunities_y__c DESC
                    LIMIT :searchLimit
                )
            ];
            accounts = searchResults[0];
        }        
    	return accounts;
    }
    @AuraEnabled   
    public static List<Lead> searchLeads(  String searchTerm, String searchOption, Integer searchLimit, Integer searchRadius ) {            
        List<Lead> leads = new List<Lead>();   
        if ( String.isNotBlank( searchTerm ) ) {
	       	List<List<SObject>> results = [FIND :searchTerm IN ALL FIELDS
                           RETURNING Lead(
                           Id, Latitude, Longitude
                           WHERE Latitude != Null AND Longitude !=null
                           LIMIT 1)
                           ];
            Lead [] tempLeads = (Lead[])results[0];
            Lead centerLead = tempLeads.get(0);
            
            List<List<SObject>> searchResults = [
                FIND :searchTerm IN ALL FIELDS
                RETURNING Lead(
                    Id,LastName,Address,Company,State,City,Street,PostalCode,Latitude,Longitude,Owner__c, Client_Type_new__c
                    WHERE Latitude !=null AND Longitude !=null AND DISTANCE (Address, Geolocation(:centerLead.Latitude, :centerLead.Longitude),'km')<:searchRadius
                    ORDER BY Company DESC
                    LIMIT :searchLimit
                )
            ];
            leads = searchResults[0];
        }
    	return leads;
    }
}

Test Class
@isTest
public with sharing class AccountSearchControllerTest {

    @isTest
    public static void testAccountSearchController() {
        Account acct = new Account(Name ='Test');
        acct.BillingPostalCode = '105-0011';
        acct.BillingCountry = 'JP';
        acct.BillingState = 'Tokyo';
        acct.BillingCity = 'Minato';
        acct.BillingStreet = 'Shibakoen 3-1-1';
        acct.BillingLatitude = 35.661;
        acct.BillingLongitude = 139.748;
        insert acct;
                
        Lead lead = new Lead (LastName ='Test');
        lead.PostalCode = '105-0011';
        lead.Country = 'JP';
        lead.State = 'Tokyo';
        lead.City = 'Minato';
        lead.Street = 'Shibakoen 3-1-1';
        lead.Latitude = 35.661;
        lead.Longitude = 139.748; 
        insert lead;
        
        Test.startTest();
            List<Account> searchAccounts = AccountSearchController.searchAccounts(acct.BillingState,'option1',25,6);
            System.assertEquals(true,searchAccounts.isEmpty());
      
            List<Lead> searchLeads = AccountSearchController.searchLeads(lead.State,'option2',25,6);
            System.assertEquals(true,searchLeads.isEmpty());
        Test.stopTest();

    }
}

I attached the debug result with highlighting the lines are not test.
Thank you!
  • January 25, 2021
  • Like
  • 0
Hi all, I'm working on Google Chat API. Using this example. Successfully posted into my Google Chat Room (via webhook) but failed to show the card. 
https://github.com/muenzpraeger/salesforce-google-hangouts-chat-apex-webhook
Anyone knows what I'm missing?

User-added image

But result is this...

User-added image
global class GoogleChatWebhook {
    
    global class GoogleChatData {
        @InvocableVariable
        public String webhookUrl;
        @InvocableVariable
        public Id accountId;
        @InvocableVariable
        public String accountName;
        @InvocableVariable
        public String accountNo;
    }
    
    @InvocableMethod
    public static void sendChat(List<GoogleChatData> chatData) {
        for (Integer i=0;i<chatData.size();i++) {
            sendOutputMessageToRoom(chatData.get(i).webhookUrl,
                                    chatData.get(i).accountId, 
                                    chatData.get(i).accountName,
                                    chatData.get(i).accountNo);
        }
    }
    
    @future(callout=true)
    public static void sendOutputMessageToRoom(String webhookUrl,
                                               String accountId,
                                               String accountName,
                                               String accountNo) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(webhookUrl);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json;charset=UTF-8');
        
        String url = System.Url.getSalesforceBaseUrl().toExternalForm();
        String accountUrl = url + '/lightning/r/Account/' + accountId + '/view'; // Using the new URL format!
        
        String message = '{"cards":[{"header":{"title":"Salesforce Account alert","subtitle":"via Webhooks"},"sections":[{"widgets":[{"keyValue":{"topLabel":"Account No.","content":"' + accountNo + '"}},{"keyValue":{"topLabel":"Account Name","content":"' + accountName + '"}}]},{"widgets":[{"buttons":[{"textButton":{"text":"Open Account record","onClick":{"openLink":{"url":"' + accountUrl + '"}}}}]}]}]}]}';
        
        req.setBody(message);
        system.debug(message);

        Http http = new Http();
        HttpResponse res = http.send(req);
        system.debug(res);
    }
    
}
  • August 23, 2021
  • Like
  • 0
Chatter on Files/ContentDocument in Lightning is not available UNLESS you upload a new version of the File. Does anyone know how to generate a new FeedItem with parentId is ContentDocumentId by flow/Apex?

FIle / ContentDocument with Chatter Feed
I tested as follows:

1) SUCCESS
Simple post a chatter post, successfully made a new FeedItem.
FeedItem fi = new FeedItem();
fi.Body = 'Hello There';
fi.Title = 'File Name';
fi.parentId = '00510000000me1VAAQ'; // UserId
insert fi;

2) FAILURE
Chatter post with ParentId with ContentDocumentId is failed.
FeedItem fi = new FeedItem();
fi.Body = 'Hello There';
fi.Title = 'File Name';
fi.parentId = '0691000000Krzl1AAB'; //ContentDocumentId
insert fi;
I analyzed the FeedItem by a new Version, it shows the FeedItem as follows:
Id (feedItemId) : 0D510000047U2i1CAC
ParentId (File) : 0691000000Krzl1AAB
Type : TrackedChange

This may be because that the FeedItem is automatically generated by the Type "TrackedChange" upon a new Version is inserted. But TrackedChanged can't be inserted directly by Apex

Type "TextPost" is no enabled for ContentDocument.

Thank you for your advice!
  • August 17, 2021
  • Like
  • 0
Hi all, I have visualforce and added a visualforce tab. I want to use the same background image of Home Page across the custom tabs.

It looks I need to use slds brandband.
Brand Band
https://www.lightningdesignsystem.com/components/brand-band/

However, I don't know how to make the exactly same background on visualforce tab page from page top to the bottom like this.

Thank you for your help!
Brand-Band-Image
  • June 21, 2021
  • Like
  • 0
Hi all, if anyone spot what is the cause of in the following code error?
debug log tells me that is "argument can not be null."

User-added image
and it seems it null pointer exception issue in the loop of for (Billing__c due : dueBillings) part, but not sure what can fix this.

The logic is Apex Controller is called by controller.js fetchDueBalance method, pass the return value to the controller.js, then set component dueBalance (Decimal) in the component.

Apex Controller
@AuraEnabled
    public static Decimal fetchDueBalance(Id recordId) {
        // check which object started the apex class
        Decimal dueBalance;
        String sObjName = recordId.getSObjectType().getDescribe().getName();
        List<Billing__c> dueBillings = new List<Billing__c>();
        System.debug('dueBlalance: initial point: ' + dueBalance); //passed

        if (sObjName == 'Billing__c') {
            Billing__c bl = [
            SELECT Id, Case__c
            FROM Billing__c 
            WHERE Id =:recordId
            LIMIT 1
            ];

            dueBillings = [
                SELECT Id, Due_Balance__c
                FROM Billing__c
                WHERE Case__c = :bl.Case__c AND Due_Balance__c !=0 AND User_Due_Date__c < :Date.today()
                ORDER BY User_Due_Date__c ASC
            ];
            System.debug('dueBlalance: list search: ' + dueBalance); // passed

            if (dueBillings != null){
                System.debug('dueBlalance: nullcheck: ' + dueBalance); // passed
                dueBalance = 0;

                for (Billing__c due: dueBillings) {
                    dueBalance += due.Due_Balance__c;

                    System.debug('dueBlalance: loop point: ' + dueBalance); /passed
                }
            }
        }     
    return dueBalance;
    }
It passes to the final debug log, but not returning the dueBalance value (Decimal) to the component via controller.

Component
<aura:component controller="BillingCheckController" implements="force:hasRecordId,force:appHostable,flexipage:availableForAllPageTypes">
   <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
   <aura:attribute name="recordId" type="Id" />
   <aura:attribute name="dueBalance" type="Decimal" />
   <aura:attribute name="billings" type="Billing__c[]" description="store accounts with there child contacts"/>
   
   <h1><lightning:formattedNumber value="{!v.dueBalance}"></lightning:formattedNumber></h1>
Controller.js
({
    doInit: function(component, event, helper) {
       //call apex class method
        var recordId = component.get("v.recordId");
        var action = component.get('c.fetchBillings');
        action.setParams({recordId :recordId});
        action.setCallback(this, function(response) {
            var state = response.getState();
                if (state === "SUCCESS") {
                    component.set('v.billings', response.getReturnValue());
                }
        });
        $A.enqueueAction(action);

        /* action1 for the sumup number of the overdue billings */
        var recordId = component.get("v.recordId");
        var action = component.get('c.fetchDueBalance');
        action.setParams({recordId :recordId});
        action.setCallback(this, function(response) {
            //store state of response
            var state = response.getState();
                if (state === "SUCCESS") {
                    component.set('v.dueBalance', response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
    },
})
Help me sleep...

 
  • February 28, 2021
  • Like
  • 0
Hi all, I'm working on get the current record with some fields (Name, Current_Balance__c) by <force:recordData /> in aura component. How do we get the value from Controller and update the record field?

Thanks!

Component
<aura:component controller="DueBalanceController" implements="force:lightningQuickAction,force:appHostable,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
   <aura:attribute name="accountRecord" type="Object"/>
   <aura:attribute name="sumBalance" type="Object"/>
   <aura:attribute name="today" type="Date" />
   <aura:handler name="init" value="{!this}" action="{!c.init}"/>
    
    <force:recordData aura:id="recordLoader"
                      recordId="{!v.recordId}"
                      fields="Name,Current_Balance__c"
                      targetFields="{!v.accountRecord}"
                      /> 
    <div>
        <lightning:card aura:id="warning-box" title="Billing So-far" >
			<lightning:layout>
               <lightning:layoutItem class="slds-p-around_x-small" size="12">
                       <lightning:formattedNumber value="{!v.accountRecord.Current_Balance__c}"/></div>
               </lightning:layoutItem>
            </lightning:layout>
        </lightning:card>
    </div>
</aura:component>
Contorller.js
({
    init: function(component, event, helper) {
        var today = $A.localizationService.formatDate(new Date(), "YYYY-MM-DD");
        component.set('v.today', today);
        var accountRecord = component.get("v.accountRecord");
        var recordId = component.get ("v.recordId");      
        var action = component.get("c.sumBalance");
        action.setParams({recordId:recordId});
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
            component.set("v.sumBalance",response.getReturnValue());
            component.set("v.accountRecord.Current_Balance__c",sumBalance.bl);
            component.find("recordLoader").reloadRecord();
            } else {
                console.log("Failed with state: " + state);
            }
        });
        $A.enqueueAction(action);
    },
})

Apex Contorller
public with sharing class DueBalanceController{
    @AuraEnabled
    public static Object sumBalance (String recordId){
        Date d_today = system.Date.today();
        AggregateResult[] groupedResults = [
            SELECT SUM(Balance__c)bl,SUM(Paid_Total__c)pt,SUM(Billing_Total__c)bt, Tenant__c
            FROM Billing__c 
            WHERE Tenant__c	= :recordId AND (User_Due_Date__c <= :d_today OR Paid_Date__c <=:d_today) AND (Status__c='Unpaid' OR Status__c='Paid' OR Status__c='Captured' OR Status__c ='Chasing Overdue')
            GROUP BY Tenant__c
        ];
        return groupedResults[0];
       
    }
}



 
  • February 01, 2021
  • Like
  • 0

Based on this project (https://trailhead.salesforce.com/ja/content/learn/projects/develop-account-geolocation-app-with-aura-components), I'm working on the aura components that lists the accounts and leads on google map and table compoents of those data, search with search terms and other parameters.

That works fine. 

I want to tweak when I click the "view detail" button then open in a new tab (or open in background tab) instead of refreshing the same tab I was looking at. If anyone has any idea how to open in a new tab, please adivice.

I sense the issue is in the bottom part of the AccountListController.js
Thanks!

AccountListController.js
https://gist.github.com/liuxiachanghong/a2675b44a3f55bdcf864c38787baf438
 

({
    onAccountsLoaded: function( component, event, helper ) {
        var accounts = event.getParam( 'accounts' );
        if (accounts != null) {
            component.set( 'v.iconName', 'standard:account' );
            component.set( 'v.listName', 'Account List' );
            component.set( 'v.rows', event.getParam( 'accounts' ) );
            var cols = [
                {
                    'label': 'Name',
                    'fieldName': 'Name',
                    'initialWidth': 320,
                    'type': 'text'
                },
                {
                    'label': 'Industry',
                    'fieldName': 'Industry',
                    'initialWidth': 190,
                    'type': 'text'
                },
                {
                    'label': 'Type',
                    'fieldName': 'Client_Type_new__c',
                    'initialWidth': 100,
                    'type': 'text'
                },
                {
                    'label': 'Owner',
                    'fieldName': 'Owner_Alias__c',
                    'initialWidth': 110,
                    'type': 'text'
                },
                {
                    'label': 'Last',
                    'fieldName': 'Last_Sales_Activity_By__c',
                    'initialWidth': 110,
                    'type': 'text'
                },
                {
                    'label': 'Last At',
                    'fieldName': 'Last_Sales_Activity__c',
                    'initialWidth': 110,
                    'type': 'date'
                },
                {
                    'label': 'Last Won At',
                    'fieldName': 'Recent_Closed_Won_Date__c',
                    'initialWidth': 110,
                    'type': 'date'
                }, 
                {
                    'label': 'Deal',
                    'fieldName': 'Related_Opportunities_y__c',
                    'type': 'currency'
                },
                {
                    'label': 'Action',
                    'type': 'button',
                    'typeAttributes': {
                        'label': 'Details',
                        'name': 'view_details'
                    }
                }
            ];
        } else {
            component.set( 'v.iconName', 'standard:lead' );
            component.set( 'v.listName', 'Lead List' );
            component.set( 'v.rows', event.getParam( 'leads' ) );          
            var cols = [
                {
                    'label': 'Name',
                    'fieldName': 'LastName',
                    'type': 'text'
                },
                {
                    'label': 'Company',
                    'fieldName': 'Company',
                    'initialWidth': 320,
                    'type': 'text'
                },
                {
                    'label': 'Prefecture',
                    'fieldName': 'State',
                    'type': 'text'
                },
                {
                    'label': 'City',
                    'fieldName': 'City',
                    'type': 'text'
                },
                {
                    'label': 'Street',
                    'fieldName': 'Street',
                    'initialWidth': 320,
                    'type': 'text'
                },
                {
                    'label': 'Type',
                    'fieldName': 'Client_Type_new__c',
                    'type': 'text'
                },
                {
                    'label': 'Owner',
                    'fieldName': 'Owner__c',
                    'type': 'text'
                },
                {
                    'label': 'Action',
                    'type': 'button',
                    'typeAttributes': {
                        'label': 'Details',
                        'name': 'view_details'
                    }
                }
            ];
        }   
        component.set( 'v.cols', cols );
    },
    
    onRowAction: function( component, event, helper ) {
        var accounts = event.getParam( 'accounts' );
        var action = event.getParam( 'action' );
        var row = event.getParam( 'row' );
        if (accounts != null) {
             if ( action.name == 'view_details' ) {
                var navigation = component.find( 'navigation' );
                navigation.navigate({
                    'type': 'standard__recordPage',
                    'attributes': {
                        'objectApiName': 'Account',
                        'recordId': row.Id,
                        'actionName': 'view'
                    }
                });
        	} 
        } else {
            if ( action.name == 'view_details' ) {
                var navigation = component.find( 'navigation' );
                navigation.navigate({
                    'type': 'standard__recordPage',
                    'attributes': {
                        'objectApiName': 'Lead',
                        'recordId': row.Id,
                        'actionName': 'view'
                    }
                });
            }           
        }
    }
})
 


 

AccountList.cmp

https://gist.github.com/liuxiachanghong/705799f378c180a664cd70d52a13b58f

AccountsLoaded.event
https://gist.github.com/liuxiachanghong/c56f9b120ab6c60510db33b473aaeac7


 

  • January 31, 2021
  • Like
  • 0
I'm working on Apex Test Class. If anyone knows the problem the below test code. Thanks!

The Class is returning Object sumBalance with SUM values (alias: bl, bt, pt) from SOQL and component receives {v.sumBalance.bl}, {v.sumBalance.bt},  {v.sumBalance.pt}. These are working fine. But can't figure out how to test this Class....

Class
public with sharing class DueBalanceController{
    @AuraEnabled
    public static Object sumBalance (String recordId){
        Date d_today = system.Date.today();
        AggregateResult[] groupedResults = [
            SELECT SUM(Balance__c)bl,SUM(Paid_Total__c)pt,SUM(Billing_Total__c)bt, Tenant__c
            FROM Billing__c 
            WHERE Tenant__c	= :recordId AND User_Due_Date__c <= :d_today AND (Status__c='Unpaid' OR Status__c='Paid' OR Status__c='Captured')
            GROUP BY Tenant__c
        ];
        Object sumAmount = groupedResults[0];
        return sumAmount;
    }
}

Test Class
@isTest
public class DueBalanceControllerTest {
        
    @isTest 
    public static void testDueBalanceController(){
        String recordTypeId  = Schema.getGlobalDescribe().get('Account').getDescribe().getRecordTypeInfosByName().get('Person Account').getRecordTypeId();
        Account acct= new Account(
          RecordTypeID = recordTypeId ,
          FirstName = 'Test FName',
          LastName='Test LName',
          PersonMailingStreet='test@yahoo.com',
          PersonMailingPostalCode='12345',
          PersonMailingCity='SFO',
          PersonEmail='test@yahoo.com',
          PersonHomePhone='1234567',
          PersonMobilePhone='12345678' 
        );
        insert acct;
        
        Case cs = new Case(
        	Subject='Test',
            Status ='Closed'
        );
        insert cs;
            
        Billing__c bl = new Billing__c(Name='Test');
        bl.Monthly_Rent__c = 200000;
        bl.Paid_Total__c = 100000;
        bl.Status__c = 'Paid';
        bl.User_Due_Date__c = system.Date.today()-20;
        bl.Tenant__c = acct.Id;
        bl.Case__c = cs.Id;
        insert bl;
    
    Test.StartTest();
		Object sumBalance = DueBalanceController.sumBalance(acct.Id);
        System.assertNotEquals(null,sumBalance);
    Test.StopTest();
        
	}
}


 
  • January 30, 2021
  • Like
  • 0
This whole works fine in sandbox but I couldn't figure out where to test to clear the test coverage. Thanks for your help!

ApexClass
public with sharing class AccountSearchController {
    @AuraEnabled
    public static List<Account> searchAccounts( String searchTerm, String searchOption, Integer searchLimit, Integer searchRadius ) {            
        List<Account> accounts = new List<Account>(); 
        if ( String.isNotBlank( searchTerm ) ) {
        	List<List<SObject>> results = [FIND :searchTerm IN ALL FIELDS
                    RETURNING Account(
                      Id, BillingLatitude, BillingLongitude
                      WHERE BillingLatitude != Null AND BillingLongitude !=null
                      LIMIT 1)
            ];
            Account [] tempAccts = (Account[])results[0];
            Account centerAcct = tempAccts.get(0);
            
            List<List<SObject>> searchResults = [FIND :searchTerm IN ALL FIELDS
                RETURNING Account(
                    Id,Name,BillingAddress,Recent_Closed_Won_Date__c,Last_Sales_Activity__c,Last_Sales_Activity_By__c,Related_Opportunities_y__c,Owner_Alias__c,Client_Type_new__c,Industry,BillingState,BillingCity,BillingStreet,BillingPostalCode,BillingLatitude,BillingLongitude
                	WHERE DISTANCE (BillingAddress, Geolocation(:centerAcct.BillingLatitude, :centerAcct.BillingLongitude),'km')<:searchRadius
                    ORDER BY Related_Opportunities_y__c DESC
                    LIMIT :searchLimit
                )
            ];
            accounts = searchResults[0];
       
        }  else {
                List<List<SObject>> searchResults = [
                FIND '東京都' IN ALL FIELDS
                RETURNING Account(
                    Id,Name,BillingAddress,Recent_Closed_Won_Date__c,Last_Sales_Activity__c,Last_Sales_Activity_By__c,Related_Opportunities_y__c,Owner_Alias__c,Client_Type_new__c,Industry,BillingState,BillingCity,BillingStreet,BillingPostalCode,BillingLatitude,BillingLongitude
                    ORDER BY Related_Opportunities_y__c DESC
                    LIMIT :searchLimit
                )
            ];
            accounts = searchResults[0];
        }        
    	return accounts;
    }
    @AuraEnabled   
    public static List<Lead> searchLeads(  String searchTerm, String searchOption, Integer searchLimit, Integer searchRadius ) {            
        List<Lead> leads = new List<Lead>();   
        if ( String.isNotBlank( searchTerm ) ) {
	       	List<List<SObject>> results = [FIND :searchTerm IN ALL FIELDS
                           RETURNING Lead(
                           Id, Latitude, Longitude
                           WHERE Latitude != Null AND Longitude !=null
                           LIMIT 1)
                           ];
            Lead [] tempLeads = (Lead[])results[0];
            Lead centerLead = tempLeads.get(0);
            
            List<List<SObject>> searchResults = [
                FIND :searchTerm IN ALL FIELDS
                RETURNING Lead(
                    Id,LastName,Address,Company,State,City,Street,PostalCode,Latitude,Longitude,Owner__c, Client_Type_new__c
                    WHERE Latitude !=null AND Longitude !=null AND DISTANCE (Address, Geolocation(:centerLead.Latitude, :centerLead.Longitude),'km')<:searchRadius
                    ORDER BY Company DESC
                    LIMIT :searchLimit
                )
            ];
            leads = searchResults[0];
        }
    	return leads;
    }
}

Test Class
@isTest
public with sharing class AccountSearchControllerTest {

    @isTest
    public static void testAccountSearchController() {
        Account acct = new Account(Name ='Test');
        acct.BillingPostalCode = '105-0011';
        acct.BillingCountry = 'JP';
        acct.BillingState = 'Tokyo';
        acct.BillingCity = 'Minato';
        acct.BillingStreet = 'Shibakoen 3-1-1';
        acct.BillingLatitude = 35.661;
        acct.BillingLongitude = 139.748;
        insert acct;
                
        Lead lead = new Lead (LastName ='Test');
        lead.PostalCode = '105-0011';
        lead.Country = 'JP';
        lead.State = 'Tokyo';
        lead.City = 'Minato';
        lead.Street = 'Shibakoen 3-1-1';
        lead.Latitude = 35.661;
        lead.Longitude = 139.748; 
        insert lead;
        
        Test.startTest();
            List<Account> searchAccounts = AccountSearchController.searchAccounts(acct.BillingState,'option1',25,6);
            System.assertEquals(true,searchAccounts.isEmpty());
      
            List<Lead> searchLeads = AccountSearchController.searchLeads(lead.State,'option2',25,6);
            System.assertEquals(true,searchLeads.isEmpty());
        Test.stopTest();

    }
}

I attached the debug result with highlighting the lines are not test.
Thank you!
  • January 25, 2021
  • Like
  • 0