Using Lightning Web Components to Check Salesforce Feature Enablement

Here, we are creating an LWC (Lightning Web Components) to check whether the “Enable Opportunity Team” , “Enable OpportunitySplit” and “Multi-Currency” features are enabled. We have designed an LWC component along with a button. When the button is clicked, it invokes an Apex method to verify the enablement status of the specified objects. If any of the features are enabled, the checkbox will be checked; otherwise, it will remain unchecked.

To make a Lightning Web Component (LWC) visible to users, we set up custom settings. In these settings, there are four fields: Last_Run_Date__c, Opportunity_Team__c, Opportunity_Split__c, and Multi_Currency_enabled__c. These fields hold the latest values, making sure that the component shows the most up-to-date information

public class ConfigController {
    
    @AuraEnabled
    public static Boolean checkAndUpdateMultiCurrency() {
        Boolean multiCurrencyEnabled = Schema.getGlobalDescribe().containsKey('CurrencyType');
        System.debug('multiCurrencyEnabled=====>>'+multiCurrencyEnabled);
        
        Configs__c conf = Configs__c.getInstance();
        conf.Last_Run_Date__c = System.now();
        conf.Multi_Currency_enabled__c = multiCurrencyEnabled;
        
        Boolean isOpportunityTeamEnabled = isOpportunityTeamEnabled();
        conf.Opportrunity_Team__c = isOpportunityTeamEnabled;
        System.debug('isOpportunityTeamEnabled======'+isOpportunityTeamEnabled);
        
        Boolean isOpportunitySplitEnabled = isOpportunitySplitEnabled();
        conf.Opportunity_Split__c = isOpportunitySplitEnabled;
        System.debug('isOpportunitySplitEnabled====>'+isOpportunitySplitEnabled);
        
        if(conf != null){
            update conf;
        }
        
        return true;
    }
    
    @AuraEnabled(cacheable=true)
    public static Configs__c getConfigs() {
        
        return Configs__c.getInstance();
    }
    
    @AuraEnabled(cacheable=true) 
    public static Boolean isOpportunityTeamEnabled() {
        return Type.forName('Schema','OpportunityTeamMember') != null;
    }
    
    @AuraEnabled(cacheable=true) 
    public static Boolean isOpportunitySplitEnabled() {
        return Type.forName('Schema','OpportunitySplit') != null;
    }
    
    
}
<template>
    <lightning-card title="Click Run Button To Check Below Configurations Enabled Or Not">
        <div class="slds-m-around_medium">
            <lightning-button label="Run" icon-name="utility:bundle_config" onclick={runCheck} variant="brand"></lightning-button>
            
            <div>
               <lightning-input type="checkbox" label="Multi Currency Enabled" checked={isMultiCurrencyEnabled} disabled></lightning-input>
               <lightning-input type="checkbox" label="Opportunity Team" checked={isOpportunityTeamEnabled} disabled></lightning-input>
               <lightning-input type="checkbox" label="Opportunity Split" checked={isOpportunitySplitEnabled} disabled></lightning-input>
                <lightning-input type="datetime" label="Last Run Date" value={lastRunDate} readonly ></lightning-input>
            
            </div>
            
               
        </div>
    </lightning-card>
</template>
import { LightningElement, track, wire } from 'lwc';
import checkAndUpdateMultiCurrency from '@salesforce/apex/ConfigController.checkAndUpdateMultiCurrency';
import getConfigs from '@salesforce/apex/ConfigController.getConfigs';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { refreshApex } from '@salesforce/apex';
export default class ShowConfig extends LightningElement {
     

   @track resultMessage;
    @track isMultiCurrencyEnabled;
    @track isOpportunityTeamEnabled;
    @track isOpportunitySplitEnabled;
    @track lastRunDate;
    @track wiredResult;

    runCheck() {
        checkAndUpdateMultiCurrency()
            .then(result => {
                this.isMultiCurrencyEnabled = result;
                this.showSuccessToast();
               //refresh
                return refreshApex(this.wiredResult);
            })
            .catch(error => {
                console.error('Error running multi-currency check', error);
            });
    }

    @wire(getConfigs)
    wiredConfigs(result) {
        this.wiredResult = result;
        if (result.data) {
            const data = result.data;
            this.isMultiCurrencyEnabled = data.Multi_Currency_enabled__c;
            this.isOpportunityTeamEnabled = data.Opportrunity_Team__c;
            this.isOpportunitySplitEnabled = data.Opportunity_Split__c;
            this.lastRunDate = data.Last_Run_Date__c;
        } else if (result.error) {
            console.error('Error retrieving Configs__c', result.error);
        }
    }

    showSuccessToast() {
        const event = new ShowToastEvent({
            title: 'Success',
            message: 'Configurations updated successfully !!.',
            variant: 'success',
        });
        this.dispatchEvent(event);
    }

}
<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
	<apiVersion>57.0</apiVersion>
	<isExposed>true</isExposed>
	<targets>
		<target>lightning__RecordPage</target>
		<target>lightning__AppPage</target>
		<target>lightning__HomePage</target>
		<target>lightning__Tab</target>
	</targets>
</LightningComponentBundle>

Thank you for taking the time to read our blog! We appreciate your interest and hope you found valuable insights within these articles. If you have any questions, comments, or suggestions, we’d love to hear from you.

Stripe payment gateway integration with salesforce

Setting up Stripe in your app is relatively straightforward. Begin by creating your account on Stripe and obtaining the secret access keys. Once you have these keys, you are ready to utilize their API for processing payments. For testing purposes, Stripe offers test credentials to validate your payment flow.

Stripe provides well-organized API documentation that makes it easy to get started. In this article, we will use Salesforce Apex language to sync accounts from Salesforce to Stripe as customers. This synchronization will include opportunity price and amount data from Salesforce, ensuring that it corresponds with the customer invoice in Stripe.

1. Create Stripe Account

In order to use Stripe, you need to register yourself on Stripe.

This image has an empty alt attribute; its file name is Create-Account-registration-stripe.png

2.Get Secret key and publishable key

Go to the developers’ section in Stripe and click here to find the API key section. Copy both the publishable key and the secret key. Once you have located these keys, copy and save them in Salesforce custom settings.

In the custom setting, create two fields: “Secret Test Key” and “Publishable Test Key.” Add the required data and save it. Furthermore, when integrating with Apex, we can utilize these keys through a method.

3.Establish a connection between Stripe and Salesforce via Apex.

You need to create a class and a method for this connection. In order to establish the connection, create two separate classes for this purpose: one is “StripeCallout,” and the second is “StripeApi” for handling the API key. You can also refer to the API documentation provided by Stripe for additional guidance

public class StripeCallout {
    
    private static final String CUSTOMER_URL = 'https://api.stripe.com/v1/customers';
    private static final String API_KEY = StripeAPI.ApiKey;
    public static HttpResponse makeCallout(Account acc){
       
      System.debug('acc from makecallout controller  ==============----------- '+acc);
        System.debug('api key----------- '+API_KEY);
        Integer statusCode;
        string response;
        HttpRequest http = new HttpRequest();
        http.setEndPoint(CUSTOMER_URL);
        http.setMethod('POST');
        Blob headerValue = Blob.valueOf(API_KEY + ':');
        String authorizationHeader = 'BASIC '+EncodingUtil.base64Encode(headerValue);
        http.setHeader('Authorization',authorizationHeader);
         String requestBody = 'email=' + EncodingUtil.urlEncode(acc.Email__c, 'UTF-8') +
             		      '&phone=' + EncodingUtil.urlEncode(acc.phone , 'UTF-8')+
                             '&name=' + EncodingUtil.urlEncode(acc.Name, 'UTF-8');
	http.setBody(requestBody);
        http con = new Http();
        HttpResponse hs = con.send(http);
        System.debug('hs getbody --->> '+hs.getBody());
        response = hs.getBody();
        statusCode= hs.getStatusCode();
        System.debug(' statusCode --->> '+hs.getStatusCode());
        return hs;
    }
}
global class StripeAPI {
    global static String ApiKey{
        get{
            StripeSettings__c s = StripeSettings__c.getInstance();
            if(s!=null){
                System.debug('s.Stripe_Secret_Test_Key__c is ==>'+s.Stripe_Secret_Test_Key__c);
                return s.Stripe_Secret_Test_Key__c; 
            }
            return null;
        }
    }
   
}

4. Test your connections

Now, you can test your connection to check whether you receive a “200 OK” status. If you receive an “OK,” it indicates that your connection is ready to sync the data. Additionally, we are using the account record to sync as a customer from Salesforce to Stripe.

I have created a separate Visualforce page for this purpose and a controller to pass the data at runtime via an action button. The customer ID is also stored as a response. In order to sync the invoice, you have to pass the customer ID and then synchronize the invoice for that particular customer.

public class StripeCustomerController {
   public StripeCustomerController(){
}
     public static Id accId{get;set;}
     public Account acc {get;set;}
    
    public StripeCustomerController(ApexPages.StandardController controller){
         accId = controller.getRecord().Id;
        System.debug('acc is '+acc);
    }
    @future(callout = true)
    public static void createCustomer(Id accId){
       Account  acc = StripeCustomerHelper.getAccount(accId); 
        HttpResponse responses = StripeCallout.makeCallout(acc);
        system.debug('responses'+responses);
       CustomerResponse cr =  CustomerResponse.parse(responses.getbody());
        System.debug('crid---------s'+cr);
        Update new Account (id=accId,CustomerID__c=cr.id);
    }
   
     public Pagereference syncStripe(){
        StripeCustomerController.createCustomer(accId);
        return new PageReference('/'+accId);
         
    }
}
public class StripeCustomerHelper {
  
    public static Account getAccount(Id accId){
        return [SELECT Id,
                      Name, 
                      ShippingCity,
                      ShippingStreet,
                      ShippingState,
                      ShippingCountry,
                      ShippingPostalCode,
                      phone,
                     Email__c,
                      BillingCity,
                      BillingStreet,
                      BillingCountry,
                      BillingPostalCode,
                      BillingState,
                      BillingLatitude,
                      BillingLongitude
              FROM Account 
              WHERE Id =: accId];
    }

}
<apex:page standardController="Account" extensions="StripeCustomerController" action="{!syncStripe}">
</apex:page>

5. Account setup in Salesforce

After completing these steps, create a button on the Account record page and call the Visualforce page, then save it. Add the button to the page layout and save the changes.

After clicking the button, your account data, including name, email, and phone, will sync with the Stripe customer. You can add more fields as needed based on your business requirements. Now, check your Stripe dashboard and click on “Customers”; you will see the synced customer.

6. Opportunity sync as invoice

Afterward, create an opportunity related to the account, add the amount, price, description, currency, and save it.

Now, I have created a separate class and method to send the opportunity as an invoice from Salesforce. Create a wrapper class for this to receive the response

public class Stripe{

    String API_KEY='';
    public Stripe(string apiKey){
        API_KEY = apiKey;
    }
    public StripeResponseModel createInvoice(String customer,String amount,String curency,String description){
        StripeInvoice strInv=new StripeInvoice(API_KEY);
        return strInv.createInvoice(customer,amount,curency,description);
    }

}
public class StripeOpportunityHelper {
    
    public static Opportunity  getOpportunity(Id oppId){
        return [SELECT id,AccountId,Amount,Account.CustomerID__c,Account.Name,Description  FROM Opportunity WHERE Id =: oppId];
    }

    
    
   
}
public class StripeOpportunityController {

   public StripeOpportunityController(){
         
    }
     public static Id oppId{get;set;}
    public Opportunity  opp {get;set;}
    
    public StripeOpportunityController(ApexPages.StandardController controller){
        oppId = controller.getRecord().Id;
        opp = StripeOpportunityHelper.getOpportunity(oppId);
        System.debug('opp is '+opp);
        Stripe request = new Stripe(StripeAPI.ApiKey);
   StripeResponseModel response = request.createInvoices(opp.Account.CustomerID__c,String.valueOf(Integer.valueOf(opp.Amount)), 'inr', opp.Description);
        
       
        
    }
    
     public Pagereference syncStripe(){
        //acc = StripeCustomerHelper.createOrUpdateCustomer(acc);
        //update acc;
        return new PageReference('/'+oppId);
         
    }
}
public class StripeResponseModel{
    public string id {get;set;}
    public string rObject{get;set;}
    public string amount {get;set;}
    public string paid {get;set;}
    public String curency{get;set;}
    public string fee {get;set;}
    public string created {get;set;}
    public string livemode {get;set;}
    public string description {get;set;}
    public string refunded {get;set;}
    public boolean isError {get;set;}
    public String name{get;set;}
    public String interval{get;set;}
    
    public StripeCardResponseModel card {get;set;}
    public StripeErrorModel errorResponse {get;set;}
    
    public StripeResponseModel(){
        card=new StripeCardResponseModel();
        errorResponse=new StripeErrorModel();
    }
}
 
public class StripeInvoice{

    StripeResponseModel StripeResponseModel{get;set;}
    public StripeGetResponseValues stripeGetResponseValue{get;set;}    
    
    Integer statusCode;
    string response;
    
    String INVOICE_URL='https://api.stripe.com/v1/invoiceitems';
    String INVOICE_URLS='https://api.stripe.com/v1/invoices';
    
    String API_KEY='';
    public StripeInvoice(String apikey){
        API_KEY=apikey;
        StripeResponseModel=new StripeResponseModel();
        stripeGetResponseValue=new StripeGetResponseValues();
    }
public StripeResponseModel createInvoice(String customer,String amount,String curency,String description){    
        HTTpRequest http=new HTTpRequest();
        http.setEndPoint(INVOICE_URL);
         http.setMethod('POST');
        Blob headerValue = Blob.valueOf(API_KEY + ':');
        String authorizationHeader = 'BASIC ' +
        EncodingUtil.base64Encode(headerValue);
        http.setHeader('Authorization', authorizationHeader);
        http.setBody('customer='+customer+'&amount='+amount+'&currency='+curency+'&description='+description);
            Http con = new Http();
            HttpResponse hs = con.send(http);
            response = hs.getBody();
            statusCode=hs.getStatusCode();
          
        if(statusCode!=200){
            StripeResponseModel.errorResponse.code=statusCode;
            StripeResponseModel.errorResponse.message=stripeGetResponseValue.getValue(response,'"message":');            
            StripeResponseModel.errorResponse.param =stripeGetResponseValue.getValue(response,'"param":');
            StripeResponseModel.isError=true;            
        }else{
               
            StripeResponseModel.isError=false;            
            StripeResponseModel.id=stripeGetResponseValue.getValue(response,'"id":');                
        }
        return StripeResponseModel;
        
    }
}
public class StripeGetResponseValues{

    public string getValue(string accRecordJson, string fieldName){
        integer startIdx = accRecordJson.indexOf(fieldName);
        integer endIdx = -1;
        if(startIdx > 0){
            endIdx = accRecordJson.indexOf('",', startIdx);
        }
        if(startIdx > 0 && endIdx > startIdx){
            
            return accRecordJson.substring(startIdx + fieldName.length() + 2,endIdx);
        }
        return '';
   }
}

Map all these classes and methods, and also add the button for the Opportunity custom button to sync the opportunity. You can also incorporate additional logic or conditions based on your business requirements.

After completing these steps, click the “Sync Invoice” button. You will find the invoice items inside the Stripe customer. You can convert the invoice and send a payment link to that particular customer via their email.

“Thank you for taking the time to read our blog! We appreciate your interest and hope you found valuable insights within these articles. If you have any questions, comments, or suggestions, we’d love to hear from you. Your feedback is invaluable as we strive to provide content that matters to you. Stay tuned for more enriching articles, and once again, thank you for being part of our reading community!”

Xero Integration with Salesforce

XERO is a cloud-based accounting software platform that allows businesses to manage their finances in real-time.

“Here, we have integrated Xero with Salesforce.

First, you need to create a Xero account. Inside the account, create an app and generate the client ID and client secret for authorization and authentication. You can refer to this website for your API reference and documents as well.”

1. Create Xero Account

In order to use Xero, you need to register yourself on Xero.

2.Get Client id and Client secret

Go to this link, create an app, and obtain the client ID and client secret for the Configurations tab.

3.Create Auth providers in salesforce .

Create Auth providers in Salesforce and fill in all the information in the Auth Provider. After that, provide the callback URL from Salesforce to register the callback URL. Prompt the authentication, allow, and accept the terms and conditions. for more details click the link.1 , link2

4.Account Sync Process.

we are using the account record to sync as a contact from Salesforce to Xero.

I have created a separate Visualforce page for this purpose and a controller to pass the data at runtime via an action button. The customer ID is also stored as a response. In order to sync the invoice , you have to pass the customer ID and then synchronize the invoice for that particular Contact.

public class XeroCustomerController {
	
   public  XeroCustomerController(){
        
    }
    
    public static Id accId{get;set;}
    public Account acc {get;set;}
    
    public XeroCustomerController(ApexPages.StandardController controller){
         accId = controller.getRecord().Id;
         Account  acc = XeroCustomerHelper.getAccount(accId);
        System.debug('acc is '+acc);
      
        
    }
    
    
    @future(callout = true)
    public static void createCustomer(Id accId){
       Account  acc = XeroCustomerHelper.getAccount(accId); 
         XeroContact xeroContact = new XeroContact(acc);
        try {
            XeroContact responseContact = XeroAPI.sendContact(xeroContact);
            System.debug('Contact sent successfully to Xero. Xero ID: ' + responseContact.ContactID);
            update new Account (id=accId,Xero_Customer_ID__c=responseContact.ContactID);
        } catch (XeroApiException e) {
            System.debug('Error sending contact to Xero: ' + e.getMessage());
        }
        
    }
       
    public Pagereference syncXero(){
        XeroCustomerController.createCustomer(accId);
        return new PageReference('/'+accId);
        
         
    }
     
}
public class XeroCustomerHelper {
	
     public static Account getAccount(Id accId){
        return [SELECT Id,
                      Name, 
                      ShippingCity,
                      ShippingStreet,
                      ShippingState,
                      ShippingCountry,
                      ShippingPostalCode,
                      phone,
                	  fax,
                     Email__c,
                      BillingCity,
                      BillingStreet,
                      BillingCountry,
                      BillingPostalCode,
                      BillingState,
                      BillingLatitude,
                      BillingLongitude
              FROM Account 
              WHERE Id =: accId];
    }
}
public class XeroAPICall {
	
	public static XeroContact sendContact (XeroContact xeroContact) {
        String resourceName = 'Contacts';
        HttpResponse response = XeroServiceClass.getCallout('POST', resourceName, xeroContact.serialize());
        Boolean isSuccess = response.getStatusCode() < 300;
		String jsonSerialized = XeroUtils.generateJsonStringForParsing(response.getBody(), isSuccess ? resourceName : 'Elements');
		List<XeroContact> xeroContacts = (List<XeroContact>) JSON.deserialize(jsonSerialized, List<XeroContact>.class);
		if (isSuccess) {
			return xeroContacts[0];
		}
    }
	
	
	public static XeroInvoice sendInvoice (XeroInvoice xeroInvoice) {
        String resourceName = 'Invoices';
        HttpResponse response = XeroServiceClass.getCallout('POST', resourceName, xeroInvoice.serialize());
        Boolean isSuccess = response.getStatusCode() < 300;
            String jsonSerialized = XeroUtils.generateJsonStringForParsing(response.getBody(), isSuccess ? resourceName : 'Elements');
            List<XeroInvoice> xeroInvoices = (List<XeroInvoice>) JSON.deserialize(jsonSerialized, List<XeroInvoice>.class);
            if (isSuccess) {

                return xeroInvoices[0];
            }
            
        
    }

}
public class XeroServiceClass {
    
    public static HttpResponse getCallout (String method, String resourceName) {
        return executeCallout(method, resourceName, null, null);
    }
    
    public static HttpResponse getCallout (String method, String resourceName, String requestBody) {
        return executeCallout(method, resourceName, requestBody, null);
    }
    
    public static HttpResponse getCallout (String method, String resourceName, String requestBody, Map<String, String> headers) {
        
        String errorMessage = '';
        
        HttpRequest request = new HttpRequest();
        request.setMethod(method);
        request.setEndpoint( 'callout:Xero/' + (resourceName.equalsIgnoreCase('connections') ? 'connections': 'api.xro/2.0/' + resourceName) );
        
        request.setHeader('Accept', 'application/json');
        
        if(resourceName.equalsIgnoreCase('connections')){
            request.setHeader('xero-tenant-id', '');
        }else{
            request.setHeader('xero-tenant-id', XeroTenant .getXeroTenantId());
        }
            request.setTimeout(120000);
            request.setBody(requestBody);
            request.setHeader('Content-Type', 'application/json');
            for (String headerKey :headers.keySet()) {
                request.setHeader(headerKey, headers.get(headerKey));
            }
        
        HttpResponse response = new HttpResponse();
        response = new Http().send(request);
        System.debug('respons'+response);
        return response;
    }  
}
global class XeroTenant {
	  global static String getXeroTenantId () {
        HttpResponse response = XeroServiceClass.getCallout('GET', 'connections'); 
        if (response.getStatusCode() < 300) {
            List<XeroConnection> xeroConnections = (List<XeroConnection>) JSON.deserialize(response.getBody(), List<XeroConnection>.class);
            return xeroConnections[0].tenantId;
        }
        else {
            System.debug('error occurred');
        }
    }
}
public with sharing class XeroConnection {
    
    public String id;
    public String tenantId;	
    public String tenantType;
    public String createdDateUtc;
    public String updatedDateUtc;
}

5. Account setup in Salesforce

After completing these steps, create a button on the Account record page and call the Visualforce page, then save it. Add the button to the page layout and save the changes.

After clicking the button, your account data, including name, Phone, billing address,shipping address will sync with the Xero contact. You can add more fields as needed based on your business requirements. Now, check your xero dashboard and click on “contacts”; you will see the synced contact.

6. Opportunity (Product) sync as invoice

Afterward, create an opportunity related to the account, add the amount, price, description, currency, product list of product and save it.

Now, I have created a separate class and method to send the opportunity product as an invoice from Salesforce. Create a wrapper class for this to send JSON serialized data.

public class XeroInvoice {
    public XeroInvoice(){
        
    }
    public String Type;	//ACCREC
    public String InvoiceID;	//94b8a8d6-31fd-4d8c-8791-d1a2a7dfe898
    public String InvoiceNumber;	//asdsdasd 
    public String Reference;	//ABC123
    public XeroContact Contact;
    public String Date_x;	
    public String DueDate;	

    public Boolean SentToContact;
    public Decimal AmountDue;	//115
    public Decimal AmountPaid;	//0
    public Decimal AmountCredited;	//0
    public Decimal CurrencyRate;	//1
    public Boolean IsDiscounted;
    public Boolean HasAttachments;
    public Boolean HasErrors;
    
    public String Status;	//AUTHORISED
    public String LineAmountTypes;
    public Decimal SubTotal;	//100
    public Decimal TotalTax;	//15
    public Decimal Total;	//115
    public String UpdatedDateUTC;	
    public String CurrencyCode;	
    public String Url;
    
    public List<LineItem> LineItems;
    public List<Payment> Payments;
    public List<CreditNote> CreditNotes;
    public List<PrePayment> Prepayments;
    public List<OverPayment> Overpayments;

    public List<XeroValidationError> ValidationErrors;

    public class Payment {}

    public class CreditNote {}

    public class PrePayment {}

    public class OverPayment {}

    public class LineItem {

        public String LineItemID;	//006fc261-ee0b-4f4c-bb55-c96ca97cdb53
        public String Description;	//
        public String AccountCode;	//200
        public String TaxType;	//OUTPUT2
        public Decimal UnitAmount;	//100
        public Decimal TaxAmount;	//15
        public Decimal LineAmount;	//100
        public Decimal Quantity;	//1

    }


    public XeroInvoice(String xeroContactId) {

       
        this.Type = 'ACCREC';

      
        this.Contact = new XeroContact(xeroContactId);
    }

    public String serialize() {
        
       
        String serialized = JSON.serialize(this, true);

      
        serialized = serialized.replace('"Date_x"', '"Date"');

        return serialized;
    }
}

public class XeroOpportunityController {
    
    
    public XeroOpportunityController(){
        
    }
    public static Id oppId{get;set;}
    public Opportunity  opp {get;set;}
    public  List<OpportunityLineItem> opplineItem {get;set;}
    public XeroOpportunityController(ApexPages.StandardController controller){
        oppId = controller.getRecord().Id;
        List <OpportunityLineItem> lstOpportunityLineItem = XeroOpportunityHelper.getOpportunityProductLineItem(oppId);
        System.debug('lstOpportunityLineItem is =======================================>'+lstOpportunityLineItem); 
        
        Set<String> customerIdSet = new Set<String>();
        List<XeroInvoice.LineItem> lstInvoiceItem = new List<XeroInvoice.LineItem>();
        for(OpportunityLineItem opplist :lstOpportunityLineItem){
            customerIdSet.add(opplist.Opportunity.Account.Xero_Customer_ID__c);
            
        }
        System.debug('customerIdSet is =======================================>'+customerIdSet);
        
        for(OpportunityLineItem opplist :lstOpportunityLineItem){
            
             XeroInvoice.LineItem lineItem = new XeroInvoice.LineItem();
            lineItem.Description = opplist.Product2.Name;
            lineItem.Quantity = opplist.Quantity;
            lineItem.UnitAmount = opplist.UnitPrice;
           // lineItem.TaxAmount = 10.00;
            lineItem.LineAmount = opplist.TotalPrice;
            
            lstInvoiceItem.add(lineItem);
        }
        //-------------------------------
        System.debug('lstOpportunityLineItem[0].Opportunity.Account.Xero_Customer_ID__c-------======'+lstOpportunityLineItem[0].Opportunity.Account.Xero_Customer_ID__c);
        XeroInvoice newInvoice = new XeroInvoice(lstOpportunityLineItem[0].Opportunity.Account.Xero_Customer_ID__c);
         newInvoice.Date_x = String.valueof(Date.today());
        newInvoice.DueDate = String.valueof(Date.today()+15);
        newInvoice.Reference = 'Website Design';
         newInvoice.LineItems = lstInvoiceItem;
        
        //newInvoice.SubTotal = 100.00;
        //newInvoice.TotalTax = 15.00;
        //newInvoice.Total = 115.00;
        try {
            XeroInvoice resultInvoice = XeroAPICall.sendInvoice(newInvoice);
            System.debug('Invoice successfully sent to Xero. InvoiceID: ' + resultInvoice.InvoiceID);
        } catch (Exception e) {
            System.debug('Error sending invoice to Xero: ' + e.getMessage());
        }
	 //-------------------------------
    }
    
    public Pagereference syncXero(){
        
        
        //return to the same page 
        return new PageReference('/'+oppId);
        
    }
    
}
public class XeroOpportunityHelper {
    
    
    public static List<OpportunityLineItem>  getOpportunityProductLineItem(Id oppId){
        return [select id,
                TotalPrice,
                UnitPrice,
                Name,
                OpportunityId,
                Opportunity.Account.Name,
                Opportunity.Amount,
                Opportunity.Account.Xero_Customer_ID__c ,
                Quantity, 
                ListPrice,
                Product2.Name,
                product2.Id from
                OpportunityLineItem 
                where OpportunityId=: oppId];
    }
    
}
<apex:page standardController="Opportunity" extensions="XeroOpportunityController" action="{!syncXero}">
</apex:page>
<apex:page standardController="Account" extensions="XeroCustomerController" action="{!syncXero}">
</apex:page>

Map all these classes and methods, and also add the button for the Opportunity custom button to sync the opportunity. You can also incorporate additional logic or conditions based on your business requirements.

After completing these steps, click the “Sync Invoice” button. You will find the invoice items inside the Xero contact.

“Thank you for taking the time to read our blog! We appreciate your interest and hope you found valuable insights within these articles. If you have any questions, comments, or suggestions, we’d love to hear from you. Your feedback is invaluable as we strive to provide content that matters to you. Stay tuned for more enriching articles, and once again, thank you for being part of our reading community!”

Sync invoice with Quickbooks invoice (Part 2)

OpportunityController

public class OpportunityController {

    public Id oppId{get;set;}
    public Opportunity opp {get;set;}
    public List<OpportunityLineItem> olis {get;set;}

    public OpportunityController(ApexPages.StandardController controller) {
        oppId = controller.getRecord().Id;
        opp = OpportunityHelper.getOpportunity(oppId);
        olis = opp.OpportunityLineItems;
    }

    public Pagereference createInvoice(){
        
        List<SObject> upsertObjects = OpportunityHelper.mapInvoice(opp, olis);
        Invoice__c inv;
        List<Invoice_Line_Item__c> ilis = new List<Invoice_Line_Item__c>();
        for(SObject so : upsertObjects){
            if(so instanceof Invoice__c){
                inv = (Invoice__c) so;
            }else if(so instanceof Invoice_Line_Item__c){
                ilis.add((Invoice_Line_Item__c) so);
            }
        }
        insert inv;
        for(Invoice_Line_Item__c ili : ilis){
            ili.Invoice__c = inv.Id;
        }
        insert ilis;
        return new PageReference('/'+ inv.Id);
    }
    
}
public class InvoiceHelper {
  public static void mapInvoices(String query){
        List<Invoice__c> invList = new List<Invoice__c>();
        List<Invoice_Line_Item__c> iliList = new List<Invoice_Line_Item__c>();
        InvoiceQueryJSON queryJson = AuthCallout.queryInvoices(query);
        List<SObject> returnList = CustomerMap.mapInvoices(queryJson);
        for(SObject so : returnList){
            System.debug(so);
            if(so instanceOf Invoice__c){
                invList.add((Invoice__c) so);
            }else if(so instanceof Invoice_Line_Item__c){
                iliList.add((Invoice_Line_Item__c) so);
            }
        }

        Database.UpsertResult [] invoiceResults = Database.upsert(invList,Invoice__c.QBO_Id__c,false);
        for(Database.UpsertResult result : invoiceResults){
            if(!result.success){
                System.debug(result.errors);
            }
        }
        Database.UpsertResult [] iliResults = Database.upsert(iliList,Invoice_Line_Item__c.QB_Id__c,false);
        for(Database.UpsertResult result : iliResults){
            if(!result.success){
                System.debug(result.errors);
            }
        }
       


    public static void mapInvoice(Invoice__c inv){
        List<Invoice__c> invList = new List<Invoice__c>();
        List<Invoice_Line_Item__c> iliList = new List<Invoice_Line_Item__c>();

        String query = 'select * from Invoice where id = \''+ inv.QBO_Id__c + '\'';
        InvoiceQueryJSON queryJson = AuthCallout.getInvoice(inv);
        
        if(queryJson != null){
            if(queryJson.QueryResponse.Invoice.size() == 1){
                inv.QBO_Id__c = queryJson.QueryResponse.Invoice[0].Id;
                inv.QB_SyncToken__c = queryJson.QueryResponse.Invoice[0].SyncToken;
            }else{
                
                throw new ListException(); 
            }
        }
        List<SObject> returnList = customerMap.mapInvoices(queryJson);
        for(SObject so : returnList){
            System.debug(so);
            if(so instanceOf Invoice__c){
                invList.add((Invoice__c) so);
            }else if(so instanceof Invoice_Line_Item__c){
                iliList.add((Invoice_Line_Item__c) so);
            }
        }

        Database.UpsertResult [] invoiceResults = Database.upsert(invList,Invoice__c.QBO_Id__c,false);
        for(Database.UpsertResult result : invoiceResults){
            if(!result.success){
                System.debug(result.errors);
            }
        }
        Database.UpsertResult [] iliResults = Database.upsert(iliList,Invoice_Line_Item__c.QB_Id__c,false);
        for(Database.UpsertResult result : iliResults){
            if(!result.success){
                System.debug(result.errors);
            }
        }
    public static void createOrUpdateInvoices(List<Invoice__c> invs){
        List<SObject> returnList = new List<SObject>();
        List<QBO_Config__mdt> config = QIME.getConfig();
        for(Invoice__c inv : invs){
            String customerId = inv.Opportunity__r.Account.QBO_Id__c;
           
            if(checkCustomerCreate(config) && String.isBlank(inv.Opportunity__r.Account.QBO_Id__c) ){
                Account acc = QBCustomerHelper.getAccount(inv.Opportunity__r.AccountId);
                acc =QBCustomerHelper.createOrUpdateCustomer(acc);
                returnList.add(acc);
            } else if(String.isBlank(inv.Opportunity__r.Account.QBO_Id__c)){
                inv.addError('Please sync customer before syncing invoice');
                continue;
            }
  
            String invoiceJson = QBMap.mapInvoice(inv, inv.Invoice_Line_Item__r, customerId);
            try{
                InvoiceResponseJSON resJSON = QBCallout.createInvoice(invoiceJson);
                returnList.addAll(CustomerMap.mapInvoiceData(inv, inv.Invoice_Line_Item__r, resJSON));
            }catch(Exception e){
                System.debug(e);
            }
        }
        createOrUpdateInvoiceDML(returnList);
    }

    private static void createOrUpdateInvoiceDML(List<SObject> returnList){
        List<Invoice__c> invList = new List<Invoice__c>();
        List<Invoice_Line_Item__c> iliList = new List<Invoice_Line_Item__c>();
        List<Account> accList = new List<Account>();
        for(SObject so : returnList){
            System.debug(so);
            if(so instanceOf Invoice__c){
                invList.add((Invoice__c) so);
            }else if(so instanceof Invoice_Line_Item__c){
                iliList.add((Invoice_Line_Item__c) so);
            } else if(so instanceof Account){
                accList.add((Account) so);
            }
        }

        Database.UpsertResult [] accResults = Database.upsert(accList,false);
        debugResults(accResults);
        Database.UpsertResult [] invoiceResults = Database.upsert(invList,false);
        debugResults(invoiceResults);
        Database.UpsertResult [] iliResults = Database.upsert(iliList,false);
        debugResults(iliResults);
    }
    public static Invoice__c getInvoice(Id invId){
        return [SELECT Id,
                     Due_Date__c,
                      Subtotal__c,
                      Balance__c,
                       Name, 
                       QBO_Id__c,
                       Opportunity__r.Account.QBO_Id__c,
                       Opportunity__r.AccountId,
                       QB_SyncToken__c,
                      

                       (SELECT Id, Unit_Price__c, Quantity__c, 
                                Amount__c, ItemRef_Id__c, QB_Id__c,
                                Product__r.QB_Id__c, Product__r.QB_Description__c,Product__r.Name,Product__r.Description,
 
Product__c FROM Invoice_Line_Item__r)
FROM Invoice__c 
WHERE Id =: invId];
    }
}

And then map all the invoices in which you just sent the products as an invoice, or you can use LWC to sync the product.”

<apex:page standardcontroller="Invoice__c" extensions="SendInvoiceController" action="{!syncQB}">
</apex:page>

public class SendInvoiceController{
    public Id invId{get;set;}
    public Invoice__c inv {get;set;}

    public SendInvoiceController(ApexPages.StandardController controller) {
        invId = controller.getRecord().Id;
        inv = [SELECT Id,
                     
                       Name, 
                       QBO_Id__c, 
                       QBO_SyncToken__c,
                       
                       Description__c,
                       Invoice__c.Opportunity__r.Name,
                       Invoice__c.Opportunity__r.Account.contact__r.email,
                       Invoice__c.Opportunity__r.Account.QBO_Id__c,
                       Invoice__c.opportunity__r.account.BillingCity,  

                       Invoice__c.opportunity__r.account.name,
                    FROM Invoice__c 
                    WHERE Id =: invId];
    }

    public Pagereference syncQB(){
        AuthCallout callout = new AuthCallout ();
        QBInvoiceQueryJSON queryJson = callout.getInvoice(inv);
        if(queryJson != null){
            if(queryJson.QueryResponse.Invoice.size() == 1){
                inv.QBO_Id__c = queryJson.QueryResponse.Invoice[0].Id;
                inv.QBO_SyncToken__c = queryJson.QueryResponse.Invoice[0].SyncToken;
            }else{

               throw new Exception(); 
            }
        }
        InvoiceResponseJSON res = AuthCallout.createInvoiceCall(inv);
        callout.updateRefreshToken();
        inv.QBO_Id__c = res.Invoice.Id;
        inv.QBO_SyncToken__c = res.Invoice.SyncToken;
        inv.tax__c = res.Invoice.TxnTaxDetail.TotalTax;
        inv.total__c = res.Invoice.TotalAmt;
        update inv;
        return new PageReference('/'+ invId);
    }
}

Thank you for taking the time to read our blog! We appreciate your interest and hope you found valuable insights within these articles. If you have any questions, comments, or suggestions, we’d love to hear from you.

Custom KanBan Using Lwc with Apex Cotroller

KanBan Gif

Welcome to the technical documentation for the Drag-and-Drop Lightning Web Component (LWC). This guide will walk you through the implementation and usage of this component, which provides a streamlined interface for managing opportunities using a Kanban-style view, with our own custom customization

Installation and Setup

  • Salesforce Environment Setup: Before you begin, make sure you have a Salesforce environment set up for Lightning Component development.
  • Deploy the LWC: To use the Drag-and-Drop LWC in your org, follow these steps:
    • Retrieve the source code of the component.
    • Deploy the component to your Salesforce org using Salesforce CLI or another appropriate deployment method.

Component Architecture

The Drag-and-Drop LWC is structured as follows:

  • dragAndDropLwc Folder:
    • dragAndDropLwc.html: This file contains the HTML template for the component’s user interface,and child componet of drag-and-drop-list
    • dragAndDropLwc.js: The JavaScript file holds the component’s logic, including data fetching, drag-and-drop handling, and stage updates.
    • dragAndDropLwc.css: CSS styling to enhance the component’s visual appearance.
  • dragAndDropList Folder:
    • dragAndDropList.html: This file contains the HTML template for the component’s user interface.
    • dragAndDropList.js: The JavaScript file holds the component’s logic.
  • dragAndDropCard Folder:
    • dragAndDropCard.html: This file contains the HTML template for the component’s user interface.
    • dragAndDropCard.js: The JavaScript file holds the component’s logic,

The primary purpose is to create an interactive interface where stages are displayed as tab-like elements, each showing the stage’s name and the count of records associated with it. Users can engage with these stages using drag-and-drop functionality. The component’s structure includes an iteration loop to dynamically generate these elements for each stage. Additionally, the component utilizes a nested child component, c-drag-and-drop-list, to display and manage the records associated with each stage. The main goal is to provide an intuitive way for users to view, interact with, and rearrange records across different stages through a visually engaging and user-friendly interface.

 <template>
    <div class="card_Design">
        <template for:each={pickVals} for:item="item">
            <div class="slds-tabs--path"  role="application" key={item}  style = {equalwidthHorizontalAndVertical}>
                <ul class="slds-tabs--path__nav" role="tablist">
                <li class="slds-tabs--path__item slds-is-incomplete  slds-path__item slds-is-current slds-is-active "  role="presentation">
                    <a class="slds-tabs--path__link slds-button_brand " tabindex="-1" role="tab" >
                        <span class="slds-tabs--path__title" >
                                <div class="stage-info">
                                    <span class="stage-name">{item.stage}</span>
                                    <span class="record-count">({item.noRecords})</span>
                                </div>
                        </span>
                    </a>
                </li>
            </ul>
            <c-drag-and-drop-list records={records} stage={item.stage}
            onlistitemdrag={handleListItemDrag}
            onitemdrop={handleItemDrop}
            ></c-drag-and-drop-list>
            </div>
            
        </template>
    </div>   
    
</template> 

dragAndDropLwc.js

import { LightningElement, wire } from 'lwc';
import {getObjectInfo } from 'lightning/uiObjectInfoApi';
import OPPORTUNITY_OBJECT from '@salesforce/schema/Opportunity'
import STAGE_FIELD from '@salesforce/schema/Opportunity.StageName'
import ID_FIELD from '@salesforce/schema/Opportunity.Id';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import getTopOpportunities from '@salesforce/apex/OpportunityKanbanController.getTopOpportunities';
import updateOpprtuity from '@salesforce/apex/OpportunityKanbanController.updateOpprtuity';
export default class DragAndDropLwc extends LightningElement {
    records;
    stageList;
    pickVals;
    stageMap;
    recordId;

    @wire(getObjectInfo, {objectApiName:OPPORTUNITY_OBJECT})
    objectInfo

       connectedCallback() {
           getTopOpportunities()
            .then(result => {
                console.log("Inside ConnectedCallback");
                this.records = result.opportunities;
                this.pickVals = result.pickVals;
            })
            .catch(error => {
                console.log('getTopOpportunities error==>',JSON.stringify(error));
                
            });
       }

    
    get equalwidthHorizontalAndVertical(){
        let len = this.pickVals.length +1
        return `width: calc(100vw/ ${len})`
    }

    handleListItemDrag(event){
        this.recordId = event.detail
    }

    handleItemDrop(event){
        let stage = event.detail
        this.updateHandler(stage)
    }
    updateHandler(stage){
        const fields = {};
        fields[ID_FIELD.fieldApiName] = this.recordId;
        fields[STAGE_FIELD.fieldApiName] = stage;
        const recordInput ={fields}
        
        updateOpprtuity({ recordId: this.recordId, stageName: stage })
            .then(result => {
                console.log("Updated Successfully");
                this.showToast();
                this.records = result.opportunities;
                this.pickVals = result.pickVals;
                
            })
            .catch(error => {
                
            });
    }

    showToast(){
        this.dispatchEvent(
            new ShowToastEvent({
                title:'Success',
                message:'Stage updated Successfully',
                variant:'success'
            })
        )
    }

    
}

dragAndDropLwc.css

.card_Design{
    padding: 0.5rem;
    display: flex;
    justify-content: space-around;
    background: #fff;
}
.column_heading{
    padding: 0.5rem 0.25rem;
    font-size: 16px;
    background: #005fb2;
    color: #fff;
    margin-bottom: 1rem;
}
.stageContainer{
    float:left;
    border: 2px solid #d8dde6;
    margin: 0.05rem;
    border-radius: .25rem;
}
.slds-text-heading--small{
    font-size: 8 px;
    text-align: left;
    align-items:left ;
}
.slds-tabs--path__title{
    text-align: left;
}

.stage-info {
        display: flex;
        flex-direction: column;
        align-items: left;
        text-align: center;
    }

.stage-name {
       font-size: 13 px;
        line-height: 15px;
    }

.record-count {
        font-size: 13 px;
        line-height: 16px;
        align-items: center;
    }

OpportunityKanbanController.apxc

public with sharing class OpportunityKanbanController {
   
    @AuraEnabled(cacheable=true)
    public static OpportunityKanbanController.wrapper  getTopOpportunities(){
        OpportunityKanbanController.wrapper wrapperResponse = new OpportunityKanbanController.wrapper();
          List<Opportunity>  opps= [
                SELECT Id, Name,Priority__c,CloseDate, Account.name, StageName FROM Opportunity 
                 ORDER BY LastModifiedDate DESC NULLS LAST ];    
            List<Opportunity> oppList = new List<Opportunity>();
                 
        for(Opportunity op : opps){
            if( op.Priority__c !=null && op.Priority__c=='High' ){
                oppList.add(op);
            }
        }
   
         for(Opportunity op : opps){
            if( op.Priority__c == null ){
                oppList.add(op);
            }
        }
        wrapperResponse.opportunities = oppList;

            String objectName = 'Opportunity';
            String fieldName ='StageName';
            Map<String,Integer> stageNameMap = new Map<String,Integer>();
            Schema.SObjectType s = Schema.getGlobalDescribe().get(objectName) ;
            Schema.DescribeSObjectResult r = s.getDescribe() ;
            Map<String,Schema.SObjectField> fields = r.fields.getMap() ;
            Schema.DescribeFieldResult fieldResult = fields.get(fieldName).getDescribe();
            List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
            for( Schema.PicklistEntry pickListVal : ple){
                if(!stageNameMap.containsKey(pickListVal.getValue())){
                    stageNameMap.put(pickListVal.getValue(), 0);
                }
            }
            for(Opportunity opp: oppList){
                if(stageNameMap.containsKey(opp.StageName)){
                    Integer count = 0;
                    count = stageNameMap.get(opp.StageName);
                    stageNameMap.put(opp.StageName, ++count);
                }
            }
            List<OpportunityKanbanController.stageObject> pickValList = new List<OpportunityKanbanController.stageObject>();
            for(String stage: stageNameMap.keySet()){
                OpportunityKanbanController.stageObject pickVal = new OpportunityKanbanController.stageObject();
                pickVal.stage = stage;
                pickVal.noRecords = stageNameMap.get(stage);
                pickValList.add(pickVal);
            }
            wrapperResponse.pickVals = pickValList;


        return wrapperResponse;
    }
    @AuraEnabled
    public static OpportunityKanbanController.wrapper updateOpprtuity(String recordId, String stageName){
        if(recordId != null && stageName != null){
            Update new Opportunity(id = recordId , StageName = stageName);
        }
        return OpportunityKanbanController.getTopOpportunities();
    }
    public class wrapper{
        @AuraEnabled
        public List<Opportunity> opportunities = new List<Opportunity>(); 
        @AuraEnabled
        public List<OpportunityKanbanController.stageObject> pickVals = new List<OpportunityKanbanController.stageObject>();

    }
    public class stageObject{
        @AuraEnabled
        public String stage = '';
        @AuraEnabled
        public Integer noRecords = 0;
    }
}

dragAndDropList.html

<template>
    
     <ul class="slds-has-dividers_around-space dropZone" 
    ondrop={handleDrop}
    ondragover={handleDragOver}
    style="height:70vh; overflow-y:auto;">
        <template for:each={records} for:item="recordItem">
             
            <c-drag-and-drop-card stage={stage} record={recordItem} 
            key={recordItem.Id} onitemdrag={handleItemDrag}>
            
            </c-drag-and-drop-card>
             
        </template>
    </ul> 


</template>

dragAndDropList.js

import { LightningElement, api } from 'lwc';

export default class DragAndDropList extends LightningElement {
    @api records
    @api stage
    handleItemDrag(evt){
        const event = new CustomEvent('listitemdrag', {
            detail: evt.detail
        })
        this.dispatchEvent(event)
    }
    handleDragOver(evt){
        evt.preventDefault()
    }
    handleDrop(evt){
        const event = new CustomEvent('itemdrop', {
            detail: this.stage
        })
        this.dispatchEvent(event)
    }
}

dragAndDropCard.html

<template>
<template if:true={isSameStage} for:index="index">
   <li class="slds-item slds-var-m-around_small" draggable="true" ondragstart={itemDragStart} >
       <article class="slds-tile slds-tile_board">
           <h3 class="slds-truncate" title={record.Name}>
               <a href="#" data-id={record.Id} onclick={navigateOppHandler}>
                   <span class="slds-truncate" data-id={record.Id}>
                   {index}.{record.Name}
                   </span>
               </a>
           </h3>
           <div class="slds-tile__detail slds-text-body_small">
               <p class="slds-text-heading_small">Priority:{record.Priority__c}</p>
               <p class="slds-truncate" title={record.AccountName}>
                    <a href="#" data-id={record.AccountId} onclick={navigateAccHandler}>
                    <span class="slds-truncate" data-id={record.AccountId}>
                        {record.AccountId}
                    </span>
                </a>
               </p>

               <p class="slds-truncate" title={record.StageName}>{record.StageName}</p>
           </div>
       </article>
   </li>
</template>
</template>
.slds-item {
    border: 1px solid #d8dde6;
    border-radius: 0.25rem;
    background-clip: padding-box;
    padding: 0.45rem;
    margin: 0.25rem;
   
}
import { LightningElement, api } from 'lwc';
import { NavigationMixin } from 'lightning/navigation'
export default class DragAndDropCard extends NavigationMixin(LightningElement) {
    @api stage
    @api record

    get isSameStage(){
        return this.stage === this.record.StageName
    }
    navigateOppHandler(event){
        event.preventDefault()
        this.navigateHandler(event.target.dataset.id, 'Opportunity')
    }
    navigateAccHandler(event){
        event.preventDefault()
        this.navigateHandler(event.target.dataset.id, 'Account')
    }
    navigateHandler(Id, apiName) {
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: Id,
                objectApiName: apiName,
                actionName: 'view',
            },
        });
    }
    itemDragStart(){
        const event = new CustomEvent('itemdrag', {
            detail: this.record.Id
        })
        this.dispatchEvent(event)
    }
}

Code Explanation

Let’s dive into the code that powers the Drag-and-Drop LWC. JavaScript

Fetching Opportunities and Stages

The connectedCallback method is called when the component is connected to the DOM. Inside this method, the getTopOpportunities Apex method is used to fetch a list of opportunities and their associated stages. The fetched data is stored in the records and pickVals properties.

connectedCallback() {

    getTopOpportunities()

        .then(result => {

            this.records = result.opportunities;

            this.pickVals = result.pickVals;

        })

        .catch(error => {

            console.error(‘Error fetching opportunities:’, error);

        });

}

Rendering Kanban-Style Stages

The for:each loop in the HTML template iterates over each stage in the pickVals array and renders a tab for each stage. The equalwidthHorizontalAndVertical getter calculates the width of each tab based on the number of stages.

<template for:each={pickVals} for:item=”item”>

    <div class=”slds-tabs–path” role=”application” key={item} style={equalwidthHorizontalAndVertical}>

        <!– Render stage tabs here –>

    </div>

</template>

Drag-and-Drop Functionality

The handleListItemDrag method is triggered when an opportunity is dragged within the list. It captures the ID of the dragged opportunity.

handleListItemDrag(event) {

    this.recordId = event.detail;

}

Updating Opportunity Stages

The handleItemDrop method is called when an opportunity is dropped onto a new stage. This method triggers the updateHandler to update the opportunity’s stage.

handleItemDrop(event) {

    const stage = event.detail;

    this.updateHandler(stage);

}

Class: OpportunityKanbanController

Purpose

This class provides backend functionality for retrieving and managing data related to Opportunities in a Kanban view within a Salesforce Lightning component.

Methods

getTopOpportunities()

Description: Retrieves a list of opportunities categorized by priority and stage. Also, provides a count of opportunities in each stage.

Return Type: OpportunityKanbanController.wrapper

updateOpprtuity(String recordId, String stageName)

Description: Updates the stage of an Opportunity given its record ID.

Parameters:

  • recordId (Type: String) – The ID of the Opportunity record to update.
  • stageName (Type: String) – The new stage name to set for the Opportunity.

Return Type: OpportunityKanbanController.wrapper

Inner Classes

wrapper

Description: A wrapper class to hold the response data for both opportunities and stage information.

Attributes:

  • opportunities (Type: List<Opportunity>) – List of Opportunity records.
  • pickVals (Type: List<OpportunityKanbanController.stageObject>) – List of stageObject instances containing stage names and counts.

stageObject

Description: Represents a stage in the Kanban view along with the number of opportunities in that stage.

Attributes:

  • stage (Type: String) – The name of the stage.

noRecords (Type: Integer) – The count of opportunities in the stage.