Skip to content
Home » QuickBooks

QuickBooks

QuickBooks Integration

Easily sync your FTM invoices with QuickBooks Online to streamline your accounting workflows. Our QuickBooks integration allows you to push invoices directly from FTM into QuickBooks, update payment statuses, and maintain a seamless link between your financial records and transportation operations.

Benefits of the Integration

  • Automatic syncing of invoices from FTM to QuickBooks
  • Real-time balance updates every 15 minutes
  • Payment status updates from QuickBooks to FTM
  • Line item mapping, including additional charges and product-level breakdowns
  • Customization for customer IDs, product codes, and payment terms
  • Easy switching between sandbox and production environments

Prerequisites

Before you begin, make sure the following are completed:

Create and update Account on QB:

This Visualforce page is the UI button screen that triggers your two Apex classes to sync an

Account → QuickBooks Customer

<apex:page standardcontroller="Account" extensions="QuickBooksAccountUpdate,QuickBooksAccountUpdate2" tabStyle="Account" lightningStylesheets="true">
    <style>
    .customerError { font-weight: bold; list-style: none; }
    .invoiceError { color: red; font-weight: strong; }
    .bkg-inv { background-color: #eee; }
    .products tr { border: solid 1px #efefef; }
    .container { padding: 0% 5%;}
    .text { padding: 1%;}
    </style>
     
    <script>
    function launchWindow()
    {
        // window.top.location.reload();
       
    };
    function redir() {
        window.location = "/{!Ac.Id}";
    }
    </script>
    
    <apex:pageMessages /><br />
    <apex:form >
       
            <apex:outputPanel id="allPanel">
                    <script>
                       window.onload=function()
                       {
                         doInit();
                       };
                    </script>
                    <div class="container">
                        <h2>Create/Update Customer on QuickBooks</h2>
                        <div class="bkg-inv">
                            <div class="text">
                                <table width="100%">
                                    <tr>
                                        <td>
                                           <span style="text-align: left; vertical-align: top; font-size: 1.5em; font-weight: bold;">Customer:&nbsp;<apex:outputField value="{!Account.Name}"/></span><br /><!-- (Conf. #)-->
                                           
                                        </td>
                                        <td style="text-align: right; padding-right:5%;">
                                            <span></span><br />
                                           
                                        </td>
                                    </tr>
                                </table>
                                <br /><br />
                                <table width="100%">
                                    <tr>
                                        <td width="30%">
                                            <b>Billing Address:</b><br />
                                            <apex:outputField value="{!Account.BillingStreet}"/><br />
                                            <apex:outputPanel ><apex:outputText value="{!Account.BillingCity}"/>, <apex:outputText value=" {!Account.BillingState}"/> <apex:outputText value=" {!Account.BillingPostalCode}"/></apex:outputPanel><br />
                                            <apex:outputField value="{!Account.BillingCountry}"/><br /><br />
                                        </td>
                                        <td width="30%" style="vertical-align: top;"><b>Email:</b><br /><apex:outputText value=" {!Account.Accounting_Email__c}"/></td>
                                        <td width="30%" style="vertical-align: top;"><b>Phone:</b><br /><apex:outputText value=" {!Account.Accounting_Phone__c}"/></td>
                                    </tr>
                                </table>
                                
                           </div>
                        </div>
                        <br />
                        <apex:commandButton onComplete="redir();" value="Back" />
                        <apex:commandButton value="Create/Update Customer** OLD" action="{!send}" />
                         <apex:commandButton value="Create/Update Customer ON New QuickBooks" action="{!send2}" />
                        <br/> <br/> <br/> <br/> 
                    </div>
                </apex:outputPanel>
    </apex:form>
</apex:page>

Visualforce controller-style Apex class:

public class QuickBooksAccountUpdate{
public string Termss;
    public Account Ac {get; set;}
    public id AcID {get; set;}
    //public Account acct {get; set;}
    //public Boolean cid {get; set;}
    public string qbcustomerid;
    public string msg;
    public integer taxcodeid;
    //public id loadid {get; set;} //for unit test
    public List<Customer> clist;
    public List<Customer> clist2;
    public string cname;//for unit test
    public string cnames;//for unit test
    public string cnames2;//for unit test
     public string cnames3;//for unit test
    public string invbody3;//for unit test
    public string invbody_x;//for unit test
    
    //Apex classes from QB Customer Object
    public class BillAddr {
		public String Id;
		public String Line1;
		public String City;
        public string Country;
		public String CountrySubDivisionCode;
		public String PostalCode;
		//public String Lat; 
		//public String Long_Z; //in json: Long
    }
    public class CurrencyRef {
		public String value;
		public String name;
    }
    public class Cust { //this needs to be in a class for json deserialization to work
    	public QueryResponse QueryResponse;
		public String time_x; //time is an apex reserved word, replaced it with time_x in the response string
    }
    public class PrimaryPhone {
		public String FreeFormNumber;
    }
    public class Customer {
		public Boolean Taxable;
		public BillAddr BillAddr;
		//public BillAddr ShipAddr; 
		public Boolean Job;
		public Boolean BillWithParent;
		public Double Balance;
		public Double BalanceWithJobs;
		public CurrencyRef CurrencyRef;
		public String PreferredDeliveryMethod;
		public String domain;
		public Boolean sparse;
		public String Id;
		public String SyncToken;
		public MetaData MetaData;
		public String GivenName;
		public String FamilyName;
		public String FullyQualifiedName;
		public String CompanyName;
		public String DisplayName;
		public String PrintOnCheckName;
		public Boolean Active;
		public PrimaryPhone PrimaryPhone;
		//public PrimaryEmailAddr PrimaryEmailAddr;
    }
    public class MetaData {
		public String CreateTime;
		public String LastUpdatedTime;
    }
    public class QueryResponse {
		public List<Customer> Customer; 
		public Integer startPosition;
		public Integer maxResults;
    }
    //public class PrimaryEmailAddr {
		//public String Address;
    //}
    //end QB Customer Object classes
    
    //Apex classes from QB Invoice Object
    public class BillAddr_i { //rename to avoid duplicate class
		public String Id;
		public String Line1;
		public String Line2;
		public String City;
		public String Country;
		public String CountrySubDivisionCode;
		public String PostalCode;
        public String Lat;
		public String Long_Z; // in json: Long
    }
    public class CurrencyRef_i {
		public String value;
		public String name;
    }
	public class Invoice {
		public Integer Deposit;
		public Boolean AllowIPNPayment;
		public Boolean AllowOnlinePayment;
		public Boolean AllowOnlineCreditCardPayment;
		public Boolean AllowOnlineACHPayment;
        public String EInvoiceStatus;
		public String ECloudStatusTimeStamp;
		public String domain;
		public Boolean sparse;
		public String Id;
		public String SyncToken;
		public MetaData_i MetaData_i;
		public List<CustomField> CustomField;
		public String DocNumber;
		public String TxnDate;
		public CurrencyRef_i CurrencyRef_i;
        //public Integer ExchangeRate;
		public List<LinkedTxn> LinkedTxn;
		public List<Line> Line;
		//public TxnTaxDetail TxnTaxDetail; //
		public CurrencyRef CustomerRef;
        public TaxCodeRef CustomerMemo; //
		public BillAddr_i BillAddr_i;
        //public BillAddr ShipAddr;
        public TaxCodeRef SalesTermRef; //
		public String DueDate;
        //public String GlobalTaxCalculation; //
		public Double TotalAmt;
        public Double HomeTotalAmt;
		public Boolean ApplyTaxAfterDiscount; //USA
		public String PrintStatus;
		public String EmailStatus;
		public BillEmail BillEmail;
		public Double Balance; //use Double instead of Integer, otherwise errors with decimals
        //public Double HomeBalance;
        public DeliveryInfo DeliveryInfo;
		public BillAddr_Z ShipAddr;
    }
    public class BillEmail {
		public String Address;
	}
    public class DeliveryInfo {
		public String DeliveryType;
		public String DeliveryTime;
	}
    public class BillAddr_Z {
		public String Id;
		public String Line1;
		public String Line2;
		public String City;
		public String CountrySubDivisionCode;
		public String PostalCode;
	}
    public class SalesItemLineDetail {
		public CurrencyRef ItemRef;
        public Double UnitPrice; //use Double instead of Integer, otherwise errors with decimals
        public Integer Qty; //
        public CurrencyRef ItemAccountRef;
		public TaxCodeRef TaxCodeRef; //
    }
    public class Line_Z {
		public String Id;
		public Integer LineNum;
		public Double Amount; //use Double instead of Integer, otherwise errors with decimals
		public String DetailType;
		public SalesItemLineDetail SalesItemLineDetail;
		public CustomField SubTotalLineDetail;
	}

	public class Line_Y {
		public String Id;
		public Integer LineNum;
		public Double Amount; //use Double instead of Integer, otherwise errors with decimals
		public String DetailType;
		public SalesItemLineDetail SalesItemLineDetail;
		public CustomField SubTotalLineDetail;
	}

	public class Line_X {
		public String Id;
		public Integer LineNum;
		public Double Amount; //use Double instead of Integer, otherwise errors with decimals
		public String DetailType;
		public SalesItemLineDetail SalesItemLineDetail;
		public CustomField SubTotalLineDetail;
	}
    public class Line_W {
		public String Id;
		public Integer LineNum;
		public Double Amount; //use Double instead of Integer, otherwise errors with decimals
		public String DetailType;
		public SalesItemLineDetail SalesItemLineDetail;
		public CustomField SubTotalLineDetail;
	}
    public class Inv {
		public QueryResponse_i QueryResponse_i;
		public String time_i;
    }
    /*public class TaxLine { /////
		public Double Amount;
		public String DetailType;
		//public TaxLineDetail TaxLineDetail;
    }*/
    public class Line {
		public String Id;
		public Integer LineNum;
		//public String Description;
		public Double Amount; //use Double instead of Integer, otherwise errors with decimals
		public String DetailType;
		public SalesItemLineDetail SalesItemLineDetail;
		public LinkedTxn SubTotalLineDetail;
    }
    public class TaxCodeRef { //
		public String value;
    }
    public class MetaData_i {
		public String CreateTime;
		public String LastUpdatedTime;
    }
   /*public class TaxLineDetail {
		public TaxCodeRef TaxRateRef;
		public Boolean PercentBased;
		public Double TaxPercent; //Changed from Integer to Double, otherwise errors with decimals
		public Double NetAmountTaxable;
    }*/
    public class QueryResponse_i { //rename to avoid duplicate class
		public List<Invoice> Invoice;
		public Integer startPosition;
		public Integer maxResults;
		public Integer totalCount;
    }
    public class CustomField {
		public String DefinitionId;
		public String Type_Z; // in json: Type
		public String Name;
		public String StringValue;
    }
    //public class BillEmail {
		//public String Address;
    //}
    public class LinkedTxn {
        public String TxnId;
		public String TxnType;
    }
    /*public class TxnTaxDetail {
		public Double TotalTax;
        public List<TaxLine> TaxLine;
    }*/
    //end QB Invoice Object classes
    
    
    
      public QuickBooksAccountUpdate(ApexPages.StandardController stdController) {
        AcID = ApexPages.currentPage().getParameters().get('id');
        clist = new List<Customer>();
           clist2 = new List<Customer>();
     	  Ac = [select Id, Name,BillingState,BillingCity,BillingStreet,Phone,BillingPostalCode,BillingCountry,Accounting_Phone__c,Accounting_Email__c,LQB_QB_Id__c
                                     from Account 
                                     where Id = :AcID];
          system.debug('Account :'+Ac);
    }
    
    
    
    
    
    
    
    //create invoice in QB
    public PageReference send() {
        
        system.debug('Account :'+Ac);
        //if customer fields are blank on Account, prompt error msg before creating new customer
        if(Ac.Accounting_Phone__c != null && Ac.BillingStreet != null && Ac.BillingCity != null) {
            
            HttpRequest reqcust = new HttpRequest();
            //Prep customer name (spaces + 's) for query to work
            string cnam = Ac.Name;
            String cname = cnam.replace('&', '%26');
            String cnames = cname.replace(' ', '%20');
            String cnames2 = cnames.replace('\'s', '%5c%27s');  
            String cnames3 = cnames2.replace('é', '%C3%A9'); 
            reqcust.setEndpoint('callout:QuickBooks/query?query=select%20%2a%20from%20Customer%20Where%20DisplayName%20%3d%20%27'+ cnames3 +'%27');
            reqcust.setHeader('Accept','application/json');
            reqcust.setMethod('GET');
            reqcust.setTimeout(2000); // timeout in milliseconds
            
            Http httpc = new Http();
            HTTPResponse rescust = httpc.send(reqcust);    
            //System.debug(rescust.toString()); 
            
            String custbody = rescust.getBody();
            String custbody_x = custbody.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
            system.debug('EEE: ' + custbody_x);
            
            //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
            if (rescust.getStatusCode() >= 200 && rescust.getStatusCode() < 300) {
                QuickBooksAccountUpdate.Cust c = (QuickBooksAccountUpdate.Cust)JSON.deserialize(custbody_x, QuickBooksAccountUpdate.Cust.class);
                system.debug('Will see: ' + c);
                QueryResponse q = c.QueryResponse;
                system.debug('Will see: ' + q); 
                List<Customer> clist = q.Customer;
                system.debug('Will see: ' + clist); 
                
                //if Customer does not exist in QB, create new Customer in QB
                if(clist == null) {
                    
                    string CustomerEmailAdd;
                    if(Ac.Accounting_Email__c != null ) {
                        CustomerEmailAdd= Ac.Accounting_Email__c;
                    }else{
                        CustomerEmailAdd = '';
                    }
                    
                    string BillingPostalCode;
                    if(Ac.BillingPostalCode != null ) {
                        BillingPostalCode= Ac.BillingPostalCode;
                    }else{
                        BillingPostalCode = '';
                    }
                    
                    string BillingCountry;
                    if(Ac.BillingCountry != null ) {
                        BillingCountry= Ac.BillingCountry;
                    }else{
                        BillingCountry = '';
                    }
                    string BillingState;
                    if(Ac.BillingState != null ) {
                        BillingState= Ac.BillingState;
                    }else{
                        BillingState = '';
                    }
                    
                    
                    
                    JSONGenerator genc = JSON.createGenerator(true); 
                    genc.writeStartObject(); //{
                    genc.writeStringField('DisplayName', Ac.Name); 
                    genc.writeFieldName('PrimaryEmailAddr');
                    genc.writeStartObject(); //{
                    genc.writeStringField('Address', CustomerEmailAdd);  
                    genc.writeEndObject(); //}
                    genc.writeFieldName('PrimaryPhone');
                    genc.writeStartObject(); //{
                    genc.writeStringField('FreeFormNumber', Ac.Accounting_Phone__c); 
                    genc.writeEndObject(); //}
                    genc.writeStringField('CompanyName', Ac.Name); 
                    genc.writeFieldName('BillAddr');
                    genc.writeStartObject(); //{
                    genc.writeStringField('CountrySubDivisionCode', BillingState); 
                    genc.writeStringField('City', Ac.BillingCity); 
                    genc.writeStringField('PostalCode', BillingPostalCode); 
                    genc.writeStringField('Line1', Ac.BillingStreet); 
                    genc.writeStringField('Country', BillingCountry); 
                    genc.writeEndObject(); //}
                    genc.writeEndObject(); //}
                    
                    String qbnewc = genc.getAsString();
                    //Sending the http body with JSON 
                    HttpRequest req = new HttpRequest();
                    req.setEndpoint('callout:QuickBooks/customer');
                    req.setHeader('Content-Type','application/json');
                    req.setMethod('POST');
                    req.setHeader('Cache-Control', 'no-cache');
                    req.setTimeout(2000); //timeout in milliseconds
                    req.setBody(qbnewc);
                    
                    Http http = new Http();
                    HTTPResponse res = http.send(req);
                    
                    System.debug('Response: ' + res);
                    //getQBCustomer();
                    if (res.getStatusCode() >= 200 && res.getStatusCode() < 300 && Ac.LQB_QB_Id__c == null || Ac.LQB_QB_Id__c == '') {
                        
                        
                        HttpRequest reqcust2 = new HttpRequest();
                        reqcust2.setEndpoint('callout:QuickBooks/query?query=select%20%2a%20from%20Customer%20Where%20DisplayName%20%3d%20%27'+ cnames2 +'%27');
                        reqcust2.setHeader('Accept','application/json');
                        reqcust2.setMethod('GET');
                        reqcust2.setTimeout(2000); // timeout in milliseconds
                        
                        Http httpc2 = new Http();
                        HTTPResponse rescust2 = httpc2.send(reqcust2);    
                        //System.debug(rescust.toString()); 
                        
                        String custbody2 = rescust2.getBody();
                        String custbody2_x = custbody2.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
                        system.debug('EEE: ' + custbody2_x);
                        
                        //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
                        if (rescust2.getStatusCode() >= 200 && rescust2.getStatusCode() < 300) {
                            QuickBooksAccountUpdate.Cust c2 = (QuickBooksAccountUpdate.Cust)JSON.deserialize(custbody2_x, QuickBooksAccountUpdate.Cust.class);
                            system.debug('Will see: ' + c2);
                            QueryResponse q2 = c2.QueryResponse;
                            system.debug('Will see: ' + q2); 
                            List<Customer> clist2 = q2.Customer;
                            system.debug('Will see: ' + clist2); 
                            if (!Test.isRunningTest()) {
                                for(Customer qbc : clist2) {
                                    
                                    String SyncToken = qbc.SyncToken;
                                    String CustomerID = qbc.Id;
                                    qbcustomerid = qbc.Id;
                                    updateAccountQBCustomerId(Ac.Id, qbcustomerid);
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Customer has been added to QB.'));
                                }
                            }
                        }
                    }
                    else {
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was an Error on Creating the Customer, Please contact the administrator.'));
                                }
                        
                        
                        
                       
                    }
                    
                    else { //customer exists in QB
                        for(Customer qbc : clist) {
                            
                             String SyncToken = qbc.SyncToken;
                            String CustomerID = qbc.Id;
                            system.debug('Customer val: ' + qbc.DisplayName);
                            //check if Customer exists in QB, if it does, grab Customer Id from QB
                            if(qbc.DisplayName == Ac.Name) {
                                
                         string CustomerEmailAdd;
                                        if(Ac.Accounting_Email__c != null ) {
                                            CustomerEmailAdd= Ac.Accounting_Email__c;
                                        }else{
                                            CustomerEmailAdd = '';
                                        }
                        
                        string BillingPostalCode;
                                        if(Ac.BillingPostalCode != null ) {
                                            BillingPostalCode= Ac.BillingPostalCode;
                                        }else{
                                            BillingPostalCode = '';
                                        }
                        
                           string BillingCountry;
                                        if(Ac.BillingCountry != null ) {
                                            BillingCountry= Ac.BillingCountry;
                                        }else{
                                            BillingCountry = '';
                                        }    
                                
                                 string BillingState;
                                        if(Ac.BillingState != null ) {
                                            BillingState= Ac.BillingState;
                                        }else{
                                            BillingState = '';
                                        }      
                        
                                String jsonString;
                                
                                // Create a JSON generator
                                JSONGenerator genc = JSON.createGenerator(true);
                                
                                // Start building the JSON object
                                genc.writeStartObject(); //{
                                genc.writeStringField('domain', 'QBO');
                                genc.writeFieldName('PrimaryEmailAddr');
                                genc.writeStartObject(); //{
                                genc.writeStringField('Address', CustomerEmailAdd);
                                genc.writeEndObject(); //}
                                genc.writeStringField('DisplayName', Ac.Name);
                                genc.writeStringField('FullyQualifiedName', Ac.Name);
                                genc.writeFieldName('PrimaryPhone');
                                genc.writeStartObject(); //{
                                genc.writeStringField('FreeFormNumber', Ac.Accounting_Phone__c);
                                genc.writeEndObject(); //}
                                genc.writeBooleanField('Active', true);
                                genc.writeFieldName('BillAddr');
                                genc.writeStartObject(); //{
                                genc.writeStringField('CountrySubDivisionCode', BillingState); 
                                genc.writeStringField('City', Ac.BillingCity); 
                                genc.writeStringField('PostalCode', BillingPostalCode); 
                                genc.writeStringField('Line1', Ac.BillingStreet); 
                                genc.writeStringField('Country', BillingCountry); 
                                genc.writeEndObject(); //}
                                genc.writeStringField('SyncToken', SyncToken);
                                genc.writeStringField('CompanyName', Ac.Name);
                                genc.writeStringField('Id', CustomerID);
                                genc.writeEndObject(); //}
                                

                                String qbnewc = genc.getAsString();
                                //Sending the http body with JSON 
                                HttpRequest req = new HttpRequest();
                                req.setEndpoint('callout:QuickBooks/customer');
                                req.setHeader('Content-Type','application/json');
                                req.setMethod('POST');
                                req.setHeader('Cache-Control', 'no-cache');
                                req.setTimeout(2000); //timeout in milliseconds
                                req.setBody(qbnewc);
                                
                                Http http = new Http();
                                HTTPResponse res = http.send(req);
                                
                                System.debug('Response: ' + res);
                                if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
                                    qbcustomerid = qbc.Id;
                                    updateAccountQBCustomerId(Ac.Id, qbCustomerId);
                                    system.debug('great, but no page prompt: ' + qbc.Id);
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Customer has been Updated in QB.'));
                                    // Update the Salesforce record here
                                } else {
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was an Error, Please contact the administrator.'));
                                }
                                
                                
                                //msg = 'Customer is in QB. Please refresh browser, and click button once more to create Invoice in QB.';
                            }
                        }
                    }
                
                }//if query response error, prompt error msg
                else {
                 ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was a query issue in QB. Please contact your Administrator.')); 
                }
            }//customer address/phone is not null
            else {
            ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'Please make sure Customer\'s Phone and Billing Address is filled in under Accounting Information.')); 
            }
    	return null;
    }      
    
    
    private static void updateAccountQBCustomerId(Id accountId, String qbCustomerId) {
    if (qbcustomerid != null) {
        Account accToUpdate = new Account(Id = accountId, QBCustomerID__c = qbCustomerId,LQB_QB_Error__c = 'Success');
        update accToUpdate;
    } else {
        // Handle the case where qbcustomerid is null (optional)
        System.debug('qbcustomerid is null. Update not performed.');
    }
}
    

    public String getName() {
        return 'QuickBooksAccountUpdate';
    }
}

========================================================================

Apex2:

public class QuickBooksAccountUpdate2{
public string Termss;
    public Account Ac {get; set;}
    public id AcID {get; set;}
    //public Account acct {get; set;}
    //public Boolean cid {get; set;}
    public string qbcustomerid;
    public string msg;
    public integer taxcodeid;
    //public id loadid {get; set;} //for unit test
    public List<Customer> clist;
    public List<Customer> clist2;
    public string cname;//for unit test
    public string cnames;//for unit test
    public string cnames2;//for unit test
     public string cnames3;//for unit test
    public string invbody3;//for unit test
    public string invbody_x;//for unit test
    
    //Apex classes from QB Customer Object
    public class BillAddr {
        public String Id;
        public String Line1;
        public String City;
        public string Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        //public String Lat; 
        //public String Long_Z; //in json: Long
    }
    public class CurrencyRef {
        public String value;
        public String name;
    }
    public class Cust { //this needs to be in a class for json deserialization to work
        public QueryResponse QueryResponse;
        public String time_x; //time is an apex reserved word, replaced it with time_x in the response string
    }
    public class PrimaryPhone {
        public String FreeFormNumber;
    }
    public class Customer {
        public Boolean Taxable;
        public BillAddr BillAddr;
        //public BillAddr ShipAddr; 
        public Boolean Job;
        public Boolean BillWithParent;
        public Double Balance;
        public Double BalanceWithJobs;
        public CurrencyRef CurrencyRef;
        public String PreferredDeliveryMethod;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData MetaData;
        public String GivenName;
        public String FamilyName;
        public String FullyQualifiedName;
        public String CompanyName;
        public String DisplayName;
        public String PrintOnCheckName;
        public Boolean Active;
        public PrimaryPhone PrimaryPhone;
        //public PrimaryEmailAddr PrimaryEmailAddr;
    }
    public class MetaData {
        public String CreateTime;
        public String LastUpdatedTime;
    }
    public class QueryResponse {
        public List<Customer> Customer; 
        public Integer startPosition;
        public Integer maxResults;
    }
    //public class PrimaryEmailAddr {
        //public String Address;
    //}
    //end QB Customer Object classes
    
    //Apex classes from QB Invoice Object
    public class BillAddr_i { //rename to avoid duplicate class
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        public String Lat;
        public String Long_Z; // in json: Long
    }
    public class CurrencyRef_i {
        public String value;
        public String name;
    }
    public class Invoice {
        public Integer Deposit;
        public Boolean AllowIPNPayment;
        public Boolean AllowOnlinePayment;
        public Boolean AllowOnlineCreditCardPayment;
        public Boolean AllowOnlineACHPayment;
        public String EInvoiceStatus;
        public String ECloudStatusTimeStamp;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData_i MetaData_i;
        public List<CustomField> CustomField;
        public String DocNumber;
        public String TxnDate;
        public CurrencyRef_i CurrencyRef_i;
        //public Integer ExchangeRate;
        public List<LinkedTxn> LinkedTxn;
        public List<Line> Line;
        //public TxnTaxDetail TxnTaxDetail; //
        public CurrencyRef CustomerRef;
        public TaxCodeRef CustomerMemo; //
        public BillAddr_i BillAddr_i;
        //public BillAddr ShipAddr;
        public TaxCodeRef SalesTermRef; //
        public String DueDate;
        //public String GlobalTaxCalculation; //
        public Double TotalAmt;
        public Double HomeTotalAmt;
        public Boolean ApplyTaxAfterDiscount; //USA
        public String PrintStatus;
        public String EmailStatus;
        public BillEmail BillEmail;
        public Double Balance; //use Double instead of Integer, otherwise errors with decimals
        //public Double HomeBalance;
        public DeliveryInfo DeliveryInfo;
        public BillAddr_Z ShipAddr;
    }
    public class BillEmail {
        public String Address;
    }
    public class DeliveryInfo {
        public String DeliveryType;
        public String DeliveryTime;
    }
    public class BillAddr_Z {
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String CountrySubDivisionCode;
        public String PostalCode;
    }
    public class SalesItemLineDetail {
        public CurrencyRef ItemRef;
        public Double UnitPrice; //use Double instead of Integer, otherwise errors with decimals
        public Integer Qty; //
        public CurrencyRef ItemAccountRef;
        public TaxCodeRef TaxCodeRef; //
    }
    public class Line_Z {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }

    public class Line_Y {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }

    public class Line_X {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Line_W {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Inv {
        public QueryResponse_i QueryResponse_i;
        public String time_i;
    }
    /*public class TaxLine { /////
        public Double Amount;
        public String DetailType;
        //public TaxLineDetail TaxLineDetail;
    }*/
    public class Line {
        public String Id;
        public Integer LineNum;
        //public String Description;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public LinkedTxn SubTotalLineDetail;
    }
    public class TaxCodeRef { //
        public String value;
    }
    public class MetaData_i {
        public String CreateTime;
        public String LastUpdatedTime;
    }
   /*public class TaxLineDetail {
        public TaxCodeRef TaxRateRef;
        public Boolean PercentBased;
        public Double TaxPercent; //Changed from Integer to Double, otherwise errors with decimals
        public Double NetAmountTaxable;
    }*/
    public class QueryResponse_i { //rename to avoid duplicate class
        public List<Invoice> Invoice;
        public Integer startPosition;
        public Integer maxResults;
        public Integer totalCount;
    }
    public class CustomField {
        public String DefinitionId;
        public String Type_Z; // in json: Type
        public String Name;
        public String StringValue;
    }
    //public class BillEmail {
        //public String Address;
    //}
    public class LinkedTxn {
        public String TxnId;
        public String TxnType;
    }
    /*public class TxnTaxDetail {
        public Double TotalTax;
        public List<TaxLine> TaxLine;
    }*/
    //end QB Invoice Object classes
    
    
    
      public QuickBooksAccountUpdate2(ApexPages.StandardController stdController) {
        AcID = ApexPages.currentPage().getParameters().get('id');
        clist = new List<Customer>();
           clist2 = new List<Customer>();
          Ac = [select Id, Name,BillingState,BillingCity,BillingStreet,Phone,BillingPostalCode,BillingCountry,Accounting_Phone__c,Accounting_Email__c,LQB_QB_Id__c
                                     from Account 
                                     where Id = :AcID];
          system.debug('Account :'+Ac);
    }
    
    
    
    
    
    
    
    //create invoice in QB
    public PageReference send2() {
        
        system.debug('Account :'+Ac);
        //if customer fields are blank on Account, prompt error msg before creating new customer
        if(Ac.Accounting_Phone__c != null && Ac.BillingStreet != null && Ac.BillingCity != null) {
            
            HttpRequest reqcust = new HttpRequest();
            //Prep customer name (spaces + 's) for query to work
            string cnam = Ac.Name;
            String cname = cnam.replace('&', '%26');
            String cnames = cname.replace(' ', '%20');
            String cnames2 = cnames.replace('\'s', '%5c%27s');  
            String cnames3 = cnames2.replace('é', '%C3%A9'); 
            reqcust.setEndpoint('callout:QuickBooks2/query?query=select%20%2a%20from%20Customer%20Where%20DisplayName%20%3d%20%27'+ cnames3 +'%27');
            reqcust.setHeader('Accept','application/json');
            reqcust.setMethod('GET');
            reqcust.setTimeout(2000); // timeout in milliseconds
            
            Http httpc = new Http();
            HTTPResponse rescust = httpc.send(reqcust);    
            //System.debug(rescust.toString()); 
            
            String custbody = rescust.getBody();
            String custbody_x = custbody.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
            system.debug('EEE: ' + custbody_x);
            
            //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
            if (rescust.getStatusCode() >= 200 && rescust.getStatusCode() < 300) {
                QuickBooksAccountUpdate2.Cust c = (QuickBooksAccountUpdate2.Cust)JSON.deserialize(custbody_x, QuickBooksAccountUpdate2.Cust.class);
                system.debug('Will see: ' + c);
                QueryResponse q = c.QueryResponse;
                system.debug('Will see: ' + q); 
                List<Customer> clist = q.Customer;
                system.debug('Will see: ' + clist); 
                
                //if Customer does not exist in QB, create new Customer in QB
                if(clist == null) {
                    
                    string CustomerEmailAdd;
                    if(Ac.Accounting_Email__c != null ) {
                        CustomerEmailAdd= Ac.Accounting_Email__c;
                    }else{
                        CustomerEmailAdd = '';
                    }
                    
                    string BillingPostalCode;
                    if(Ac.BillingPostalCode != null ) {
                        BillingPostalCode= Ac.BillingPostalCode;
                    }else{
                        BillingPostalCode = '';
                    }
                    
                    string BillingCountry;
                    if(Ac.BillingCountry != null ) {
                        BillingCountry= Ac.BillingCountry;
                    }else{
                        BillingCountry = '';
                    }
                    string BillingState;
                    if(Ac.BillingState != null ) {
                        BillingState= Ac.BillingState;
                    }else{
                        BillingState = '';
                    }
                    
                    
                    
                    JSONGenerator genc = JSON.createGenerator(true); 
                    genc.writeStartObject(); //{
                    genc.writeStringField('DisplayName', Ac.Name); 
                    genc.writeFieldName('PrimaryEmailAddr');
                    genc.writeStartObject(); //{
                    genc.writeStringField('Address', CustomerEmailAdd);  
                    genc.writeEndObject(); //}
                    genc.writeFieldName('PrimaryPhone');
                    genc.writeStartObject(); //{
                    genc.writeStringField('FreeFormNumber', Ac.Accounting_Phone__c); 
                    genc.writeEndObject(); //}
                    genc.writeStringField('CompanyName', Ac.Name); 
                    genc.writeFieldName('BillAddr');
                    genc.writeStartObject(); //{
                    genc.writeStringField('CountrySubDivisionCode', BillingState); 
                    genc.writeStringField('City', Ac.BillingCity); 
                    genc.writeStringField('PostalCode', BillingPostalCode); 
                    genc.writeStringField('Line1', Ac.BillingStreet); 
                    genc.writeStringField('Country', BillingCountry); 
                    genc.writeEndObject(); //}
                    genc.writeEndObject(); //}
                    
                    String qbnewc = genc.getAsString();
                    //Sending the http body with JSON 
                    HttpRequest req = new HttpRequest();
                    req.setEndpoint('callout:QuickBooks2/customer');
                    req.setHeader('Content-Type','application/json');
                    req.setMethod('POST');
                    req.setHeader('Cache-Control', 'no-cache');
                    req.setTimeout(2000); //timeout in milliseconds
                    req.setBody(qbnewc);
                    
                    Http http = new Http();
                    HTTPResponse res = http.send(req);
                    
                    System.debug('Response: ' + res);
                    //getQBCustomer();
                    if (res.getStatusCode() >= 200 && res.getStatusCode() < 300 && Ac.LQB_QB_Id__c == null || Ac.LQB_QB_Id__c == '') {
                        
                        
                        HttpRequest reqcust2 = new HttpRequest();
                        reqcust2.setEndpoint('callout:QuickBooks2/query?query=select%20%2a%20from%20Customer%20Where%20DisplayName%20%3d%20%27'+ cnames2 +'%27');
                        reqcust2.setHeader('Accept','application/json');
                        reqcust2.setMethod('GET');
                        reqcust2.setTimeout(2000); // timeout in milliseconds
                        
                        Http httpc2 = new Http();
                        HTTPResponse rescust2 = httpc2.send(reqcust2);    
                        //System.debug(rescust.toString()); 
                        
                        String custbody2 = rescust2.getBody();
                        String custbody2_x = custbody2.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
                        system.debug('EEE: ' + custbody2_x);
                        
                        //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
                        if (rescust2.getStatusCode() >= 200 && rescust2.getStatusCode() < 300) {
                            QuickBooksAccountUpdate2.Cust c2 = (QuickBooksAccountUpdate2.Cust)JSON.deserialize(custbody2_x, QuickBooksAccountUpdate2.Cust.class);
                            system.debug('Will see: ' + c2);
                            QueryResponse q2 = c2.QueryResponse;
                            system.debug('Will see: ' + q2); 
                            List<Customer> clist2 = q2.Customer;
                            system.debug('Will see: ' + clist2); 
                            if (!Test.isRunningTest()) {
                                for(Customer qbc : clist2) {
                                    
                                    String SyncToken = qbc.SyncToken;
                                    String CustomerID = qbc.Id;
                                    qbcustomerid = qbc.Id;
                                    updateAccountQBCustomerId(Ac.Id, qbcustomerid);
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Customer has been added to QB.'));
                                }
                            }
                        }
                    }
                    else {
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was an Error on Creating the Customer, Please contact the administrator.'));
                                }
                        
                        
                        
                       
                    }
                    
                    else { //customer exists in QB
                        for(Customer qbc : clist) {
                            
                             String SyncToken = qbc.SyncToken;
                            String CustomerID = qbc.Id;
                            system.debug('Customer val: ' + qbc.DisplayName);
                            //check if Customer exists in QB, if it does, grab Customer Id from QB
                            if(qbc.DisplayName == Ac.Name) {
                                
                         string CustomerEmailAdd;
                                        if(Ac.Accounting_Email__c != null ) {
                                            CustomerEmailAdd= Ac.Accounting_Email__c;
                                        }else{
                                            CustomerEmailAdd = '';
                                        }
                        
                        string BillingPostalCode;
                                        if(Ac.BillingPostalCode != null ) {
                                            BillingPostalCode= Ac.BillingPostalCode;
                                        }else{
                                            BillingPostalCode = '';
                                        }
                        
                           string BillingCountry;
                                        if(Ac.BillingCountry != null ) {
                                            BillingCountry= Ac.BillingCountry;
                                        }else{
                                            BillingCountry = '';
                                        }    
                                
                                 string BillingState;
                                        if(Ac.BillingState != null ) {
                                            BillingState= Ac.BillingState;
                                        }else{
                                            BillingState = '';
                                        }      
                        
                                String jsonString;
                                
                                // Create a JSON generator
                                JSONGenerator genc = JSON.createGenerator(true);
                                
                                // Start building the JSON object
                                genc.writeStartObject(); //{
                                genc.writeStringField('domain', 'QBO');
                                genc.writeFieldName('PrimaryEmailAddr');
                                genc.writeStartObject(); //{
                                genc.writeStringField('Address', CustomerEmailAdd);
                                genc.writeEndObject(); //}
                                genc.writeStringField('DisplayName', Ac.Name);
                                genc.writeStringField('FullyQualifiedName', Ac.Name);
                                genc.writeFieldName('PrimaryPhone');
                                genc.writeStartObject(); //{
                                genc.writeStringField('FreeFormNumber', Ac.Accounting_Phone__c);
                                genc.writeEndObject(); //}
                                genc.writeBooleanField('Active', true);
                                genc.writeFieldName('BillAddr');
                                genc.writeStartObject(); //{
                                genc.writeStringField('CountrySubDivisionCode', BillingState); 
                                genc.writeStringField('City', Ac.BillingCity); 
                                genc.writeStringField('PostalCode', BillingPostalCode); 
                                genc.writeStringField('Line1', Ac.BillingStreet); 
                                genc.writeStringField('Country', BillingCountry); 
                                genc.writeEndObject(); //}
                                genc.writeStringField('SyncToken', SyncToken);
                                genc.writeStringField('CompanyName', Ac.Name);
                                genc.writeStringField('Id', CustomerID);
                                genc.writeEndObject(); //}
                                

                                String qbnewc = genc.getAsString();
                                //Sending the http body with JSON 
                                HttpRequest req = new HttpRequest();
                                req.setEndpoint('callout:QuickBooks2/customer');
                                req.setHeader('Content-Type','application/json');
                                req.setMethod('POST');
                                req.setHeader('Cache-Control', 'no-cache');
                                req.setTimeout(2000); //timeout in milliseconds
                                req.setBody(qbnewc);
                                
                                Http http = new Http();
                                HTTPResponse res = http.send(req);
                                
                                System.debug('Response: ' + res);
                                if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
                                    qbcustomerid = qbc.Id;
                                    updateAccountQBCustomerId(Ac.Id, qbCustomerId);
                                    system.debug('great, but no page prompt: ' + qbc.Id);
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Customer has been Updated in QB.'));
                                    // Update the Salesforce record here
                                } else {
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was an Error, Please contact the administrator.'));
                                }
                                
                                
                                //msg = 'Customer is in QB. Please refresh browser, and click button once more to create Invoice in QB.';
                            }
                        }
                    }
                
                }//if query response error, prompt error msg
                else {
                 ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was a query issue in QB. Please contact your Administrator.')); 
                }
            }//customer address/phone is not null
            else {
            ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'Please make sure Customer\'s Phone and Billing Address is filled in under Accounting Information.')); 
            }
        return null;
    }      
    
    
    private static void updateAccountQBCustomerId(Id accountId, String qbCustomerId) {
    if (qbcustomerid != null) {
        Account accToUpdate = new Account(Id = accountId, QBCustomerIDNEW__c = qbCustomerId,LQB_QB_Error__c = 'Success');
        update accToUpdate;
    } else {
        // Handle the case where qbcustomerid is null (optional)
        System.debug('qbcustomerid is null. Update not performed.');
    }
}
    

    public String getName() {
        return 'QuickBooksAccountUpdate2';
    }
}


“QuickBooksAccountUpdate2” is pointing to a different QuickBooks connection and saving the QB Id into a different Salesforce field:

public class QuickBooksAccountUpdate2{
public string Termss;
    public Account Ac {get; set;}
    public id AcID {get; set;}
    //public Account acct {get; set;}
    //public Boolean cid {get; set;}
    public string qbcustomerid;
    public string msg;
    public integer taxcodeid;
    //public id loadid {get; set;} //for unit test
    public List<Customer> clist;
    public List<Customer> clist2;
    public string cname;//for unit test
    public string cnames;//for unit test
    public string cnames2;//for unit test
     public string cnames3;//for unit test
    public string invbody3;//for unit test
    public string invbody_x;//for unit test
    
    //Apex classes from QB Customer Object
    public class BillAddr {
        public String Id;
        public String Line1;
        public String City;
        public string Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        //public String Lat; 
        //public String Long_Z; //in json: Long
    }
    public class CurrencyRef {
        public String value;
        public String name;
    }
    public class Cust { //this needs to be in a class for json deserialization to work
        public QueryResponse QueryResponse;
        public String time_x; //time is an apex reserved word, replaced it with time_x in the response string
    }
    public class PrimaryPhone {
        public String FreeFormNumber;
    }
    public class Customer {
        public Boolean Taxable;
        public BillAddr BillAddr;
        //public BillAddr ShipAddr; 
        public Boolean Job;
        public Boolean BillWithParent;
        public Double Balance;
        public Double BalanceWithJobs;
        public CurrencyRef CurrencyRef;
        public String PreferredDeliveryMethod;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData MetaData;
        public String GivenName;
        public String FamilyName;
        public String FullyQualifiedName;
        public String CompanyName;
        public String DisplayName;
        public String PrintOnCheckName;
        public Boolean Active;
        public PrimaryPhone PrimaryPhone;
        //public PrimaryEmailAddr PrimaryEmailAddr;
    }
    public class MetaData {
        public String CreateTime;
        public String LastUpdatedTime;
    }
    public class QueryResponse {
        public List<Customer> Customer; 
        public Integer startPosition;
        public Integer maxResults;
    }
    //public class PrimaryEmailAddr {
        //public String Address;
    //}
    //end QB Customer Object classes
    
    //Apex classes from QB Invoice Object
    public class BillAddr_i { //rename to avoid duplicate class
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        public String Lat;
        public String Long_Z; // in json: Long
    }
    public class CurrencyRef_i {
        public String value;
        public String name;
    }
    public class Invoice {
        public Integer Deposit;
        public Boolean AllowIPNPayment;
        public Boolean AllowOnlinePayment;
        public Boolean AllowOnlineCreditCardPayment;
        public Boolean AllowOnlineACHPayment;
        public String EInvoiceStatus;
        public String ECloudStatusTimeStamp;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData_i MetaData_i;
        public List<CustomField> CustomField;
        public String DocNumber;
        public String TxnDate;
        public CurrencyRef_i CurrencyRef_i;
        //public Integer ExchangeRate;
        public List<LinkedTxn> LinkedTxn;
        public List<Line> Line;
        //public TxnTaxDetail TxnTaxDetail; //
        public CurrencyRef CustomerRef;
        public TaxCodeRef CustomerMemo; //
        public BillAddr_i BillAddr_i;
        //public BillAddr ShipAddr;
        public TaxCodeRef SalesTermRef; //
        public String DueDate;
        //public String GlobalTaxCalculation; //
        public Double TotalAmt;
        public Double HomeTotalAmt;
        public Boolean ApplyTaxAfterDiscount; //USA
        public String PrintStatus;
        public String EmailStatus;
        public BillEmail BillEmail;
        public Double Balance; //use Double instead of Integer, otherwise errors with decimals
        //public Double HomeBalance;
        public DeliveryInfo DeliveryInfo;
        public BillAddr_Z ShipAddr;
    }
    public class BillEmail {
        public String Address;
    }
    public class DeliveryInfo {
        public String DeliveryType;
        public String DeliveryTime;
    }
    public class BillAddr_Z {
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String CountrySubDivisionCode;
        public String PostalCode;
    }
    public class SalesItemLineDetail {
        public CurrencyRef ItemRef;
        public Double UnitPrice; //use Double instead of Integer, otherwise errors with decimals
        public Integer Qty; //
        public CurrencyRef ItemAccountRef;
        public TaxCodeRef TaxCodeRef; //
    }
    public class Line_Z {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }

    public class Line_Y {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }

    public class Line_X {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Line_W {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Inv {
        public QueryResponse_i QueryResponse_i;
        public String time_i;
    }
    /*public class TaxLine { /////
        public Double Amount;
        public String DetailType;
        //public TaxLineDetail TaxLineDetail;
    }*/
    public class Line {
        public String Id;
        public Integer LineNum;
        //public String Description;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public LinkedTxn SubTotalLineDetail;
    }
    public class TaxCodeRef { //
        public String value;
    }
    public class MetaData_i {
        public String CreateTime;
        public String LastUpdatedTime;
    }
   /*public class TaxLineDetail {
        public TaxCodeRef TaxRateRef;
        public Boolean PercentBased;
        public Double TaxPercent; //Changed from Integer to Double, otherwise errors with decimals
        public Double NetAmountTaxable;
    }*/
    public class QueryResponse_i { //rename to avoid duplicate class
        public List<Invoice> Invoice;
        public Integer startPosition;
        public Integer maxResults;
        public Integer totalCount;
    }
    public class CustomField {
        public String DefinitionId;
        public String Type_Z; // in json: Type
        public String Name;
        public String StringValue;
    }
    //public class BillEmail {
        //public String Address;
    //}
    public class LinkedTxn {
        public String TxnId;
        public String TxnType;
    }
    /*public class TxnTaxDetail {
        public Double TotalTax;
        public List<TaxLine> TaxLine;
    }*/
    //end QB Invoice Object classes
    
    
    
      public QuickBooksAccountUpdate2(ApexPages.StandardController stdController) {
        AcID = ApexPages.currentPage().getParameters().get('id');
        clist = new List<Customer>();
           clist2 = new List<Customer>();
          Ac = [select Id, Name,BillingState,BillingCity,BillingStreet,Phone,BillingPostalCode,BillingCountry,Accounting_Phone__c,Accounting_Email__c,LQB_QB_Id__c
                                     from Account 
                                     where Id = :AcID];
          system.debug('Account :'+Ac);
    }
    
    
    
    
    
    
    
    //create invoice in QB
    public PageReference send2() {
        
        system.debug('Account :'+Ac);
        //if customer fields are blank on Account, prompt error msg before creating new customer
        if(Ac.Accounting_Phone__c != null && Ac.BillingStreet != null && Ac.BillingCity != null) {
            
            HttpRequest reqcust = new HttpRequest();
            //Prep customer name (spaces + 's) for query to work
            string cnam = Ac.Name;
            String cname = cnam.replace('&', '%26');
            String cnames = cname.replace(' ', '%20');
            String cnames2 = cnames.replace('\'s', '%5c%27s');  
            String cnames3 = cnames2.replace('é', '%C3%A9'); 
            reqcust.setEndpoint('callout:QuickBooks2/query?query=select%20%2a%20from%20Customer%20Where%20DisplayName%20%3d%20%27'+ cnames3 +'%27');
            reqcust.setHeader('Accept','application/json');
            reqcust.setMethod('GET');
            reqcust.setTimeout(2000); // timeout in milliseconds
            
            Http httpc = new Http();
            HTTPResponse rescust = httpc.send(reqcust);    
            //System.debug(rescust.toString()); 
            
            String custbody = rescust.getBody();
            String custbody_x = custbody.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
            system.debug('EEE: ' + custbody_x);
            
            //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
            if (rescust.getStatusCode() >= 200 && rescust.getStatusCode() < 300) {
                QuickBooksAccountUpdate2.Cust c = (QuickBooksAccountUpdate2.Cust)JSON.deserialize(custbody_x, QuickBooksAccountUpdate2.Cust.class);
                system.debug('Will see: ' + c);
                QueryResponse q = c.QueryResponse;
                system.debug('Will see: ' + q); 
                List<Customer> clist = q.Customer;
                system.debug('Will see: ' + clist); 
                
                //if Customer does not exist in QB, create new Customer in QB
                if(clist == null) {
                    
                    string CustomerEmailAdd;
                    if(Ac.Accounting_Email__c != null ) {
                        CustomerEmailAdd= Ac.Accounting_Email__c;
                    }else{
                        CustomerEmailAdd = '';
                    }
                    
                    string BillingPostalCode;
                    if(Ac.BillingPostalCode != null ) {
                        BillingPostalCode= Ac.BillingPostalCode;
                    }else{
                        BillingPostalCode = '';
                    }
                    
                    string BillingCountry;
                    if(Ac.BillingCountry != null ) {
                        BillingCountry= Ac.BillingCountry;
                    }else{
                        BillingCountry = '';
                    }
                    string BillingState;
                    if(Ac.BillingState != null ) {
                        BillingState= Ac.BillingState;
                    }else{
                        BillingState = '';
                    }
                    
                    
                    
                    JSONGenerator genc = JSON.createGenerator(true); 
                    genc.writeStartObject(); //{
                    genc.writeStringField('DisplayName', Ac.Name); 
                    genc.writeFieldName('PrimaryEmailAddr');
                    genc.writeStartObject(); //{
                    genc.writeStringField('Address', CustomerEmailAdd);  
                    genc.writeEndObject(); //}
                    genc.writeFieldName('PrimaryPhone');
                    genc.writeStartObject(); //{
                    genc.writeStringField('FreeFormNumber', Ac.Accounting_Phone__c); 
                    genc.writeEndObject(); //}
                    genc.writeStringField('CompanyName', Ac.Name); 
                    genc.writeFieldName('BillAddr');
                    genc.writeStartObject(); //{
                    genc.writeStringField('CountrySubDivisionCode', BillingState); 
                    genc.writeStringField('City', Ac.BillingCity); 
                    genc.writeStringField('PostalCode', BillingPostalCode); 
                    genc.writeStringField('Line1', Ac.BillingStreet); 
                    genc.writeStringField('Country', BillingCountry); 
                    genc.writeEndObject(); //}
                    genc.writeEndObject(); //}
                    
                    String qbnewc = genc.getAsString();
                    //Sending the http body with JSON 
                    HttpRequest req = new HttpRequest();
                    req.setEndpoint('callout:QuickBooks2/customer');
                    req.setHeader('Content-Type','application/json');
                    req.setMethod('POST');
                    req.setHeader('Cache-Control', 'no-cache');
                    req.setTimeout(2000); //timeout in milliseconds
                    req.setBody(qbnewc);
                    
                    Http http = new Http();
                    HTTPResponse res = http.send(req);
                    
                    System.debug('Response: ' + res);
                    //getQBCustomer();
                    if (res.getStatusCode() >= 200 && res.getStatusCode() < 300 && Ac.LQB_QB_Id__c == null || Ac.LQB_QB_Id__c == '') {
                        
                        
                        HttpRequest reqcust2 = new HttpRequest();
                        reqcust2.setEndpoint('callout:QuickBooks2/query?query=select%20%2a%20from%20Customer%20Where%20DisplayName%20%3d%20%27'+ cnames2 +'%27');
                        reqcust2.setHeader('Accept','application/json');
                        reqcust2.setMethod('GET');
                        reqcust2.setTimeout(2000); // timeout in milliseconds
                        
                        Http httpc2 = new Http();
                        HTTPResponse rescust2 = httpc2.send(reqcust2);    
                        //System.debug(rescust.toString()); 
                        
                        String custbody2 = rescust2.getBody();
                        String custbody2_x = custbody2.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
                        system.debug('EEE: ' + custbody2_x);
                        
                        //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
                        if (rescust2.getStatusCode() >= 200 && rescust2.getStatusCode() < 300) {
                            QuickBooksAccountUpdate2.Cust c2 = (QuickBooksAccountUpdate2.Cust)JSON.deserialize(custbody2_x, QuickBooksAccountUpdate2.Cust.class);
                            system.debug('Will see: ' + c2);
                            QueryResponse q2 = c2.QueryResponse;
                            system.debug('Will see: ' + q2); 
                            List<Customer> clist2 = q2.Customer;
                            system.debug('Will see: ' + clist2); 
                            if (!Test.isRunningTest()) {
                                for(Customer qbc : clist2) {
                                    
                                    String SyncToken = qbc.SyncToken;
                                    String CustomerID = qbc.Id;
                                    qbcustomerid = qbc.Id;
                                    updateAccountQBCustomerId(Ac.Id, qbcustomerid);
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Customer has been added to QB.'));
                                }
                            }
                        }
                    }
                    else {
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was an Error on Creating the Customer, Please contact the administrator.'));
                                }
                        
                        
                        
                       
                    }
                    
                    else { //customer exists in QB
                        for(Customer qbc : clist) {
                            
                             String SyncToken = qbc.SyncToken;
                            String CustomerID = qbc.Id;
                            system.debug('Customer val: ' + qbc.DisplayName);
                            //check if Customer exists in QB, if it does, grab Customer Id from QB
                            if(qbc.DisplayName == Ac.Name) {
                                
                         string CustomerEmailAdd;
                                        if(Ac.Accounting_Email__c != null ) {
                                            CustomerEmailAdd= Ac.Accounting_Email__c;
                                        }else{
                                            CustomerEmailAdd = '';
                                        }
                        
                        string BillingPostalCode;
                                        if(Ac.BillingPostalCode != null ) {
                                            BillingPostalCode= Ac.BillingPostalCode;
                                        }else{
                                            BillingPostalCode = '';
                                        }
                        
                           string BillingCountry;
                                        if(Ac.BillingCountry != null ) {
                                            BillingCountry= Ac.BillingCountry;
                                        }else{
                                            BillingCountry = '';
                                        }    
                                
                                 string BillingState;
                                        if(Ac.BillingState != null ) {
                                            BillingState= Ac.BillingState;
                                        }else{
                                            BillingState = '';
                                        }      
                        
                                String jsonString;
                                
                                // Create a JSON generator
                                JSONGenerator genc = JSON.createGenerator(true);
                                
                                // Start building the JSON object
                                genc.writeStartObject(); //{
                                genc.writeStringField('domain', 'QBO');
                                genc.writeFieldName('PrimaryEmailAddr');
                                genc.writeStartObject(); //{
                                genc.writeStringField('Address', CustomerEmailAdd);
                                genc.writeEndObject(); //}
                                genc.writeStringField('DisplayName', Ac.Name);
                                genc.writeStringField('FullyQualifiedName', Ac.Name);
                                genc.writeFieldName('PrimaryPhone');
                                genc.writeStartObject(); //{
                                genc.writeStringField('FreeFormNumber', Ac.Accounting_Phone__c);
                                genc.writeEndObject(); //}
                                genc.writeBooleanField('Active', true);
                                genc.writeFieldName('BillAddr');
                                genc.writeStartObject(); //{
                                genc.writeStringField('CountrySubDivisionCode', BillingState); 
                                genc.writeStringField('City', Ac.BillingCity); 
                                genc.writeStringField('PostalCode', BillingPostalCode); 
                                genc.writeStringField('Line1', Ac.BillingStreet); 
                                genc.writeStringField('Country', BillingCountry); 
                                genc.writeEndObject(); //}
                                genc.writeStringField('SyncToken', SyncToken);
                                genc.writeStringField('CompanyName', Ac.Name);
                                genc.writeStringField('Id', CustomerID);
                                genc.writeEndObject(); //}
                                

                                String qbnewc = genc.getAsString();
                                //Sending the http body with JSON 
                                HttpRequest req = new HttpRequest();
                                req.setEndpoint('callout:QuickBooks2/customer');
                                req.setHeader('Content-Type','application/json');
                                req.setMethod('POST');
                                req.setHeader('Cache-Control', 'no-cache');
                                req.setTimeout(2000); //timeout in milliseconds
                                req.setBody(qbnewc);
                                
                                Http http = new Http();
                                HTTPResponse res = http.send(req);
                                
                                System.debug('Response: ' + res);
                                if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
                                    qbcustomerid = qbc.Id;
                                    updateAccountQBCustomerId(Ac.Id, qbCustomerId);
                                    system.debug('great, but no page prompt: ' + qbc.Id);
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Customer has been Updated in QB.'));
                                    // Update the Salesforce record here
                                } else {
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was an Error, Please contact the administrator.'));
                                }
                                
                                
                                //msg = 'Customer is in QB. Please refresh browser, and click button once more to create Invoice in QB.';
                            }
                        }
                    }
                
                }//if query response error, prompt error msg
                else {
                 ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was a query issue in QB. Please contact your Administrator.')); 
                }
            }//customer address/phone is not null
            else {
            ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'Please make sure Customer\'s Phone and Billing Address is filled in under Accounting Information.')); 
            }
        return null;
    }      
    
    
    private static void updateAccountQBCustomerId(Id accountId, String qbCustomerId) {
    if (qbcustomerid != null) {
        Account accToUpdate = new Account(Id = accountId, QBCustomerIDNEW__c = qbCustomerId,LQB_QB_Error__c = 'Success');
        update accToUpdate;
    } else {
        // Handle the case where qbcustomerid is null (optional)
        System.debug('qbcustomerid is null. Update not performed.');
    }
}
    

    public String getName() {
        return 'QuickBooksAccountUpdate2';
    }
}

  • You have an active QuickBooks Online account.

  • Your Salesforce FTM environment is ready (production or sandbox).
  • Each customer in your FTM system has a corresponding Customer QB ID (can be added manually).

Adding manually:

Step 1: Create or Access the QuickBooks App

  1. Go to https://developer.intuit.com

2. Sign in and navigate to your Dashboard.

3. Under the Production Settings section on the left sidebar, click Keys & Credentials.

4. Complete all required fields, including the App Assessment Questionnaire.

Use your company website URL if needed for any required fields.

Important: If the App Assessment Questionnaire is not completed correctly, your app may be rejected by Intuit.

Step 2: Connect QuickBooks with FTM (Sandbox)

  1. Log in to your Salesforce sandbox environment.
  2. In the sandbox, click the QuickBooks button from the Invoice screen to send test data.
  3. Test multiple records to verify everything works properly before deploying to production.
  4. If any additional Line Items need to be sent, ensure each product has a valid Product ID from QuickBooks.

5. Once confirmed, refresh the Invoice page to ensure the QuickBooks ID has been attached.

Step 3: Connect QuickBooks with FTM (Production)

  1. Once testing is complete, set up your production connection:
    • Log in to your QuickBooks Developer account.
    • Navigate to Production Settings > Keys & Credentials.
    • Retrieve your Client ID and Client Secret.
  2. Provide these credentials to the FTM team to enable production deployment.
  3. From your production Salesforce system, click the QuickBooks button on any Invoice to begin sending records.

Step 4: Customer Setup in FTM

Each Account in FTM must have a Customer QB ID to be pushed to QuickBooks.

  • Navigate to the Accounts object in FTM.
  • Locate the Customer QB ID field.
  • Add the matching QuickBooks Customer ID manually, if not already populated.

 Ensure that customer names in FTM exactly match those in QuickBooks to avoid syncing errors.

Step 5: Sending Invoices

When you’re ready to send invoices to QuickBooks:

  1. Open a Load record in FTM.
  2. Click the QuickBooks button to initiate the sync.
  3. The system will:
    • Transfer the Rate as a Line Item.
    • Include additional charges if Line Items and Product IDs are set.
    • Apply Payment Terms (configured in the Customer Account).

  

4. Refresh the Invoice page after sending to confirm the record now includes the QuickBooks ID.

Create and update Carrier on QB:

This page is a QuickBooks Vendor sync tool for carriers, letting users click a button to create/update the carrier as a Vendor in either old or new QB connection, and store the QB Vendor ID back in Salesforce:

<apex:page standardcontroller="FreightTM__Carrier__c" extensions="QuickBooksCarrierCreatorUpdator,QuickBooksCarrierCreatorUpdater2" tabStyle="FreightTM__Carrier__c" lightningStylesheets="true">
    <style>
    .customerError { font-weight: bold; list-style: none; }
    .invoiceError { color: red; font-weight: strong; }
    .bkg-inv { background-color: #eee; }
    .products tr { border: solid 1px #efefef; }
    .container { padding: 0% 5%;}
    .text { padding: 1%;}
    </style>
     
    <script>
    function launchWindow()
    {
        // window.top.location.reload();
       
    };
    function redir() {
        window.location = "/{!Ca.Id}";
    }
    </script>
    
    <apex:pageMessages /><br />
    <apex:form >
       
            <apex:outputPanel id="allPanel">
                    <script>
                       window.onload=function()
                       {
                         doInit();
                       };
                    </script>
                    <div class="container">
                        <h2>Create/Update Carrier on QuickBooks</h2>
                        <div class="bkg-inv">
                            <div class="text">
                                <table width="100%">
                                    <tr>
                                        <td>
                                           <span style="text-align: left; vertical-align: top; font-size: 1.5em; font-weight: bold;">Carrier:&nbsp;<apex:outputField value="{!FreightTM__Carrier__c.Name}"/></span><br /><!-- (Conf. #)-->
                                           
                                        </td>
                                        <td style="text-align: right; padding-right:5%;">
                                            <span></span><br />
                                           
                                        </td>
                                    </tr>
                                </table>
                                <br /><br />
                                <table width="100%">
                                    <tr>
                                        <td width="30%">
                                            <b>Billing Address:</b><br />
                                            <apex:outputField value="{!FreightTM__Carrier__c.FreightTM__Billing_Address__c}"/><br />
                                            <apex:outputPanel ><apex:outputText value="{!FreightTM__Carrier__c.FreightTM__Billing_City__c}"/>, <apex:outputText value=" {!FreightTM__Carrier__c.FreightTM__Billing_State_Province__c}"/> <apex:outputText value=" {!FreightTM__Carrier__c.FreightTM__Billing_Zip_Code__c}"/></apex:outputPanel><br />
                                            <apex:outputField value="{!FreightTM__Carrier__c.FreightTM__Billing_Country__c}"/><br /><br />
                                        </td>
                                        <td width="30%" style="vertical-align: top;"><b>Email:</b><br /><apex:outputText value=" {!FreightTM__Carrier__c.FreightTM__Email__c}"/></td>
                                        <td width="30%" style="vertical-align: top;"><b>Phone:</b><br /><apex:outputText value=" {!FreightTM__Carrier__c.FreightTM__Phone__c}"/></td>
                                        <td width="30%" style="vertical-align: top;"><b>Terms:</b><br /><apex:outputText value=" {!FreightTM__Carrier__c.Payment_Terms__c}"/></td> 
                                    </tr>
                                </table>
                                
                           </div>
                        </div>
                        <br />
                        <apex:commandButton onComplete="redir();" value="Back" />
                        <apex:commandButton value="Create/Update Carrier ** OLD **" action="{!send}" />
                        <apex:commandButton value="Create/Update Carrier ON New QuickBooks" action="{!send2}" />
                        <br/> <br/> <br/> <br/> 
                    </div>
                </apex:outputPanel>
    </apex:form>
</apex:page>

This controller syncs an FTM carrier into QuickBooks as a Vendor (create if missing, update if exists), then stores the QB Vendor ID on the carrier record for future accounting workflows:

public class QuickBooksCarrierCreatorUpdator{
public string Termss;
    public FreightTM__Carrier__c Ca {get; set;}
    public id CaID {get; set;}
    //public Account acct {get; set;}
    //public Boolean cid {get; set;}
    public string qbCarrierid;
    public string msg;
    public integer taxcodeid;
    //public id loadid {get; set;} //for unit test
    public List<Vendor> clist;
    public List<Vendor> clist2;
    public string cnam;//for unit test
    public string cname;//for unit test
    public string cnames;//for unit test
    public string cnames2;//for unit test
    public string invbody3;//for unit test
    public string invbody_x;//for unit test
    
    //Apex classes from QB Customer Object
    public class BillAddr {
        public String Id;
        public String Line1;
        public String City;
        public string Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        //public String Lat; 
        //public String Long_Z; //in json: Long
    }
    public class CurrencyRef {
        public String value;
        public String name;
    }
    public class Cust { //this needs to be in a class for json deserialization to work
        public QueryResponse QueryResponse;
        public String time_x; //time is an apex reserved word, replaced it with time_x in the response string
    }
    public class PrimaryPhone {
        public String FreeFormNumber;
    }
    public class Customer {
        public Boolean Taxable;
        public BillAddr BillAddr;
        //public BillAddr ShipAddr; 
        public Boolean Job;
        public Boolean BillWithParent;
        public Double Balance;
        public Double BalanceWithJobs;
        public CurrencyRef CurrencyRef;
        public String PreferredDeliveryMethod;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData MetaData;
        public String GivenName;
        public String FamilyName;
        public String FullyQualifiedName;
        public String CompanyName;
        public String DisplayName;
        public String PrintOnCheckName;
        public Boolean Active;
        public PrimaryPhone PrimaryPhone;
        //public PrimaryEmailAddr PrimaryEmailAddr;
    }
    public class Vendor {
        public Boolean Taxable;
        public BillAddr BillAddr;
        //public BillAddr ShipAddr; 
        public Boolean Job;
        public Boolean BillWithParent;
        public Double Balance;
        public Double BalanceWithJobs;
        public CurrencyRef CurrencyRef;
        public String PreferredDeliveryMethod;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData MetaData;
        public String GivenName;
        public String FamilyName;
        public String FullyQualifiedName;
        public String CompanyName;
        public String DisplayName;
        public String PrintOnCheckName;
        public Boolean Active;
        public PrimaryPhone PrimaryPhone;
        //public PrimaryEmailAddr PrimaryEmailAddr;
    }
    
    public class MetaData {
        public String CreateTime;
        public String LastUpdatedTime;
    }
    public class QueryResponse {
        public List<Vendor> Vendor; 
        public Integer startPosition;
        public Integer maxResults;
    }
    //public class PrimaryEmailAddr {
        //public String Address;
    //}
    //end QB Customer Object classes
    
    //Apex classes from QB Invoice Object
    public class BillAddr_i { //rename to avoid duplicate class
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        public String Lat;
        public String Long_Z; // in json: Long
    }
    public class CurrencyRef_i {
        public String value;
        public String name;
    }
    public class Invoice {
        public Integer Deposit;
        public Boolean AllowIPNPayment;
        public Boolean AllowOnlinePayment;
        public Boolean AllowOnlineCreditCardPayment;
        public Boolean AllowOnlineACHPayment;
        public String EInvoiceStatus;
        public String ECloudStatusTimeStamp;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData_i MetaData_i;
        public List<CustomField> CustomField;
        public String DocNumber;
        public String TxnDate;
        public CurrencyRef_i CurrencyRef_i;
        //public Integer ExchangeRate;
        public List<LinkedTxn> LinkedTxn;
        public List<Line> Line;
        //public TxnTaxDetail TxnTaxDetail; //
        public CurrencyRef CustomerRef;
        public TaxCodeRef CustomerMemo; //
        public BillAddr_i BillAddr_i;
        //public BillAddr ShipAddr;
        public TaxCodeRef SalesTermRef; //
        public String DueDate;
        //public String GlobalTaxCalculation; //
        public Double TotalAmt;
        public Double HomeTotalAmt;
        public Boolean ApplyTaxAfterDiscount; //USA
        public String PrintStatus;
        public String EmailStatus;
        public BillEmail BillEmail;
        public Double Balance; //use Double instead of Integer, otherwise errors with decimals
        //public Double HomeBalance;
        public DeliveryInfo DeliveryInfo;
        public BillAddr_Z ShipAddr;
    }
    public class BillEmail {
        public String Address;
    }
    public class DeliveryInfo {
        public String DeliveryType;
        public String DeliveryTime;
    }
    public class BillAddr_Z {
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String CountrySubDivisionCode;
        public String PostalCode;
    }
    public class SalesItemLineDetail {
        public CurrencyRef ItemRef;
        public Double UnitPrice; //use Double instead of Integer, otherwise errors with decimals
        public Integer Qty; //
        public CurrencyRef ItemAccountRef;
        public TaxCodeRef TaxCodeRef; //
    }
    public class Line_Z {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }

    public class Line_Y {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }

    public class Line_X {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Line_W {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Inv {
        public QueryResponse_i QueryResponse_i;
        public String time_i;
    }
    /*public class TaxLine { /////
        public Double Amount;
        public String DetailType;
        //public TaxLineDetail TaxLineDetail;
    }*/
    public class Line {
        public String Id;
        public Integer LineNum;
        //public String Description;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public LinkedTxn SubTotalLineDetail;
    }
    public class TaxCodeRef { //
        public String value;
    }
    public class MetaData_i {
        public String CreateTime;
        public String LastUpdatedTime;
    }
   /*public class TaxLineDetail {
        public TaxCodeRef TaxRateRef;
        public Boolean PercentBased;
        public Double TaxPercent; //Changed from Integer to Double, otherwise errors with decimals
        public Double NetAmountTaxable;
    }*/
    public class QueryResponse_i { //rename to avoid duplicate class
        public List<Invoice> Invoice;
        public Integer startPosition;
        public Integer maxResults;
        public Integer totalCount;
    }
    public class CustomField {
        public String DefinitionId;
        public String Type_Z; // in json: Type
        public String Name;
        public String StringValue;
    }
    //public class BillEmail {
        //public String Address;
    //}
    public class LinkedTxn {
        public String TxnId;
        public String TxnType;
    }
    /*public class TxnTaxDetail {
        public Double TotalTax;
        public List<TaxLine> TaxLine;
    }*/
    //end QB Invoice Object classes
    
    
    
    

    
    
    
    
    
    
    
    public QuickBooksCarrierCreatorUpdator(ApexPages.StandardController stdController) {
        CaID = ApexPages.currentPage().getParameters().get('id');
        clist = new List<Vendor>();
        clist2 = new List<Vendor>();
        Ca = [select Id, Name,ABA__c,About__c,Account__c,ACH__c,FreightTM__After_Hours_Phone__c,Bank__c,Bank_info_link__c,FreightTM__Billing_City__c,
              FreightTM__Billing_Country__c,FreightTM__Billing_State_Province__c,FreightTM__Billing_Address__c,FreightTM__Billing_Zip_Code__c,
              FreightTM__Cargo_Insurance_Amount__c,FreightTM__Cargo_Insurance_Expiration__c,Carrier_Package__c,Chassis__c,Chassis_Split__c,Check__c,
              FreightTM__City__c,Contact__c,    Contact_Email_1__c,Contact_Email_2__c,Contact_Name_1__c,Contact_Name_2__c,Contact_Phone_1__c,
              Contact_Phone_2__c,FreightTM__Country__c,Delinquent_Carrier__c,Detention__c,Drayage__c,FreightTM__Email__c,Factor__c,Factor_NOA__c,Payment_Terms__c,Payment_Terms_QB_ID__c,
              FreightTM__Fax__c,FreightTM__Federal_ID__c,FSC__c,FTL__c,FreightTM__Hazmat_Permit__c,Lane_Zones_from__c,Lane_Zones_To__c,
              FreightTM__Liability_Insurance_Amount__c,FreightTM__MC_MX_FF_Number__c,FreightTM__Phone__c,FreightTM__Primary_Contact__c,LQB_QB_Company__c,LQB_QB_Id__c,LQB_QB_No__c,LQB_QB_Seq__c,LQB_QB_Error__c,
              FreightTM__State_Province__c,State__c,FreightTM__Address__c,FreightTM__USDOT_Number__c,FreightTM__Zip_Code__c
              from FreightTM__Carrier__c where Id = :CaID];
        system.debug('Carrier :'+Ca);
    }
    
    
    
    
    //create invoice in QB
    public PageReference send() {
        
        system.debug('Carrier :'+Ca);
        //if customer fields are blank on Account, prompt error msg before creating new customer
        if(Ca.FreightTM__Phone__c != null && Ca.FreightTM__Billing_Address__c != null && Ca.FreightTM__Billing_Zip_Code__c != null) {
            
            HttpRequest reqcust = new HttpRequest();
            //Prep customer name (spaces + 's) for query to work
            string cnam = Ca.Name;
            String cname = cnam.replace('&', '%26');
            String cnames = cname.replace(' ', '%20');
            String cnames2 = cnames.replace('\'s', '%5c%27s');      
            reqcust.setEndpoint('callout:QuickBooks/query?query=select%20%2a%20from%20Vendor%20Where%20DisplayName%20%3d%20%27'+ cnames2 +'%27');
            reqcust.setHeader('Accept','application/json');
            reqcust.setMethod('GET');
            reqcust.setTimeout(2000); // timeout in milliseconds
            
            Http httpc = new Http();
            HTTPResponse rescust = httpc.send(reqcust);    
            //System.debug(rescust.toString()); 
            
            String custbody = rescust.getBody();
            String custbody_x = custbody.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
            system.debug('EEE: ' + custbody_x);
            
            //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
            if (rescust.getStatusCode() >= 200 && rescust.getStatusCode() < 300) {
                QuickBooksCarrierCreatorUpdator.Cust c = (QuickBooksCarrierCreatorUpdator.Cust)JSON.deserialize(custbody_x, QuickBooksCarrierCreatorUpdator.Cust.class);
                system.debug('Will see: ' + c);
                QueryResponse q = c.QueryResponse;
                system.debug('Will see: ' + q); 
                List<Vendor> clist = q.Vendor;
                system.debug('Will see: ' + clist); 
                
                //if Customer does not exist in QB, create new Customer in QB
                if(clist == null) {
                    
                    string CarrierEmailAdd;
                    if(Ca.FreightTM__Email__c != null ) {
                        CarrierEmailAdd= Ca.FreightTM__Email__c;
                    }else{
                        CarrierEmailAdd = '';
                    }
                    
                    string BillingPostalCode;
                    if(Ca.FreightTM__Billing_Zip_Code__c != null ) {
                        BillingPostalCode= Ca.FreightTM__Billing_Zip_Code__c;
                    }else{
                        BillingPostalCode = '';
                    }
                    
                    string BillingCountry;
                    if(Ca.FreightTM__Billing_Country__c != null ) {
                        BillingCountry= Ca.FreightTM__Billing_Country__c;
                    }else{
                        BillingCountry = '';
                    }
                    string BillingState;
                    if(Ca.FreightTM__Billing_State_Province__c != null ) {
                        BillingState= Ca.FreightTM__Billing_State_Province__c;
                    }else{
                        BillingState = '';
                    }
                    
                     String PaymentTermsID;
                            if(Ca.Payment_Terms_QB_ID__c != null) {
                                PaymentTermsID = Ca.Payment_Terms_QB_ID__c;
                            } else {
                                PaymentTermsID = '1'; // Default value if none of the conditions match
                            }
                    
                    
                    JSONGenerator genc = JSON.createGenerator(true);
                    genc.writeStartObject();
                    genc.writeStringField('DisplayName', Ca.Name);
                    
                    genc.writeFieldName('PrimaryEmailAddr');
                    genc.writeStartObject();
                    genc.writeStringField('Address', CarrierEmailAdd);
                    genc.writeEndObject();
                    
                    genc.writeFieldName('TermRef');
                    genc.writeStartObject();
                    genc.writeStringField('value', PaymentTermsID);
                    genc.writeEndObject();
                    
                    genc.writeFieldName('PrimaryPhone');
                    genc.writeStartObject();
                    genc.writeStringField('FreeFormNumber', Ca.FreightTM__Phone__c);
                    genc.writeEndObject();
                    
                    genc.writeFieldName('BillAddr');
                    genc.writeStartObject();
                    genc.writeStringField('Line1', Ca.FreightTM__Billing_Address__c);
                    genc.writeStringField('City', Ca.FreightTM__Billing_City__c);
                    genc.writeStringField('CountrySubDivisionCode', BillingState);
                    genc.writeStringField('PostalCode', BillingPostalCode);
                    genc.writeStringField('Country', BillingCountry);
                    genc.writeEndObject();
                    
                    genc.writeEndObject();
                    
                    
                    String qbnewc = genc.getAsString();
                    //Sending the http body with JSON 
                    HttpRequest req = new HttpRequest();
                    req.setEndpoint('callout:QuickBooks/vendor');
                    req.setHeader('Content-Type','application/json');
                    req.setMethod('POST');
                    req.setHeader('Cache-Control', 'no-cache');
                    req.setTimeout(2000); //timeout in milliseconds
                    req.setBody(qbnewc);
                    
                    Http http = new Http();
                    HTTPResponse res = http.send(req);
                    
                    System.debug('Response: ' + res);
                    
                    
                    //getQBCustomer();
                    if (res.getStatusCode() >= 200 && res.getStatusCode() < 300 && Ca.LQB_QB_Id__c == null || Ca.LQB_QB_Id__c == '') {
                        ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Carrier has been added to QB.'));
                        
                        HttpRequest reqcust2 = new HttpRequest();
                        reqcust2.setEndpoint('callout:QuickBooks/query?query=select%20%2a%20from%20Vendor%20Where%20DisplayName%20%3d%20%27'+ cnames2 +'%27');
                        reqcust2.setHeader('Accept','application/json');
                        reqcust2.setMethod('GET');
                        reqcust2.setTimeout(2000); // timeout in milliseconds
                        
                        Http httpc2 = new Http();
                        HTTPResponse rescust2 = httpc2.send(reqcust2);    
                        //System.debug(rescust.toString()); 
                        
                        String custbody2 = rescust2.getBody();
                        String custbody2_x = custbody2.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
                        system.debug('EEE: ' + custbody2_x);
                        
                        //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
                        if (rescust2.getStatusCode() >= 200 && rescust2.getStatusCode() < 300) {
                            QuickBooksCarrierCreatorUpdator.Cust c2 = (QuickBooksCarrierCreatorUpdator.Cust)JSON.deserialize(custbody2_x, QuickBooksCarrierCreatorUpdator.Cust.class);
                            system.debug('Will see: ' + c2);
                            QueryResponse q2 = c2.QueryResponse;
                            system.debug('Will see: ' + q2); 
                            List<Vendor> clist2 = q2.Vendor;
                            system.debug('Will see: ' + clist2); 
                            if (!Test.isRunningTest()) {
                                for(Vendor qbc : clist2) {
                                    
                                    String SyncToken = qbc.SyncToken;
                                    String CustomerID = qbc.Id;
                                    qbCarrierid = qbc.Id;
                                    updateCarrierQBCarrierId(Ca.Id, qbCarrierid);
                                    system.debug('great, but no page prompt: ' + qbc.Id);
                                }
                            }
                        }
                        
                    }else{
                        ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.Error, 'There was a query issue and Updating the Carrier Record with QBID. Please contact your Administrator.'));
                    }
                    
                }
                
                else { //customer exists in QB
                    for(Vendor qbc : clist) {
                        
                        String SyncToken = qbc.SyncToken;
                        String CustomerID = qbc.Id;
                        system.debug('Customer val: ' + qbc.DisplayName);
                        system.debug('Customer val: ' + qbc.SyncToken);
                        system.debug('Customer val: ' + qbc.id);
                        //check if Customer exists in QB, if it does, grab Customer Id from QB
                        if(qbc.DisplayName == Ca.Name) {
                            
                            string CarrierEmailAdd;
                            if(Ca.FreightTM__Email__c != null ) {
                                CarrierEmailAdd= Ca.FreightTM__Email__c;
                            }else{
                                CarrierEmailAdd = '';
                            }
                            
                            string BillingPostalCode;
                            if(Ca.FreightTM__Billing_Zip_Code__c != null ) {
                                BillingPostalCode= Ca.FreightTM__Billing_Zip_Code__c;
                            }else{
                                BillingPostalCode = '';
                            }
                            
                            string BillingCountry;
                            if(Ca.FreightTM__Billing_Country__c != null ) {
                                BillingCountry= Ca.FreightTM__Billing_Country__c;
                            }else{
                                BillingCountry = '';
                            }
                            string BillingState;
                            if(Ca.FreightTM__Billing_State_Province__c != null ) {
                                BillingState= Ca.FreightTM__Billing_State_Province__c;
                            }else{
                                BillingState = '';
                            }
                            
                            
                                                  String PaymentTermsID;
                            if(ca.Payment_Terms_QB_ID__c != null) {
                                PaymentTermsID = ca.Payment_Terms_QB_ID__c;
                            } else {
                                PaymentTermsID = '1'; // Default value if none of the conditions match
                            }
                    
                            
                            
                            String jsonString;
                            
                            // Create a JSON generator
                            JSONGenerator genc = JSON.createGenerator(true);
                            
                            genc.writeStartObject(); //{
                            genc.writeStringField('domain', 'QBO');
                            genc.writeFieldName('PrimaryEmailAddr');
                            genc.writeStartObject(); //{
                            genc.writeStringField('Address', CarrierEmailAdd);
                            genc.writeEndObject(); //}
                            genc.writeFieldName('PrimaryPhone');
                            genc.writeStartObject(); //{
                            genc.writeStringField('FreeFormNumber', Ca.FreightTM__Phone__c);
                            genc.writeEndObject(); //}
                            genc.writeBooleanField('Active', true);
                            genc.writeFieldName('BillAddr');
                            genc.writeStartObject();
                            genc.writeStringField('Line1', Ca.FreightTM__Billing_Address__c);
                            genc.writeStringField('City', Ca.FreightTM__Billing_City__c);
                            genc.writeStringField('CountrySubDivisionCode', BillingState);
                            genc.writeStringField('PostalCode', BillingPostalCode);
                            genc.writeStringField('Country', BillingCountry);
                            genc.writeEndObject();
                            genc.writeFieldName('TermRef');
                        genc.writeStartObject();
                        genc.writeStringField('value', PaymentTermsID);
                        genc.writeEndObject();
                            
                            
                            genc.writeStringField('DisplayName', Ca.Name);
                            genc.writeStringField('SyncToken', SyncToken);
                            genc.writeStringField('Id', CustomerID);
                            genc.writeEndObject(); //}
                            
                            
                            String qbnewc = genc.getAsString();
                            //Sending the http body with JSON 
                            HttpRequest req = new HttpRequest();
                            req.setEndpoint('callout:QuickBooks/vendor');
                            req.setHeader('Content-Type','application/json');
                            req.setMethod('POST');
                            req.setHeader('Cache-Control', 'no-cache');
                            req.setTimeout(2000); //timeout in milliseconds
                            req.setBody(qbnewc);
                            
                            Http http = new Http();
                            HTTPResponse res = http.send(req);
                            
                            System.debug('Response: ' + res);
                            if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
                                qbCarrierid = qbc.Id;
                                updateCarrierQBCarrierId(Ca.Id, qbCarrierid);
                                system.debug('great: ' + qbc.Id);
                                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Carrier has been Updated in QB.'));
                                // Update the Salesforce record here
                            } else {
                                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was an Error on Updating the Carrier Record with The QBID, Please contact the administrator.'));
                            }
                            
                            
                            //msg = 'Customer is in QB. Please refresh browser, and click button once more to create Invoice in QB.';
                        }
                    }
                }
                
            }//if query response error, prompt error msg
            else {
                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was a query issue for getting the Carrier Info to update or create. Please contact your Administrator.')); 
            }
        }//customer address/phone is not null
        else {
            ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'Please make sure Carrier\'s Phone and Billing Address is filled in under Address Information.')); 
        }
        
        return null;
        
    }      
    
   
    private static void updateCarrierQBCarrierId(Id accountId, String qbCarrierid) {
    if (qbCarrierid != null) {
        FreightTM__Carrier__c CarrToUpdate = new FreightTM__Carrier__c(Id = accountId, LQB_QB_Id__c = qbCarrierid,LQB_QB_Error__c = 'Success');
        update CarrToUpdate;
    } else {
        // Handle the case where qbcustomerid is null (optional)
        System.debug('qbCarrierid is null. Update not performed.');
    }
}
    

    public String getName() {
        return 'QuickBooksCarrierCreatorUpdator';
    }
}

Creates/updates a QuickBooks Vendor for an FTM Carrier using the QuickBooks2 Named Credential, then stores the QB Vendor Id:

public class QuickBooksCarrierCreatorUpdater2{
public string Termss;
    public FreightTM__Carrier__c Ca {get; set;}
    public id CaID {get; set;}
    //public Account acct {get; set;}
    //public Boolean cid {get; set;}
    public string qbCarrierid;
    public string msg;
    public integer taxcodeid;
    //public id loadid {get; set;} //for unit test
    public List<Vendor> clist;
    public List<Vendor> clist2;
    public string cnam;//for unit test
    public string cname;//for unit test
    public string cnames;//for unit test
    public string cnames2;//for unit test
    public string invbody3;//for unit test
    public string invbody_x;//for unit test
    
    //Apex classes from QB Customer Object
    public class BillAddr {
        public String Id;
        public String Line1;
        public String City;
        public string Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        //public String Lat; 
        //public String Long_Z; //in json: Long
    }
    public class CurrencyRef {
        public String value;
        public String name;
    }
    public class Cust { //this needs to be in a class for json deserialization to work
        public QueryResponse QueryResponse;
        public String time_x; //time is an apex reserved word, replaced it with time_x in the response string
    }
    public class PrimaryPhone {
        public String FreeFormNumber;
    }
    public class Customer {
        public Boolean Taxable;
        public BillAddr BillAddr;
        //public BillAddr ShipAddr; 
        public Boolean Job;
        public Boolean BillWithParent;
        public Double Balance;
        public Double BalanceWithJobs;
        public CurrencyRef CurrencyRef;
        public String PreferredDeliveryMethod;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData MetaData;
        public String GivenName;
        public String FamilyName;
        public String FullyQualifiedName;
        public String CompanyName;
        public String DisplayName;
        public String PrintOnCheckName;
        public Boolean Active;
        public PrimaryPhone PrimaryPhone;
        //public PrimaryEmailAddr PrimaryEmailAddr;
    }
    public class Vendor {
        public Boolean Taxable;
        public BillAddr BillAddr;
        //public BillAddr ShipAddr; 
        public Boolean Job;
        public Boolean BillWithParent;
        public Double Balance;
        public Double BalanceWithJobs;
        public CurrencyRef CurrencyRef;
        public String PreferredDeliveryMethod;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData MetaData;
        public String GivenName;
        public String FamilyName;
        public String FullyQualifiedName;
        public String CompanyName;
        public String DisplayName;
        public String PrintOnCheckName;
        public Boolean Active;
        public PrimaryPhone PrimaryPhone;
        //public PrimaryEmailAddr PrimaryEmailAddr;
    }
    
    public class MetaData {
        public String CreateTime;
        public String LastUpdatedTime;
    }
    public class QueryResponse {
        public List<Vendor> Vendor; 
        public Integer startPosition;
        public Integer maxResults;
    }
    //public class PrimaryEmailAddr {
        //public String Address;
    //}
    //end QB Customer Object classes
    
    //Apex classes from QB Invoice Object
    public class BillAddr_i { //rename to avoid duplicate class
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        public String Lat;
        public String Long_Z; // in json: Long
    }
    public class CurrencyRef_i {
        public String value;
        public String name;
    }
    public class Invoice {
        public Integer Deposit;
        public Boolean AllowIPNPayment;
        public Boolean AllowOnlinePayment;
        public Boolean AllowOnlineCreditCardPayment;
        public Boolean AllowOnlineACHPayment;
        public String EInvoiceStatus;
        public String ECloudStatusTimeStamp;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData_i MetaData_i;
        public List<CustomField> CustomField;
        public String DocNumber;
        public String TxnDate;
        public CurrencyRef_i CurrencyRef_i;
        //public Integer ExchangeRate;
        public List<LinkedTxn> LinkedTxn;
        public List<Line> Line;
        //public TxnTaxDetail TxnTaxDetail; //
        public CurrencyRef CustomerRef;
        public TaxCodeRef CustomerMemo; //
        public BillAddr_i BillAddr_i;
        //public BillAddr ShipAddr;
        public TaxCodeRef SalesTermRef; //
        public String DueDate;
        //public String GlobalTaxCalculation; //
        public Double TotalAmt;
        public Double HomeTotalAmt;
        public Boolean ApplyTaxAfterDiscount; //USA
        public String PrintStatus;
        public String EmailStatus;
        public BillEmail BillEmail;
        public Double Balance; //use Double instead of Integer, otherwise errors with decimals
        //public Double HomeBalance;
        public DeliveryInfo DeliveryInfo;
        public BillAddr_Z ShipAddr;
    }
    public class BillEmail {
        public String Address;
    }
    public class DeliveryInfo {
        public String DeliveryType;
        public String DeliveryTime;
    }
    public class BillAddr_Z {
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String CountrySubDivisionCode;
        public String PostalCode;
    }
    public class SalesItemLineDetail {
        public CurrencyRef ItemRef;
        public Double UnitPrice; //use Double instead of Integer, otherwise errors with decimals
        public Integer Qty; //
        public CurrencyRef ItemAccountRef;
        public TaxCodeRef TaxCodeRef; //
    }
    public class Line_Z {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }

    public class Line_Y {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }

    public class Line_X {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Line_W {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Inv {
        public QueryResponse_i QueryResponse_i;
        public String time_i;
    }
    /*public class TaxLine { /////
        public Double Amount;
        public String DetailType;
        //public TaxLineDetail TaxLineDetail;
    }*/
    public class Line {
        public String Id;
        public Integer LineNum;
        //public String Description;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public LinkedTxn SubTotalLineDetail;
    }
    public class TaxCodeRef { //
        public String value;
    }
    public class MetaData_i {
        public String CreateTime;
        public String LastUpdatedTime;
    }
   /*public class TaxLineDetail {
        public TaxCodeRef TaxRateRef;
        public Boolean PercentBased;
        public Double TaxPercent; //Changed from Integer to Double, otherwise errors with decimals
        public Double NetAmountTaxable;
    }*/
    public class QueryResponse_i { //rename to avoid duplicate class
        public List<Invoice> Invoice;
        public Integer startPosition;
        public Integer maxResults;
        public Integer totalCount;
    }
    public class CustomField {
        public String DefinitionId;
        public String Type_Z; // in json: Type
        public String Name;
        public String StringValue;
    }
    //public class BillEmail {
        //public String Address;
    //}
    public class LinkedTxn {
        public String TxnId;
        public String TxnType;
    }
    /*public class TxnTaxDetail {
        public Double TotalTax;
        public List<TaxLine> TaxLine;
    }*/
    //end QB Invoice Object classes
    
    
    
    

    
    
    
    
    
    
    
    public QuickBooksCarrierCreatorUpdater2(ApexPages.StandardController stdController) {
        CaID = ApexPages.currentPage().getParameters().get('id');
        clist = new List<Vendor>();
        clist2 = new List<Vendor>();
        Ca = [select Id, Name,ABA__c,About__c,Account__c,ACH__c,FreightTM__After_Hours_Phone__c,Bank__c,Bank_info_link__c,FreightTM__Billing_City__c,
              FreightTM__Billing_Country__c,FreightTM__Billing_State_Province__c,FreightTM__Billing_Address__c,FreightTM__Billing_Zip_Code__c,
              FreightTM__Cargo_Insurance_Amount__c,FreightTM__Cargo_Insurance_Expiration__c,Carrier_Package__c,Chassis__c,Chassis_Split__c,Check__c,
              FreightTM__City__c,Contact__c,    Contact_Email_1__c,Contact_Email_2__c,Contact_Name_1__c,Contact_Name_2__c,Contact_Phone_1__c,
              Contact_Phone_2__c,FreightTM__Country__c,Delinquent_Carrier__c,Detention__c,Drayage__c,FreightTM__Email__c,Factor__c,Factor_NOA__c,Payment_Terms__c,Payment_Terms_QB_ID__c,
              FreightTM__Fax__c,FreightTM__Federal_ID__c,FSC__c,FTL__c,FreightTM__Hazmat_Permit__c,Lane_Zones_from__c,Lane_Zones_To__c,
              FreightTM__Liability_Insurance_Amount__c,FreightTM__MC_MX_FF_Number__c,FreightTM__Phone__c,FreightTM__Primary_Contact__c,LQB_QB_Company__c,LQB_QB_Id__c,LQB_QB_Id2__c,LQB_QB_No__c,LQB_QB_Seq__c,LQB_QB_Error__c,
              FreightTM__State_Province__c,State__c,FreightTM__Address__c,FreightTM__USDOT_Number__c,FreightTM__Zip_Code__c
              from FreightTM__Carrier__c where Id = :CaID];
        system.debug('Carrier :'+Ca);
    }
    
    
    
    
    //create invoice in QB
    public PageReference send2() {
        
        system.debug('Carrier :'+Ca);
        //if customer fields are blank on Account, prompt error msg before creating new customer
        if(Ca.FreightTM__Phone__c != null && Ca.FreightTM__Billing_Address__c != null && Ca.FreightTM__Billing_Zip_Code__c != null) {
            
            HttpRequest reqcust = new HttpRequest();
            //Prep customer name (spaces + 's) for query to work
            string cnam = Ca.Name;
            String cname = cnam.replace('&', '%26');
            String cnames = cname.replace(' ', '%20');
            String cnames2 = cnames.replace('\'s', '%5c%27s');      
            reqcust.setEndpoint('callout:QuickBooks2/query?query=select%20%2a%20from%20Vendor%20Where%20DisplayName%20%3d%20%27'+ cnames2 +'%27');
            reqcust.setHeader('Accept','application/json');
            reqcust.setMethod('GET');
            reqcust.setTimeout(2000); // timeout in milliseconds
            
            Http httpc = new Http();
            HTTPResponse rescust = httpc.send(reqcust);    
            //System.debug(rescust.toString()); 
            
            String custbody = rescust.getBody();
            String custbody_x = custbody.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
            system.debug('EEE: ' + custbody_x);
            
            //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
            if (rescust.getStatusCode() >= 200 && rescust.getStatusCode() < 300) {
                QuickBooksCarrierCreatorUpdater2.Cust c = (QuickBooksCarrierCreatorUpdater2.Cust)JSON.deserialize(custbody_x, QuickBooksCarrierCreatorUpdater2.Cust.class);
                system.debug('Will see: ' + c);
                QueryResponse q = c.QueryResponse;
                system.debug('Will see: ' + q); 
                List<Vendor> clist = q.Vendor;
                system.debug('Will see: ' + clist); 
                
                //if Customer does not exist in QB, create new Customer in QB
                if(clist == null) {
                    
                    string CarrierEmailAdd;
                    if(Ca.FreightTM__Email__c != null ) {
                        CarrierEmailAdd= Ca.FreightTM__Email__c;
                    }else{
                        CarrierEmailAdd = '';
                    }
                    
                    string BillingPostalCode;
                    if(Ca.FreightTM__Billing_Zip_Code__c != null ) {
                        BillingPostalCode= Ca.FreightTM__Billing_Zip_Code__c;
                    }else{
                        BillingPostalCode = '';
                    }
                    
                    string BillingCountry;
                    if(Ca.FreightTM__Billing_Country__c != null ) {
                        BillingCountry= Ca.FreightTM__Billing_Country__c;
                    }else{
                        BillingCountry = '';
                    }
                    string BillingState;
                    if(Ca.FreightTM__Billing_State_Province__c != null ) {
                        BillingState= Ca.FreightTM__Billing_State_Province__c;
                    }else{
                        BillingState = '';
                    }
                    
                     String PaymentTermsID;
                            if(Ca.Payment_Terms_QB_ID__c != null) {
                                PaymentTermsID = Ca.Payment_Terms_QB_ID__c;
                            } else {
                                PaymentTermsID = '1'; // Default value if none of the conditions match
                            }
                    
                    
                    JSONGenerator genc = JSON.createGenerator(true);
                    genc.writeStartObject();
                    genc.writeStringField('DisplayName', Ca.Name);
                    
                    genc.writeFieldName('PrimaryEmailAddr');
                    genc.writeStartObject();
                    genc.writeStringField('Address', CarrierEmailAdd);
                    genc.writeEndObject();
                    
                    genc.writeFieldName('TermRef');
                    genc.writeStartObject();
                    genc.writeStringField('value', PaymentTermsID);
                    genc.writeEndObject();
                    
                    genc.writeFieldName('PrimaryPhone');
                    genc.writeStartObject();
                    genc.writeStringField('FreeFormNumber', Ca.FreightTM__Phone__c);
                    genc.writeEndObject();
                    
                    genc.writeFieldName('BillAddr');
                    genc.writeStartObject();
                    genc.writeStringField('Line1', Ca.FreightTM__Billing_Address__c);
                    genc.writeStringField('City', Ca.FreightTM__Billing_City__c);
                    genc.writeStringField('CountrySubDivisionCode', BillingState);
                    genc.writeStringField('PostalCode', BillingPostalCode);
                    genc.writeStringField('Country', BillingCountry);
                    genc.writeEndObject();
                    
                    genc.writeEndObject();
                    
                    
                    String qbnewc = genc.getAsString();
                    //Sending the http body with JSON 
                    HttpRequest req = new HttpRequest();
                    req.setEndpoint('callout:QuickBooks2/vendor');
                    req.setHeader('Content-Type','application/json');
                    req.setMethod('POST');
                    req.setHeader('Cache-Control', 'no-cache');
                    req.setTimeout(2000); //timeout in milliseconds
                    req.setBody(qbnewc);
                    
                    Http http = new Http();
                    HTTPResponse res = http.send(req);
                    
                    System.debug('Response: ' + res);
                    
                    
                    //getQBCustomer();
                    if (res.getStatusCode() >= 200 && res.getStatusCode() < 300 && Ca.LQB_QB_Id2__c == null || Ca.LQB_QB_Id2__c == '') {
                        ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Carrier has been added to QB.'));
                        
                        HttpRequest reqcust2 = new HttpRequest();
                        reqcust2.setEndpoint('callout:QuickBooks2/query?query=select%20%2a%20from%20Vendor%20Where%20DisplayName%20%3d%20%27'+ cnames2 +'%27');
                        reqcust2.setHeader('Accept','application/json');
                        reqcust2.setMethod('GET');
                        reqcust2.setTimeout(2000); // timeout in milliseconds
                        
                        Http httpc2 = new Http();
                        HTTPResponse rescust2 = httpc2.send(reqcust2);    
                        //System.debug(rescust.toString()); 
                        
                        String custbody2 = rescust2.getBody();
                        String custbody2_x = custbody2.replace('"time":', '"time_x":'); //time is an apex reserved word, replaced it with time_x
                        system.debug('EEE: ' + custbody2_x);
                        
                        //If no errors in query response, deserialize json string response and assign variables to values. Return QB Customer ID
                        if (rescust2.getStatusCode() >= 200 && rescust2.getStatusCode() < 300) {
                            QuickBooksCarrierCreatorUpdater2.Cust c2 = (QuickBooksCarrierCreatorUpdater2.Cust)JSON.deserialize(custbody2_x, QuickBooksCarrierCreatorUpdater2.Cust.class);
                            system.debug('Will see: ' + c2);
                            QueryResponse q2 = c2.QueryResponse;
                            system.debug('Will see: ' + q2); 
                            List<Vendor> clist2 = q2.Vendor;
                            system.debug('Will see: ' + clist2); 
                            if (!Test.isRunningTest()) {
                                for(Vendor qbc : clist2) {
                                    
                                    String SyncToken = qbc.SyncToken;
                                    String CustomerID = qbc.Id;
                                    qbCarrierid = qbc.Id;
                                    updateCarrierQBCarrierId(Ca.Id, qbCarrierid);
                                    system.debug('great, but no page prompt: ' + qbc.Id);
                                }
                            }
                        }
                        
                    }else{
                        ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.Error, 'There was a query issue and Updating the Carrier Record with QBID. Please contact your Administrator.'));
                    }
                    
                }
                
                else { //customer exists in QB
                    for(Vendor qbc : clist) {
                        
                        String SyncToken = qbc.SyncToken;
                        String CustomerID = qbc.Id;
                        system.debug('Customer val: ' + qbc.DisplayName);
                        system.debug('Customer val: ' + qbc.SyncToken);
                        system.debug('Customer val: ' + qbc.id);
                        //check if Customer exists in QB, if it does, grab Customer Id from QB
                        if(qbc.DisplayName == Ca.Name) {
                            
                            string CarrierEmailAdd;
                            if(Ca.FreightTM__Email__c != null ) {
                                CarrierEmailAdd= Ca.FreightTM__Email__c;
                            }else{
                                CarrierEmailAdd = '';
                            }
                            
                            string BillingPostalCode;
                            if(Ca.FreightTM__Billing_Zip_Code__c != null ) {
                                BillingPostalCode= Ca.FreightTM__Billing_Zip_Code__c;
                            }else{
                                BillingPostalCode = '';
                            }
                            
                            string BillingCountry;
                            if(Ca.FreightTM__Billing_Country__c != null ) {
                                BillingCountry= Ca.FreightTM__Billing_Country__c;
                            }else{
                                BillingCountry = '';
                            }
                            string BillingState;
                            if(Ca.FreightTM__Billing_State_Province__c != null ) {
                                BillingState= Ca.FreightTM__Billing_State_Province__c;
                            }else{
                                BillingState = '';
                            }
                            
                            
                                                  String PaymentTermsID;
                            if(ca.Payment_Terms_QB_ID__c != null) {
                                PaymentTermsID = ca.Payment_Terms_QB_ID__c;
                            } else {
                                PaymentTermsID = '1'; // Default value if none of the conditions match
                            }
                    
                            
                            
                            String jsonString;
                            
                            // Create a JSON generator
                            JSONGenerator genc = JSON.createGenerator(true);
                            
                            genc.writeStartObject(); //{
                            genc.writeStringField('domain', 'QBO');
                            genc.writeFieldName('PrimaryEmailAddr');
                            genc.writeStartObject(); //{
                            genc.writeStringField('Address', CarrierEmailAdd);
                            genc.writeEndObject(); //}
                            genc.writeFieldName('PrimaryPhone');
                            genc.writeStartObject(); //{
                            genc.writeStringField('FreeFormNumber', Ca.FreightTM__Phone__c);
                            genc.writeEndObject(); //}
                            genc.writeBooleanField('Active', true);
                            genc.writeFieldName('BillAddr');
                            genc.writeStartObject();
                            genc.writeStringField('Line1', Ca.FreightTM__Billing_Address__c);
                            genc.writeStringField('City', Ca.FreightTM__Billing_City__c);
                            genc.writeStringField('CountrySubDivisionCode', BillingState);
                            genc.writeStringField('PostalCode', BillingPostalCode);
                            genc.writeStringField('Country', BillingCountry);
                            genc.writeEndObject();
                            genc.writeFieldName('TermRef');
                        genc.writeStartObject();
                        genc.writeStringField('value', PaymentTermsID);
                        genc.writeEndObject();
                            
                            
                            genc.writeStringField('DisplayName', Ca.Name);
                            genc.writeStringField('SyncToken', SyncToken);
                            genc.writeStringField('Id', CustomerID);
                            genc.writeEndObject(); //}
                            
                            
                            String qbnewc = genc.getAsString();
                            //Sending the http body with JSON 
                            HttpRequest req = new HttpRequest();
                            req.setEndpoint('callout:QuickBooks2/vendor');
                            req.setHeader('Content-Type','application/json');
                            req.setMethod('POST');
                            req.setHeader('Cache-Control', 'no-cache');
                            req.setTimeout(2000); //timeout in milliseconds
                            req.setBody(qbnewc);
                            
                            Http http = new Http();
                            HTTPResponse res = http.send(req);
                            
                            System.debug('Response: ' + res);
                            if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
                                qbCarrierid = qbc.Id;
                                updateCarrierQBCarrierId(Ca.Id, qbCarrierid);
                                system.debug('great: ' + qbc.Id);
                                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Carrier has been Updated in QB.'));
                                // Update the Salesforce record here
                            } else {
                                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was an Error on Updating the Carrier Record with The QBID, Please contact the administrator.'));
                            }
                            
                            
                            //msg = 'Customer is in QB. Please refresh browser, and click button once more to create Invoice in QB.';
                        }
                    }
                }
                
            }//if query response error, prompt error msg
            else {
                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was a query issue for getting the Carrier Info to update or create. Please contact your Administrator.')); 
            }
        }//customer address/phone is not null
        else {
            ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'Please make sure Carrier\'s Phone and Billing Address is filled in under Address Information.')); 
        }
        
        return null;
        
    }      
    
   
    private static void updateCarrierQBCarrierId(Id accountId, String qbCarrierid) {
    if (qbCarrierid != null) {
        FreightTM__Carrier__c CarrToUpdate = new FreightTM__Carrier__c(Id = accountId, LQB_QB_Id2__c = qbCarrierid,LQB_QB_Error__c = 'Success');
        update CarrToUpdate;
    } else {
        // Handle the case where qbcustomerid is null (optional)
        System.debug('qbCarrierid is null. Update not performed.');
    }
}
    

    public String getName() {
        return 'QuickBooksCarrierCreatorUpdator2';
    }
}

Create and update Carrier-Rate on QB:

Visualforce page:

<apex:page standardcontroller="Carrier_Rate__c" extensions="QuickBooksCarrierRate" tabStyle="Carrier_Rate__c" lightningStylesheets="true">
    <style>
    .customerError { font-weight: bold; list-style: none; }
    .invoiceError { color: red; font-weight: strong; }
    .bkg-inv { background-color: #eee; }
    .products tr { border: solid 1px #efefef; }
    .container { padding: 0% 5%;}
    .text { padding: 1%;}
    </style>
     
    <script>
    function launchWindow()
    {
        // window.top.location.reload();
       
    };
    function redir() {
        window.location = "/{!Car.Id}";
    }
    </script>
    
    <apex:pageMessages /><br />
    <apex:form >
       
            <apex:outputPanel id="allPanel">
                
                <!--<apex:outputPanel rendered="{!NOT(cid)}">-->
                    <script>
                       window.onload=function()
                       {
                         doInit();
                       };
                    </script>
                    
                
                 <apex:outputPanel rendered="{!car.Load_Id__r.New_QB__c == false }">
                <table width="100%">
                    <tr>
                        <td align="Center" style="text-align: left; vertical-align: top; font-size: 1.5em; font-weight: bold;color: red;">
                            **************NEW QB CHECKBOX IS SET TO FALSE | THIS INVOICE WILL BE SENT TO OLD QB ACCOUNT**************
                        </td>
                    </tr>
                    </table>
                    
                    </apex:outputPanel>
                
                    <div class="container">
                        <h2>Create/Update Carrier Rate/Bill to QuickBooks</h2>
                        <div class="bkg-inv">
                            <div class="text">
                                <table width="100%">
                                    <tr>
                                        <td>
                                           <span style="text-align: left; vertical-align: top; font-size: 1.5em; font-weight: bold;">Bill Name:&nbsp;<apex:outputField value="{!Car.Load_Id_Name__c}"/></span><br /><!-- (Conf. #)-->
                                           
                                        </td>
                                        <td style="text-align: right; padding-right:5%;">
                                            <span>BALANCE DUE</span><br />
                                            <span style="font-size: 2em; font-weight: bold;"><apex:outputField value="{!Car.Rate__c}"/></span>
                                        </td>
                                    </tr>
                                </table>
                                <br /><br />
                                <b>Customer:</b><br />
                                <apex:outputField value="{!Car.Carrier__c}" />
                                <br /><br />
                                <table width="100%">
                                    <tr>
                                        <td width="30%">
                                            <b>Billing Address:</b><br />
                                            <apex:outputField value="{!Car.Carrier__r.FreightTM__Billing_Address__c}"/><br />
                                            <apex:outputPanel ><apex:outputText value="{!Car.Carrier__r.FreightTM__Billing_City__c}"/>, <apex:outputText value=" {!Car.Carrier__r.FreightTM__Billing_State_Province__c}"/> <apex:outputText value=" {!Car.Carrier__r.FreightTM__Billing_Zip_Code__c}"/></apex:outputPanel><br />
                                            <apex:outputField value="{!Car.Carrier__r.FreightTM__Billing_Country__c}"/><br /><br />
                                        </td>
                                        <td width="30%">
                                        </td>
                                        <td width="20%" style="vertical-align: top;"><b>Invoice Date:</b><br />{!MONTH(TODAY())}/{!DAY(TODAY())}/{!YEAR(TODAY())}</td>
                                        <td width="20%" style="vertical-align: top;"><b>Terms:</b><br /><apex:outputField value="{!Car.Carrier__r.Payment_Terms__c}"/></td>
                                    </tr>
                                </table>
                                <table width="100%">
                                    <tr>
                                    </tr>
                                </table>
                           </div>
                        </div>
                        <br /><br />
                        <table width="100%" class="products">
                            <tr>
                                <th width="25%"><h4>PRODUCT/SERVICE</h4></th>
                                <th width="35%"><h4>Type</h4></th>
                                <th width="10%"><h4>QTY</h4></th>
                                <th width="25%"><h4>AMOUNT</h4></th>
                                
                            </tr>
                        
                             
                                <tr>
                                     
                                    <td><apex:outputField value="{!Car.Product__r.name}"/></td>
                                    <td><apex:outputField value="{!Car.Type__c}"/></td>
                                    <td>1</td>
                                    <td><apex:outputField value="{!Car.Rate__c}"/></td>
                                 
                                </tr>
                         
                        </table>
                        
                        <table width="100%">
                            <tr>
                                <td width="5%">&nbsp;</td>
                                <td width="15%">&nbsp;</td>
                                <td width="34%">&nbsp;</td>
                                <td width="10%">&nbsp;</td>
                                <td width="10%">&nbsp;</td>
                                <td width="11%" style="text-align: left;"><h3>Total</h3></td>
                                <td width="15%" style="text-align: left;"><h3><apex:outputField value="{!Car.Rate__c}"/></h3></td>
                            </tr>
                        </table>
                        <br/> 
                        <apex:commandButton onComplete="redir();" value="Back" />
                        <apex:commandButton value="Create/Update Carrier Rate/Bill" action="{!send}" />
                        <br/> <br/> <br/> <br/> 
                    </div>
                </apex:outputPanel>
    </apex:form>
</apex:page>

creating or updating a QuickBooks “Bill” for a carrier pay item based on your record:

public class QuickBooksCarrierRate{
    
    public string Termss;
    public Carrier_Rate__c Car {get; set;}
    public id CarId {get; set;}
    public id loadId {get; set;}
    public string qbcarrierid;
    public string qbbillid;
    public string msg;
    public integer taxcodeid;
    public List<Customer> clist;
    public string cname;//for unit test
    public string cnames;//for unit test
    public string cnames2;//for unit test
    public string invbody3;//for unit test
    public string invbody_x;//for unit test
    public date Billon;
    
    public Bill bill { get; set; }
    public String time_i { get; set; }
    
    // ====== QBO helper types ======
    public class MetaData_i { public String CreateTime { get; set; } public String LastUpdatedTime { get; set; } }
    public class CurrencyRef_i { public String value { get; set; } public String name { get; set; } }
    
    public class Line {
        public String Id { get; set; }
        public Integer LineNum { get; set; }
        public String Description {get;set;} 
        public List<LinkedTxn> LinkedTxn {get;set;} 
        public Double Amount { get; set; }
        public String DetailType { get; set; }
        public ItemBasedExpenseLineDetail ItemBasedExpenseLineDetail { get; set; }
    }
    
    public class ItemBasedExpenseLineDetail {
        public String BillableStatus { get; set; }
        public ItemRef ItemRef { get; set; }
        public Double UnitPrice { get; set; }
        public Double Qty { get; set; }
        public TaxCodeRef TaxCodeRef { get; set; }
    }
    
    public class ItemRef { public String value { get; set; } public String name { get; set; } }
    public class TaxCodeRef { public String value { get; set; } }
    public class VendorRef { public String value { get; set; } public String name { get; set; } }
    public class APAccountRef { public String value { get; set; } public String name { get; set; } }
    
    public class CurrencyRef { public String value; public String name; }
    public class PrimaryPhone { public String FreeFormNumber; }
    
    public class BillEmail { public String Address; }
    public class DeliveryInfo { public String DeliveryType; public String DeliveryTime; }
    
    public class SalesItemLineDetail {
        public CurrencyRef ItemRef;
        public Double UnitPrice;
        public Double Qty;
        public CurrencyRef ItemAccountRef;
        public TaxCodeRef TaxCodeRef;
    }
    
    public class Inv {
        public Bill Bill;
        public QueryResponse_i QueryResponse_i;
        public String time_i;
    }
    
    public class QueryResponse_i {
        public List<Bill> Bill;
        public Integer startPosition;
        public Integer maxResults;
        public Integer totalCount;
    }
    
    public class CustomField {
        public String DefinitionId;
        public String Type_Z; // in json: Type
        public String Name;
        public String StringValue;
    }
    
    public class LinkedTxn { public String TxnId; public String TxnType; }
    
    public class Bill {
        public String DueDate {get;set;} 
        public Double Balance {get;set;} 
        public String domain {get;set;} 
        public Boolean sparse {get;set;} 
        public String Id {get;set;} 
        public String SyncToken {get;set;} 
        public MetaData MetaData {get;set;} 
        public MetaData_i MetaData_i;
        public String TxnDate {get;set;} 
        public List<CustomField> CustomField;
        public CurrencyRef CurrencyRef {get;set;} 
        public List<Line> Line {get;set;} 
        public CurrencyRef VendorRef {get;set;} 
        public CurrencyRef APAccountRef {get;set;} 
        public Double TotalAmt {get;set;} 
        public String DocNumber;
    }
    
    public class Line_Y { public String Id; public Integer LineNum; public Double Amount; public String DetailType; public SalesItemLineDetail SalesItemLineDetail; public CustomField SubTotalLineDetail; }
    public class Line_X { public String Id; public Integer LineNum; public Double Amount; public String DetailType; public SalesItemLineDetail SalesItemLineDetail; public CustomField SubTotalLineDetail; }
    public class Line_W { public String Id; public Integer LineNum; public Double Amount; public String DetailType; public SalesItemLineDetail SalesItemLineDetail; public CustomField SubTotalLineDetail; }
    
    public class AccountBasedExpenseLineDetail {
        public CurrencyRef AccountRef {get;set;} 
        public String BillableStatus {get;set;} 
        public LastModifiedByRef TaxCodeRef {get;set;} 
    }
    public class Cust { public QueryResponse QueryResponse {get;set;} public String time_x {get;set;} }
    
    public class Line_Z { public String Id; public Integer LineNum; public Double Amount; public String DetailType; public SalesItemLineDetail SalesItemLineDetail; public CustomField SubTotalLineDetail; }
    public class LastModifiedByRef { public String value {get;set;} }
    public class MetaData { public String CreateTime {get;set;} public LastModifiedByRef LastModifiedByRef {get;set;} public String LastUpdatedTime {get;set;} }
    public class QueryResponse { public List<Bill> Bill {get;set;} public Integer startPosition {get;set;} public Integer maxResults {get;set;} public Integer totalCount {get;set;} }
    
    public QuickBooksCarrierRate(ApexPages.StandardController stdController) {
        carId = ApexPages.currentPage().getParameters().get('id');
        clist = new List<Customer>();
        
        Car = [
            select Id, Name, Accessorials__c,Balance_Due__c,Bill_Due__c,Bill_Due_2__c,Bill_Due_3__c,Bill_Reference__c,Billed_On__c,Billed_Status__c,
            Carrier__c,Files__c,Credit_Limit_Net_days__c,Days_after_Bill_On__c,Extra_Charges__c,Fulfillment__c,Load_ID__c,Load_ID__r.NEW_QB__c,Load_Id_Name__c,NOA__c,
            Overpayment__c,Partial_Payment__c,Payment_Date__c,Payment_Date_2__c,Payment_Method__c,Payment_Reference__c,Payment_Schedule__c,PRO__c,
            PU__c,LQB_QB_Company__c,LQB_QB_Id__c,LQB_QB_No__c,LQB_QB_Seq__c,LQB_QB_Error__c,Rate__c,Remaining_Balance_Due__c,Type__c,Carrier__r.Payment_Terms__c,Carrier__r.Payment_Terms_QB_ID__c,
            Carrier__r.LQB_QB_Id__c,Product__r.LQB_QB_Id__c,Carrier__r.LQB_QB_Id2__c,Product__r.LQB_QB_Id2__c,Product__r.name,Product__c,Carrier__r.FreightTM__Billing_Address__c,Carrier__r.FreightTM__Billing_City__c,
            Carrier__r.FreightTM__Billing_State_Province__c,Carrier__r.FreightTM__Billing_Zip_Code__c,Carrier__r.FreightTM__Billing_Country__c,New_QuickBooks_ID__c
            from Carrier_Rate__c 
            where id =: carId
        ];
    }
    
    // create/update Bill in QB
    public PageReference send() {
        try{ 
            if(Car.Carrier__c == null || (Car.Carrier__r.LQB_QB_Id__c == null && Car.Load_ID__r.New_QB__c == false)) {
                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.Error, 'Missing Old Carrier QB ID.'));
                return null;	
            } else if(Car.Carrier__c == null || (Car.Carrier__r.LQB_QB_Id2__c == null && Car.Load_ID__r.New_QB__c == true)){
                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.Error, 'Missing NEW Carrier QB ID. Please check the Assigned Carrier'));
                return null;	
            } else {
                string NEWQB ='';
                string CarRateID ='';
                if(Car.Load_ID__r.New_QB__c == True) {
                    NEWQB = 'QuickBooks2';
                    CarRateID = Car.New_QuickBooks_ID__c;
                } else {
                    NEWQB = 'QuickBooks';
                    CarRateID = Car.LQB_QB_Id__c;
                }
                
                // ===================== CREATE =====================
                if (String.isBlank(CarRateID)) {
                    datetime timenow =  DateTime.now();
                    String formattedDateTime = timenow.format('yyyy-MM-dd');
                    
                    String PaymentTermsID;
                    if(Car.Carrier__r.Payment_Terms_QB_ID__c != null) {
                        PaymentTermsID = Car.Carrier__r.Payment_Terms_QB_ID__c;
                    } else {
                        PaymentTermsID = '1';
                    }
                    
                    JSONGenerator gen = JSON.createGenerator(true); 
                    gen.writeStartObject(); // {
                    
                    // ----------- CustomField: Bill Ref -----------
                    gen.writeFieldName('CustomField');
                    gen.writeStartArray();
                    gen.writeStartObject();
                    gen.writeStringField('DefinitionId', '1');
                    gen.writeStringField('Name', 'Bill Ref');
                    gen.writeStringField('Type', 'StringType');
                    gen.writeStringField('StringValue', (Car.Bill_Reference__c == null ? '' : Car.Bill_Reference__c));
                    gen.writeEndObject();
                    gen.writeEndArray();
                    
                    // ----------- Line -----------
                    gen.writeFieldName('Line');
                    gen.writeStartArray();
                    
                    String QBIDstring;
                    if(Car.Load_ID__r.New_QB__c == True ) {
                        QBIDstring = (Car.Product__c != null && Car.Product__r.LQB_QB_Id2__c != null) ? Car.Product__r.LQB_QB_Id2__c : '1';
                    } else {
                        QBIDstring = (Car.Product__c != null && Car.Product__r.LQB_QB_Id__c  != null) ? Car.Product__r.LQB_QB_Id__c  : '1';
                    }
                    
                    gen.writeStartObject();
                    gen.writeNumberField('Amount', Car.Rate__c);
                    gen.writeStringField('DetailType', 'ItemBasedExpenseLineDetail');
                    gen.writeFieldName('ItemBasedExpenseLineDetail');
                    gen.writeStartObject();
                    gen.writeStringField('BillableStatus', 'NotBillable');
                    gen.writeFieldName('ItemRef');
                    gen.writeStartObject();
                    gen.writeStringField('value', QBIDstring);
                    gen.writeEndObject();
                    gen.writeNumberField('UnitPrice', Car.Rate__c);
                    gen.writeNumberField('Qty', 1);
                    gen.writeEndObject(); // ItemBasedExpenseLineDetail
                    gen.writeEndObject(); // Line[0]
                    
                    gen.writeEndArray(); // Line
                    
                    // ----------- Terms -----------
                    gen.writeFieldName('SalesTermRef');
                    gen.writeStartObject();
                    gen.writeStringField('value', PaymentTermsID);
                    gen.writeEndObject();
                    
                    // ----------- VendorRef -----------
                    String CarrierID = (Car.Load_ID__r.New_QB__c == True) ? Car.Carrier__r.LQB_QB_Id2__c : Car.Carrier__r.LQB_QB_Id__c;
                    gen.writeFieldName('VendorRef');
                    gen.writeStartObject();
                    gen.writeStringField('value', CarrierID);
                    gen.writeEndObject();
                    
                    // ----------- Header fields -----------
                    gen.writeStringField('DocNumber', Car.Load_Id_Name__c);
                    gen.writeStringField('TxnDate', formattedDateTime);
                    
                    gen.writeEndObject(); // }
                    
                    String jsonS = gen.getAsString();
                    System.debug(jsonS);
                    
                    HttpRequest req = new HttpRequest();
                    req.setEndpoint('callout:'+NEWQB+'/bill');
                    req.setHeader('Content-Type','application/json'); 
                    req.setHeader('Accept', 'application/json');
                    req.setMethod('POST');
                    req.setTimeout(2000);
                    req.setbody(jsonS);
                    
                    Http http = new Http();
                    HTTPResponse res = http.send(req);    
                    
                    System.debug(res);
                    String invbody = res.getBody();
                    system.debug(invbody);
                    
                    String invbody2 = invbody.replace('"BillAddr":', '"BillAddr_i":');
                    String invbody3 = invbody2.replace('"Bill":', '"Bill":'); 
                    String invbody4 = invbody3.replace('"MetaData":', '"MetaData_i":');
                    String invbody5 = invbody4.replace('"CurrencyRef":', '"CurrencyRef_i":');
                    String invbody_x = invbody5.replace('"time":', '"time_i":');
                    system.debug('III: ' + invbody_x);
                    
                    if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
                        QuickBooksCarrierRate.Inv invt = (QuickBooksCarrierRate.Inv)JSON.deserialize(invbody_x, QuickBooksCarrierRate.Inv.class);
                        bill v = invt.bill;
                        
                        if(v != null) { 
                            qbBillid = v.ID;
                            Billon = Date.today();
                            if(Car.Load_ID__r.New_QB__c == True ) {
                                updateCarrierRate2(Car.Id,qbBillid,Billon);
                            } else {
                                updateCarrierRate(Car.Id,qbBillid,Billon);
                            }
                            ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.Confirm, 'Carrier Rate has been created in QB.'));
                        }
                    } else {
                        ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.ERROR, 'Carrier Rate could NOT be created in QB. Please contact your Administrator.'));
                    }
                }
                // ===================== UPDATE =====================
                else {
                    JSONGenerator geni = JSON.createGenerator(true); 
                    String invoicestr = geni.getAsString();
                    
                    HttpRequest reqinv = new HttpRequest();
                    reqinv.setEndpoint('callout:'+NEWQB+'/query?query=select%20%2a%20from%20bill%20where%20Id%20%3d%20%27'+ CarRateID +'%27');
                    reqinv.setHeader('Accept','application/json');
                    reqinv.setMethod('GET');
                    reqinv.setTimeout(2000);
                    reqinv.setBody(invoicestr);
                    
                    Http httpinv = new Http();
                    HTTPResponse resinv = httpinv.send(reqinv);    
                    String invbody = resinv.getBody();
                    system.debug(invbody);
                    
                    String invbody2 = invbody.replace('"BillAddr":', '"BillAddr_i":');
                    String invbody3 = invbody2.replace('"QueryResponse":', '"QueryResponse_i":');
                    String invbody4 = invbody3.replace('"MetaData":', '"MetaData_i":');
                    String invbody5 = invbody4.replace('"CurrencyRef":', '"CurrencyRef_i":');
                    String invbody_x = invbody5.replace('"time":', '"time_i":');
                    system.debug('III: ' + invbody_x);
                    
                    if (resinv.getStatusCode() >= 200 && resinv.getStatusCode() < 300) {
                        QuickBooksCarrierRate.Inv invt = (QuickBooksCarrierRate.Inv)JSON.deserialize(invbody_x, QuickBooksCarrierRate.Inv.class);
                        QueryResponse_i v = invt.QueryResponse_i;
                        List<Bill> invlist = v.Bill;
                        
                        if(invlist != null) { 
                            for(Bill qbi : invlist) {
                                String synctoken = qbi.SyncToken;
                                String QBInvoiceID = qbi.ID;
                                String QBInvoiceDate = qbi.TxnDate;
                                
                                String PaymentTermsID;
                                if(Car.Carrier__r.Payment_Terms_QB_ID__c != null) {
                                    PaymentTermsID = Car.Carrier__r.Payment_Terms_QB_ID__c;
                                } else {
                                    PaymentTermsID = '1';
                                }
                                
                                JSONGenerator gen = JSON.createGenerator(true);
                                gen.writeStartObject();
                                
                                // Header
                                gen.writeStringField('DocNumber', Car.Load_Id_Name__c);
                                gen.writeStringField('SyncToken', synctoken);
                                gen.writeStringField('domain', 'QBO');
                                gen.writeStringField('Id', QBInvoiceID);
                                
                                // ----------- CustomField: Bill Ref (UPDATE) -----------
                                gen.writeFieldName('CustomField');
                                gen.writeStartArray();
                                gen.writeStartObject();
                                gen.writeStringField('DefinitionId', '1');
                                gen.writeStringField('Name', 'Bill Ref');
                                gen.writeStringField('Type', 'StringType');
                                gen.writeStringField('StringValue', (Car.Bill_Reference__c == null ? '' : Car.Bill_Reference__c));
                                gen.writeEndObject();
                                gen.writeEndArray();
                                
                                // Terms
                                gen.writeFieldName('SalesTermRef');
                                gen.writeStartObject();
                                gen.writeStringField('value', PaymentTermsID);
                                gen.writeEndObject();
                                
                                // VendorRef
                                String CarrierID = (Car.Load_ID__r.New_QB__c == True) ? Car.Carrier__r.LQB_QB_Id2__c : Car.Carrier__r.LQB_QB_Id__c;
                                gen.writeFieldName('VendorRef');
                                gen.writeStartObject();
                                gen.writeStringField('value', CarrierID);
                                gen.writeEndObject();
                                
                                // Totals (optional)
                                gen.writeNumberField('TotalAmt', Car.Rate__c);
                                
                                // Lines
                                gen.writeFieldName('Line');
                                gen.writeStartArray();
                                
                                String QBIDstring;
                                if(Car.Load_ID__r.New_QB__c == True ) {
                                    QBIDstring = (Car.Product__c != null && Car.Product__r.LQB_QB_Id2__c != null) ? Car.Product__r.LQB_QB_Id2__c : '1';
                                } else {
                                    QBIDstring = (Car.Product__c != null && Car.Product__r.LQB_QB_Id__c  != null) ? Car.Product__r.LQB_QB_Id__c  : '1';
                                }
                                
                                gen.writeStartObject();
                                gen.writeNumberField('Amount', Car.Rate__c);
                                gen.writeStringField('DetailType', 'ItemBasedExpenseLineDetail');
                                gen.writeFieldName('ItemBasedExpenseLineDetail');
                                gen.writeStartObject();
                                gen.writeStringField('BillableStatus', 'NotBillable');
                                gen.writeFieldName('ItemRef');
                                gen.writeStartObject();
                                gen.writeStringField('value', QBIDstring);
                                gen.writeEndObject();
                                gen.writeNumberField('UnitPrice', Car.Rate__c);
                                gen.writeNumberField('Qty', 1);
                                gen.writeEndObject(); // ItemBasedExpenseLineDetail
                                gen.writeEndObject(); // Line[0]
                                
                                gen.writeEndArray(); // Line
                                
                                gen.writeEndObject();
                                
                                String jsonPayload = gen.getAsString();
                                System.debug(jsonPayload);
                                
                                HttpRequest updateReq = new HttpRequest();
                                updateReq.setEndpoint('callout:'+NEWQB+'/bill');
                                updateReq.setHeader('Content-Type','application/json'); 
                                updateReq.setHeader('Accept', 'application/json');
                                updateReq.setMethod('POST');
                                updateReq.setBody(jsonPayload);
                                
                                Http http = new Http();
                                HttpResponse updateRes = http.send(updateReq);
                                String invbodyt = updateRes.getBody();
                                system.debug(invbodyt);
                                
                                if (updateRes.getStatusCode() >= 200 && updateRes.getStatusCode() < 300) {
                                    qbBillid = QBInvoiceID;
                                    Date dateValue = (QBInvoiceDate == null ? Date.today() : Date.valueOf(QBInvoiceDate));
                                    Billon = dateValue;
                                    if(Car.Load_ID__r.New_QB__c == True ) {
                                        updateCarrierRate2(Car.Id,qbBillid,Billon);
                                    } else {
                                        updateCarrierRate(Car.Id,qbBillid,Billon);
                                    }
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Carrier Rate/Bill has been Updated on QB.'));
                                } else {
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.ERROR, 'Carrier Rate/Bill could NOT be Updated in QB. Please contact your Administrator.'));
                                }
                            }
                        }
                    } else {
                        ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was a query issue in QB for getting the Carrier Rate. Please contact your Administrator.')); 
                    }
                }
            }
        } catch(DmlException ex){
            ApexPages.addMessages(ex);
        }
        return null;
    }
    
    public String getName() { return 'QuickBooksCarrierRate'; }
    
    private static void updateCarrierRate(Id CarID,String qbBillid, date Billon) {
        Carrier_Rate__c Loadrecupdate = new Carrier_Rate__c(Id = CarID,LQB_QB_Id__c =qbBillid,LQB_QB_Error__c = 'Success',Billed_On__c = Billon);
        update Loadrecupdate;
    }
    private static void updateCarrierRate2(Id CarID,String qbBillid, date Billon) {
        Carrier_Rate__c Loadrecupdate2 = new Carrier_Rate__c(Id = CarID,New_QuickBooks_ID__c =qbBillid,LQB_QB_Error__c = 'Success',Billed_On__c = Billon);
        update Loadrecupdate2;
    }
}

Create and update Invoice (Load) on QB:

Visualforce Page:

<apex:page standardcontroller="FreightTM__Load__c" extensions="InvoiceControllerUpdate" tabStyle="FreightTM__Load__c" lightningStylesheets="true">
    <style>
        .customerError { font-weight: bold; list-style: none; }
        .invoiceError { color: red; font-weight: strong; }
        .bkg-inv { background-color: #eee; }
        .products tr { border: solid 1px #efefef; }
        .container { padding: 0% 5%;}
        .text { padding: 1%;}
    </style>
    
    <script>
    function launchWindow()
    {
        // window.top.location.reload();
        
    };
    function redir() {
        window.location = "/{!lo.Id}";
    }
    </script>
    
    <apex:pageMessages /><br />
    <apex:form >
        
        <apex:outputPanel id="allPanel">
            
            <!--<apex:outputPanel rendered="{!NOT(cid)}">-->
            <script>
            window.onload=function()
            {
                doInit();
            };
            </script>
            <apex:outputPanel rendered="{!FreightTM__Load__c.New_QB__c == false}">
                <table width="100%">
                    <tr>
                        <td align="Center" style="text-align: left; vertical-align: top; font-size: 1.5em; font-weight: bold;color: red;">
                            **************NEW QB CHECKBOX IS SET TO FALSE | THIS INVOICE WILL BE SENT TO OLD QB ACCOUNT**************
                        </td>
                    </tr>
                </table>
                
            </apex:outputPanel>
            
            <div class="container">
                <h2>Update Invoice on QuickBooks</h2>
                <div class="bkg-inv">
                    <div class="text">
                        <table width="100%">
                            <tr>
                                <td>
                                    <span style="text-align: left; vertical-align: top; font-size: 1.5em; font-weight: bold;">Invoice No:&nbsp;<apex:outputField value="{!lo.Name}"/></span><br /><!-- (Conf. #)-->
                                    
                                </td>
                                <td style="text-align: right; padding-right:5%;">
                                    <span>BALANCE DUE</span><br />
                                    <span style="font-size: 2em; font-weight: bold;"><apex:outputField value="{!lo.FreightTM__Total__c}"/></span>
                                </td>
                            </tr>
                        </table>
                        <br /><br />
                        <b>Customer:</b><br />
                        <apex:outputField value="{!lo.FreightTM__Customer__c}" />
                        <br /><br />
                        <table width="100%">
                            <tr>
                                <td width="30%">
                                    <b>Billing Address:</b><br />
                                    <apex:outputField value="{!lo.FreightTM__Customer__r.BillingStreet}"/><br />
                                    <apex:outputPanel ><apex:outputText value="{!lo.FreightTM__Customer__r.BillingCity}"/>, <apex:outputText value=" {!lo.FreightTM__Customer__r.BillingState}"/> <apex:outputText value=" {!lo.FreightTM__Customer__r.BillingPostalCode}"/></apex:outputPanel><br />
                                    <apex:outputField value="{!lo.FreightTM__Customer__r.BillingCountry}"/><br /><br />
                                </td>
                                <td width="30%">
                                    <b>Shipping Address:</b><br />
                                    <apex:outputText value="{!FreightTM__Load__c.FreightTM__DeliveryFacility__r.Name}" /><br />
                                    <apex:outputField value="{!lo.FreightTM__DeliveryFacility__r.FreightTM__Street__c}"/><br />
                                    <apex:outputPanel ><apex:outputText value="{!lo.FreightTM__DeliveryFacility__r.FreightTM__City__c}"/>, <apex:outputText value=" {!lo.FreightTM__DeliveryFacility__r.FreightTM__State__c}"/> <apex:outputText value=" {!lo.FreightTM__DeliveryFacility__r.FreightTM__Zip_Code__c}"/></apex:outputPanel><br />
                                    <apex:outputField value="{!lo.FreightTM__DeliveryFacility__r.FreightTM__Country__c}"/><br /><br />
                                </td>
                                <td width="20%" style="vertical-align: top;"><b>Invoice Date:</b><br />{!MONTH(TODAY())}/{!DAY(TODAY())}/{!YEAR(TODAY())}</td>
                                <td width="20%" style="vertical-align: top;"><b>Terms:</b><br />
                                    <apex:outputField value="{!lo.FreightTM__Customer__r.Payment_Terms__c}"/></td>
                            </tr>
                        </table>
                        <table width="100%">
                            <tr>
                                <td width="30%" style="vertical-align: top;"><b>SHIP DATE:</b> <br /><apex:outputText value="{0, date, MM'/'d'/'yyyy}"><apex:param value="{!lo.FreightTM__Delivery_Date__c}"/></apex:outputText></td>
                                <td width="40%" style="vertical-align: top;"><b>TRACKING NO.</b><br /><apex:outputField value="{!lo.FreightTM__Delivery_Remarks__c}"/></td><!-- Tracking_Number__c -->
                            </tr>
                        </table>
                    </div>
                </div>
                <br /><br />
                <table width="100%" class="products">
                    <tr>
                        <th width="5%" style="padding-left: 1%;"><h4>#</h4></th>
                        <th width="15%"><h4>PRODUCT/SERVICE</h4></th>
                        <th width="35%"><h4>DESCRIPTION</h4></th>
                        <th width="5%"><h4>QTY</h4></th>
                        <th width="10%"><h4>RATE</h4></th>
                        <th width="25%"><h4>AMOUNT</h4></th>
                        <!--<th width="15%"><h4>SALES TAX</h4></th> -->
                    </tr>
                    <!--  <tr>
<td height="40" style="padding-left: 1%;">1</td>
<td>Rate</td>
<td><apex:outputPanel >Rate</apex:outputPanel></td>
<td>1</td>
<td><apex:outputField value="{!lo.FreightTM__Rate__c}"/></td>
<td><apex:outputField value="{!lo.FreightTM__Rate__c}"/></td>

</tr>-->
                    <apex:variable value="{!1}" var="rowNum"/>
                    <apex:repeat var="s" value="{!lo.FreightTM__Line_Items__r}">
                        <tr>
                            <td height="40" style="padding-left: 1%;"><apex:outputText value="{!FLOOR(rowNum)}"/><apex:variable var="rowNum" value="{!rowNum + 1}"/></td>
                            <td><apex:outputField value="{!s.Product__r.name}"/></td>
                            <td><apex:outputPanel ><apex:outputField value="{!lo.FreightTM__Container_Size__c}"/>&nbsp;-&nbsp;
                                <apex:outputField value="{!lo.FreightTM__PickupFacility__r.FreightTM__City__c}"/>,&nbsp;<apex:outputField value="{!lo.FreightTM__PickupFacility__r.FreightTM__State__c}"/>
                                &nbsp;to&nbsp;<apex:outputField value="{!lo.FreightTM__DeliveryFacility__r.FreightTM__City__c}"/>,&nbsp;<apex:outputField value="{!lo.FreightTM__DeliveryFacility__r.FreightTM__State__c}"/>
                                </apex:outputPanel></td>
                            <td><apex:outputField value="{!s.FreightTM__Number_of_Units__c}"/></td>
                            <td><apex:outputField value="{!s.FreightTM__Unit_Price__c}"/></td>
                            <td><apex:outputField value="{!s.FreightTM__Amount__c}"/></td>
                            
                        </tr>
                    </apex:repeat>
                </table>
                
                <table width="100%">
                    <tr>
                        <td width="5%">&nbsp;</td>
                        <td width="15%">&nbsp;</td>
                        <td width="34%">&nbsp;</td>
                        <td width="10%">&nbsp;</td>
                        <td width="10%">&nbsp;</td>
                        <td width="11%" style="text-align: left;"><h3>Total</h3></td>
                        <td width="15%" style="text-align: left;"><h3><apex:outputField value="{!lo.FreightTM__Total__c}"/></h3></td>
                    </tr>
                </table>
                <br/> 
                <apex:commandButton onComplete="redir();" value="Back" />
                <apex:commandButton value="Update Invoice" action="{!send}" />
                <br/> <br/> <br/> <br/> 
            </div>
        </apex:outputPanel>
    </apex:form>
</apex:page>

This controller is your customer billing integration:
Load line items → QuickBooks Invoice lines, stored back on the Load, with support for two QB companies (QuickBooks vs QuickBooks2):

public class InvoiceControllerUpdate{
    
    public string Termss;
    public FreightTM__Load__c lo {get; set;}
    public id loadId {get; set;}
    public string qbcustomerid;
    public string qbInvoiceid;
    public string msg;
    public integer taxcodeid;
    public List<Customer> clist;
    public string cname;//for unit test
    public string cnames;//for unit test
    public string cnames2;//for unit test
    public string invbody3;//for unit test
    public string invbody_x;//for unit test
    
    //Apex classes from QB Customer Object
    public class BillAddr {
        public String Id;
        public String Line1;
        public String City;
        public string Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
    }
    public class CurrencyRef {
        public String value;
        public String name;
    }
    public class Cust { //this needs to be in a class for json deserialization to work
        public QueryResponse QueryResponse;
        public String time_x; //time is an apex reserved word, replaced it with time_x in the response string
    }
    public class PrimaryPhone {
        public String FreeFormNumber;
    }
    public class Customer {
        public Boolean Taxable;
        public BillAddr BillAddr;
        public Boolean Job;
        public Boolean BillWithParent;
        public Double Balance;
        public Double BalanceWithJobs;
        public CurrencyRef CurrencyRef;
        public String PreferredDeliveryMethod;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData MetaData;
        public String GivenName;
        public String FamilyName;
        public String FullyQualifiedName;
        public String CompanyName;
        public String DisplayName;
        public String PrintOnCheckName;
        public Boolean Active;
        public PrimaryPhone PrimaryPhone;
    }
    public class MetaData {
        public String CreateTime;
        public String LastUpdatedTime;
    }
    public class QueryResponse {
        public List<Customer> Customer; 
        public Integer startPosition;
        public Integer maxResults;
    }
    
    //Apex classes from QB Invoice Object
    public class BillAddr_i { //rename to avoid duplicate class
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String Country;
        public String CountrySubDivisionCode;
        public String PostalCode;
        public String Lat;
        public String Long_Z; // in json: Long
    }
    public class CurrencyRef_i {
        public String value;
        public String name;
    }
    public class Invoice {
        public Integer Deposit;
        public Boolean AllowIPNPayment;
        public Boolean AllowOnlinePayment;
        public Boolean AllowOnlineCreditCardPayment;
        public Boolean AllowOnlineACHPayment;
        public String EInvoiceStatus;
        public String ECloudStatusTimeStamp;
        public String domain;
        public Boolean sparse;
        public String Id;
        public String SyncToken;
        public MetaData_i MetaData_i;
        public List<CustomField> CustomField;
        public String DocNumber;
        public String TxnDate;
        public CurrencyRef_i CurrencyRef_i;
        public List<LinkedTxn> LinkedTxn;
        public List<Line> Line;
        public CurrencyRef CustomerRef;
        public TaxCodeRef CustomerMemo; //
        public BillAddr_i BillAddr_i;
        public TaxCodeRef SalesTermRef; //
        public String DueDate;
        public Double TotalAmt;
        public Double HomeTotalAmt;
        public Boolean ApplyTaxAfterDiscount; //USA
        public String PrintStatus;
        public String EmailStatus;
        public BillEmail BillEmail;
        public Double Balance; //use Double instead of Integer, otherwise errors with decimals
        public DeliveryInfo DeliveryInfo;
        public BillAddr_Z ShipAddr;

    }
    public class BillEmail {
        public String Address;
    }
    public class DeliveryInfo {
        public String DeliveryType;
        public String DeliveryTime;
    }
    public class BillAddr_Z {
        public String Id;
        public String Line1;
        public String Line2;
        public String City;
        public String CountrySubDivisionCode;
        public String PostalCode;
    }
    public class SalesItemLineDetail {
        public CurrencyRef ItemRef;
        public Double UnitPrice; //use Double instead of Integer, otherwise errors with decimals
        public Double Qty; //
        public CurrencyRef ItemAccountRef;
        public TaxCodeRef TaxCodeRef; //
    }
    public class Line_Z {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    
    public class Line_Y {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    
    public class Line_X {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Line_W {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public CustomField SubTotalLineDetail;
    }
    public class Inv {
        public QueryResponse_i QueryResponse_i;
        public String time_i;
    }
    
    public class Line {
        public String Id;
        public Integer LineNum;
        public Double Amount; //use Double instead of Integer, otherwise errors with decimals
        public String DetailType;
        public SalesItemLineDetail SalesItemLineDetail;
        public LinkedTxn SubTotalLineDetail;
    }
    public class TaxCodeRef { //
        public String value;
    }
    public class MetaData_i {
        public String CreateTime;
        public String LastUpdatedTime;
    }
    
    public class QueryResponse_i { //rename to avoid duplicate class
        public List<Invoice> Invoice;
        public Integer startPosition;
        public Integer maxResults;
        public Integer totalCount;
    }
    public class CustomField {
        public String DefinitionId;
        public String Type_Z; // in json: Type
        public String Name;
        public String StringValue;
    }
    
    public class LinkedTxn {
        public String TxnId;
        public String TxnType;
    }
    
    
    public InvoiceControllerUpdate(ApexPages.StandardController stdController) {
        loadId = ApexPages.currentPage().getParameters().get('id');
        clist = new List<Customer>();
        
        lo = [select Id, Name, (select Id, Name, FreightTM__Type__c, FreightTM__Description__c, FreightTM__Amount__c,Product__c,Product__r.name,
                                Product__r.LQB_QB_Id__c,Product__r.LQB_QB_Id2__c,//Freight_Component__r.QB_Id__c,Freight_Component__r.Freight_Component_Name__c,  Freight_Component__r.name,
                                FreightTM__Unit_Price__c,FreightTM__Number_of_Units__c from FreightTM__Line_Items__r), 
              FreightTM__pickup_city__c, FreightTM__Pickup_State__c, FreightTM__Pickup_Country__c, FreightTM__Pickup_Arrival__c, 
              FreightTM__Load_Canceled__c,FreightTM__Container_Size__c,LQB_QB_Id__c,LQB_QB_error__c,
              FreightTM__PickupFacility__r.FreightTM__City__c,FreightTM__PickupFacility__r.FreightTM__state__c,
              FreightTM__DeliveryFacility__r.FreightTM__City__c,Balance_Due__c,Bill_Reference__c,FreightTM__Remarks__c,
              FreightTM__DeliveryFacility__r.FreightTM__state__c,FreightTM__DeliveryFacility__r.FreightTM__Country__c,
              FreightTM__DeliveryFacility__r.FreightTM__Zip_Code__c,FreightTM__DeliveryFacility__r.Name,
              FreightTM__DeliveryFacility__r.FreightTM__Street__c,FreightTM__Pickup_Date__c,FreightTM__Delivery_Date__c,FreightTM__Delivery_Remarks__c, //Tracking_Number__c,FreightTM__Customer__r.Payment_Terms__c,
              FreightTM__Confirmation_Number__c, FreightTM__Customer__c, FreightTM__Customer__r.Name, FreightTM__Customer__r.BillingAddress, 
              FreightTM__Customer__r.BillingStreet, FreightTM__Customer__r.BillingCity, 
              FreightTM__Customer__r.BillingState, FreightTM__Customer__r.BillingPostalCode, FreightTM__Customer__r.BillingCountry,
              FreightTM__Customer__r.QBCustomerID__c,FreightTM__Customer__r.Payment_Terms__c,FreightTM__Customer__r.Accounting_Email__c,
              FreightTM__Customer__r.ShippingAddress, FreightTM__Customer__r.ShippingStreet, FreightTM__Customer__r.ShippingCity, 
              FreightTM__Customer__r.FreightTM__General_Email__c,FreightTM__Customer__r.Net_Pay_Terms__c,
              FreightTM__Customer__r.ShippingState, FreightTM__Customer__r.ShippingPostalCode, FreightTM__Customer__r.ShippingCountry,
              FreightTM__Customer__r.Phone,  New_QB__c,FreightTM__Customer__r.QBCustomerIDNEW__c,
              FreightTM__Rate__c, FreightTM__Status__c, FreightTM__Delivery_city__c, FreightTM__Delivery_State__c, FreightTM__Delivery_Country__c, 
              FreightTM__Delivery_Departure__c, FreightTM__total__c,FreightTM__Customer__r.Payment_Terms_QB_ID__c,New_QuickBooks_ID__c
              
              from FreightTM__Load__c 
              where Id = :loadId];
    }
    
    
    
    
    
    //create invoice in QB
    public PageReference send() {
        
        string NEWQB ='';
        if(lo.New_QB__c == True) { NEWQB = 'QuickBooks2';}
        else{ NEWQB = 'QuickBooks';}
        
        try{ 
            // validation for customer↔QB mapping
            if(lo.FreightTM__Customer__c == null || lo.FreightTM__Customer__r.QBCustomerID__c == null && lo.New_QB__c == false) {
                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.Error, 'Missing Old Customer QB.'));
                return null;	
            } else if(lo.FreightTM__Customer__c == null || lo.FreightTM__Customer__r.QBCustomerIDNEW__c == null && lo.New_QB__c == true){
                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.Error, 'Missing NEW Customer QB. Please check the Assigned Customer'));
                return null;	
            }
            else{
                // check if invoice already exists in QB
                JSONGenerator geni = JSON.createGenerator(true); 
                String invoicestr = geni.getAsString();
                HttpRequest reqinv = new HttpRequest();
                reqinv.setEndpoint('callout:'+NEWQB+'/query?query=select%20%2a%20from%20Invoice%20where%20DocNumber%20%3d%20%27'+ lo.name +'%27');
                //reqinv.setEndpoint('callout:quickbooks/query?query=select%20%2a%20from%20Invoice%20where%20DocNumber%20%3d%20%27'+ lo.Load_Ref__c +'%27&minorversion=4');
                reqinv.setHeader('Accept','application/json');
                reqinv.setMethod('GET');
                reqinv.setTimeout(2000); // timeout in milliseconds
                reqinv.setBody(invoicestr);
                
                Http httpinv = new Http();
                HTTPResponse resinv = httpinv.send(reqinv);    
                
                String invbody = resinv.getBody();
                String invbody2 = invbody.replace('"BillAddr":', '"BillAddr_i":'); //to avoid duplicate classes
                String invbody3 = invbody2.replace('"QueryResponse":', '"QueryResponse_i":'); //to avoid duplicate classes
                String invbody4 = invbody3.replace('"MetaData":', '"MetaData_i":'); //to avoid duplicate classes
                String invbody5 = invbody4.replace('"CurrencyRef":', '"CurrencyRef_i":'); //to avoid duplicate classes
                String invbody_x = invbody5.replace('"time":', '"time_i":'); //time is an apex reserved word, replaced it with time_i
                system.debug('III: ' + invbody_x);
                
                //If no errors in query response, deserialize json string response and assign variables to values.
                if (resinv.getStatusCode() >= 200 && resinv.getStatusCode() < 300) {
                    InvoiceControllerUpdate.Inv invt = (InvoiceControllerUpdate.Inv)JSON.deserialize(invbody_x, InvoiceControllerUpdate.Inv.class);
                    system.debug('Invoice: ' + invt);
                    
                    QueryResponse_i v = invt.QueryResponse_i;
                    system.debug('Invoice: ' + v); 
                    
                    List<Invoice> invlist = v.Invoice;
                    system.debug('Invoice: ' + invlist);
                    
                    // ================= UPDATE EXISTING INVOICE =================
                    if(invlist != null) { 
                        for(Invoice qbi : invlist) {
                            system.debug('Invoice val: ' + qbi.DocNumber);
                            //check if Invoice exists in QB, if it does, display msg
                            String synctoken = qbi.SyncToken;
                            String QBInvoiceID = qbi.ID;
                            String QBInvoiceDate = qbi.TxnDate;
                            
                            if(qbi.DocNumber == lo.name || qbi.DocNumber == lo.FreightTM__Remarks__c) {
                                
                                JSONGenerator gen = JSON.createGenerator(true);
                                gen.writeStartObject();
                                gen.writeStringField('TxnDate', QBInvoiceDate);
                                gen.writeStringField('domain', 'QBO');
                                
                                // ---------- Lines (NEW + OLD QB) ----------
                                gen.writeFieldName('Line');
                                gen.writeStartArray();
                                
                                if (lo.FreightTM__Line_Items__r != null) {
                                    for(integer i = 0; i < lo.FreightTM__Line_Items__r.size(); i++) { 
                                        string QBIDstring;
                                        if(lo.New_QB__c == True ) {
                                            QBIDstring = (lo.FreightTM__Line_Items__r[i].Product__r.LQB_QB_Id2__c != null)
                                                ? lo.FreightTM__Line_Items__r[i].Product__r.LQB_QB_Id2__c : '1';
                                        } else {
                                            QBIDstring = (lo.FreightTM__Line_Items__r[i].Product__r.LQB_QB_Id__c != null)
                                                ? lo.FreightTM__Line_Items__r[i].Product__r.LQB_QB_Id__c : '1';
                                        }
                                        
                                        decimal UnitPrice = (lo.FreightTM__Line_Items__r[i].FreightTM__Unit_Price__c != null)
                                            ? lo.FreightTM__Line_Items__r[i].FreightTM__Unit_Price__c : 0;
                                        Double Quantity = (lo.FreightTM__Line_Items__r[i].FreightTM__Number_of_Units__c != null)
                                            ? lo.FreightTM__Line_Items__r[i].FreightTM__Number_of_Units__c : 0;
                                        decimal TotalPrice = (lo.FreightTM__Line_Items__r[i].FreightTM__Amount__c != null)
                                            ? lo.FreightTM__Line_Items__r[i].FreightTM__Amount__c : 0;
                                        
                                        string ContainerSize = (lo.FreightTM__Container_Size__c != null) ? lo.FreightTM__Container_Size__c : '';
                                        string PickupFacilityCity = (lo.FreightTM__PickupFacility__r.FreightTM__City__c != null) ? lo.FreightTM__PickupFacility__r.FreightTM__City__c : '';
                                        string PickupFacilitystate = (lo.FreightTM__PickupFacility__r.FreightTM__state__c != null) ? lo.FreightTM__PickupFacility__r.FreightTM__state__c : '';
                                        string DeliveryFacilityCity = (lo.FreightTM__DeliveryFacility__r.FreightTM__City__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__City__c : '';
                                        string DeliveryFacilitystate = (lo.FreightTM__DeliveryFacility__r.FreightTM__state__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__state__c : '';
                                        
                                        gen.writeStartObject();
                                        gen.writeStringField('Description', ContainerSize + ' - ' + PickupFacilityCity + ', ' + PickupFacilitystate + ' to ' + DeliveryFacilityCity + ', ' + DeliveryFacilitystate); 
                                        gen.writeNumberField('Amount', TotalPrice);
                                        gen.writeStringField('DetailType', 'SalesItemLineDetail');
                                        gen.writeFieldName('SalesItemLineDetail');
                                        gen.writeStartObject();
                                        gen.writeFieldName('ItemRef');
                                        gen.writeStartObject();
                                        gen.writeStringField('value', QBIDstring);
                                        gen.writeStringField('name', 'Services' );
                                        gen.writeEndObject();
                                        gen.writeNumberField('UnitPrice', UnitPrice ); 
                                        gen.writeNumberField('Qty', Quantity );
                                        gen.writeFieldName('TaxCodeRef');
                                        gen.writeStartObject();
                                        gen.writeStringField('value', 'NON' );
                                        gen.writeEndObject();
                                        gen.writeEndObject();
                                        gen.writeEndObject();
                                    }
                                }
                                gen.writeEndArray();
                                
                                gen.writeBooleanField('ApplyTaxAfterDiscount', false);
                                gen.writeStringField('DocNumber', lo.name);
                                gen.writeBooleanField('sparse', false);
                                
                                
                                
                                // ---------- CustomerRef + Email ----------
                                string QBCustomerIDField = (lo.New_QB__c == True)
                                    ? lo.FreightTM__Customer__r.QBCustomerIDNEW__c
                                    : lo.FreightTM__Customer__r.QBCustomerID__c;
                                
                                string CustomerEmailAdd = (lo.FreightTM__Customer__r.Accounting_Email__c != null)
                                    ? lo.FreightTM__Customer__r.Accounting_Email__c : '';
                                
                                gen.writeFieldName('CustomerRef');
                                gen.writeStartObject();
                                gen.writeStringField('name', (lo.FreightTM__Customer__r.Name != null ? lo.FreightTM__Customer__r.Name : ''));
                                gen.writeStringField('value', QBCustomerIDField );
                                gen.writeEndObject();
                                
                                gen.writeFieldName('BillEmail');
                                gen.writeStartObject();
                                gen.writeStringField('Address', CustomerEmailAdd );
                                gen.writeEndObject();
                                
                                // ---------- ShipAddr ----------
                                string ShippingName = (lo.FreightTM__DeliveryFacility__r.Name != null) ? lo.FreightTM__DeliveryFacility__r.Name : '';
                                string ShippingStreet = (lo.FreightTM__DeliveryFacility__r.FreightTM__Street__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__Street__c : '';
                                string ShippingState = (lo.FreightTM__DeliveryFacility__r.FreightTM__State__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__State__c : '';
                                string ShippingCity = (lo.FreightTM__DeliveryFacility__r.FreightTM__City__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__City__c : '';
                                string ShippingCountry = (lo.FreightTM__DeliveryFacility__r.FreightTM__Country__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__Country__c : '';
                                string ShippingPostalCode = (lo.FreightTM__DeliveryFacility__r.FreightTM__Zip_Code__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__Zip_Code__c : '';
                                
                                gen.writeFieldName('ShipAddr');
                                gen.writeStartObject();
                                gen.writeStringField('Line1', ShippingName );
                                gen.writeStringField('Line2', ShippingStreet );
                                gen.writeStringField('City', ShippingCity );
                                gen.writeStringField('CountrySubDivisionCode', ShippingState );
                                gen.writeStringField('PostalCode', ShippingPostalCode);
                                gen.writeStringField('Country', ShippingCountry);
                                gen.writeEndObject();
                                
                                // ---------- Terms (New vs Old) ----------
                                String pt = (lo.FreightTM__Customer__r.Payment_Terms__c != null)
                                    ? String.valueOf(lo.FreightTM__Customer__r.Payment_Terms__c).trim() : null;
                                String PaymentTermsID = '1';
                                if (lo.New_QB__c == true) {
                                    Map<String, String> newQBMap = new Map<String, String>{
                                        'Due Upon Receipt' => '1',
                                            'Net 7'            => '9',
                                            'Net 10'           => '8',
                                            'Net 15'           => '2',
                                            'Net 25'           => '10',
                                            'Net 30'           => '3',
                                            'Net 45'           => '7',
                                            'Net 60'           => '4'
                                            };
                                                PaymentTermsID = (pt != null && newQBMap.containsKey(pt)) ? newQBMap.get(pt) : '1';
                                } else {
                                    Map<String, String> oldQBMap = new Map<String, String>{
                                        'Due Upon Receipt' => '100',
                                            'Net 7'            => '109',
                                            'Net 10'           => '108',
                                            'Net 15'           => '102',
                                            'Net 25'           => '110',
                                            'Net 30'           => '103',
                                            'Net 45'           => '107',
                                            'Net 60'           => '104'
                                            };
                                                PaymentTermsID = (pt != null && oldQBMap.containsKey(pt)) ? oldQBMap.get(pt) : '1';
                                }
                                
                                // ---------- Bill Ref (CustomField) ----------
                                
                                string BillReference = (lo.Bill_Reference__c != null) ? lo.Bill_Reference__c : '';
                                

                                gen.writeFieldName('CustomField');
                                gen.writeStartArray();
                                gen.writeStartObject();
                                gen.writeStringField('DefinitionId', '1' ); 
                               
                                gen.writeStringField('Name', 'Bill Ref' ); 
                                gen.writeStringField('Type', 'StringType');
                                gen.writeStringField('StringValue', BillReference );
                                
                                gen.writeEndObject();
                                gen.writeEndArray();
                                
                                gen.writeFieldName('SalesTermRef');
                                gen.writeStartObject();
                                gen.writeStringField('value', PaymentTermsID);
                                gen.writeEndObject();
                                
                                gen.writeStringField('SyncToken', synctoken);
                                gen.writeFieldName('LinkedTxn'); gen.writeStartArray(); gen.writeEndArray();
                                gen.writeStringField('Id', QBInvoiceID);
                                gen.writeEndObject();
                                
                                String jsonPayload = gen.getAsString();
                                System.debug(jsonPayload);
                                HttpRequest updateReq = new HttpRequest();
                                updateReq.setEndpoint('callout:'+NEWQB+'/invoice');
                                updateReq.setHeader('Content-Type', 'application/json');
                                updateReq.setMethod('POST');
                                updateReq.setBody(jsonPayload);
                                
                                Http http = new Http();
                                HttpResponse updateRes = http.send(updateReq);
                                
                                if (updateRes.getStatusCode() >= 200 && updateRes.getStatusCode() < 300) {
                                    qbInvoiceid = qbi.ID;
                                    if(lo.New_QB__c == True) updateLoad2(lo.Id,qbInvoiceid);
                                    else updateLoad(lo.Id,qbInvoiceid);
                                    
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Invoice has been Updated on QB.'));
                                } else {
                                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.ERROR, 'Invoice could NOT be Updated in QB. Please contact your Administrator.'));
                                    return null;
                                }
                            }
                        }  
                    }
                    
                    // ================= CREATE IF NOT FOUND =================
                    else { 
                        string Deliverydatestring;
                        date DeliveryDate;
                        if(lo.FreightTM__Delivery_Date__c != null) {
                            DeliveryDate = lo.FreightTM__Delivery_Date__c;
                            Deliverydatestring = string.valueof(DeliveryDate);
                        } else {
                            Deliverydatestring = '';
                        }
                        string TrackingNumber = (lo.FreightTM__Delivery_Remarks__c != null) ? lo.FreightTM__Delivery_Remarks__c : '';
                        
                        // ---- Terms on CREATE (prefer explicit QB ID, else map by New/Old) ----
                        String PaymentTermsID;
                        if(lo.FreightTM__Customer__r.Payment_Terms_QB_ID__c != null) {
                            PaymentTermsID = lo.FreightTM__Customer__r.Payment_Terms_QB_ID__c;
                        } else {
                            String ptCreate = (lo.FreightTM__Customer__r.Payment_Terms__c != null)
                                ? String.valueOf(lo.FreightTM__Customer__r.Payment_Terms__c).trim() : null;
                            if (lo.New_QB__c == true) {
                                Map<String, String> newQBMap = new Map<String, String>{
                                    'Due Upon Receipt' => '1',
                                        'Net 7'            => '9',
                                        'Net 10'           => '8',
                                        'Net 15'           => '2',
                                        'Net 25'           => '10',
                                        'Net 30'           => '3',
                                        'Net 45'           => '7',
                                        'Net 60'           => '4'
                                        };
                                            PaymentTermsID = (ptCreate != null && newQBMap.containsKey(ptCreate)) ? newQBMap.get(ptCreate) : '1';
                            } else {
                                Map<String, String> oldQBMap = new Map<String, String>{
                                    'Due Upon Receipt' => '100',
                                        'Net 7'            => '109',
                                        'Net 10'           => '108',
                                        'Net 15'           => '102',
                                        'Net 25'           => '110',
                                        'Net 30'           => '103',
                                        'Net 45'           => '107',
                                        'Net 60'           => '104'
                                        };
                                            PaymentTermsID = (ptCreate != null && oldQBMap.containsKey(ptCreate)) ? oldQBMap.get(ptCreate) : '1';
                            }
                        }
                        
                        string BillReference = (lo.Bill_Reference__c != null) ? lo.Bill_Reference__c : '';
                        

                        
                        JSONGenerator gen = JSON.createGenerator(true); 
                        gen.writeStartObject();
                        
                        // Bill Ref custom field
                        gen.writeFieldName('CustomField');
                        gen.writeStartArray();
                        gen.writeStartObject();
                        gen.writeStringField('DefinitionId', '1' );
                     
                        gen.writeStringField('Name', 'Bill Ref' ); 
                        gen.writeStringField('Type', 'StringType');
                        gen.writeStringField('StringValue', BillReference );
                       
                        gen.writeEndObject();
                        gen.writeEndArray();
                        
                        gen.writeFieldName('SalesTermRef');
                        gen.writeStartObject();
                        gen.writeStringField('value', PaymentTermsID);
                        gen.writeEndObject();
                        
                        // Lines
                        gen.writeFieldName('Line');
                        gen.writeStartArray();
                        if (lo.FreightTM__Line_Items__r != null) {
                            for(integer i = 0; i < lo.FreightTM__Line_Items__r.size(); i++) { 
                                string QBIDstring;
                                if(lo.New_QB__c == True ) {
                                    QBIDstring = (lo.FreightTM__Line_Items__r[i].Product__r.LQB_QB_Id2__c != null)
                                        ? lo.FreightTM__Line_Items__r[i].Product__r.LQB_QB_Id2__c : '1';
                                } else {
                                    QBIDstring = (lo.FreightTM__Line_Items__r[i].Product__r.LQB_QB_Id__c != null)
                                        ? lo.FreightTM__Line_Items__r[i].Product__r.LQB_QB_Id__c : '1';
                                }
                                
                                decimal UnitPrice = (lo.FreightTM__Line_Items__r[i].FreightTM__Unit_Price__c != null)
                                    ? lo.FreightTM__Line_Items__r[i].FreightTM__Unit_Price__c : 0;
                                Double Quantity = (lo.FreightTM__Line_Items__r[i].FreightTM__Number_of_Units__c != null)
                                    ? lo.FreightTM__Line_Items__r[i].FreightTM__Number_of_Units__c : 0;
                                decimal TotalPrice = (lo.FreightTM__Line_Items__r[i].FreightTM__Amount__c != null)
                                    ? lo.FreightTM__Line_Items__r[i].FreightTM__Amount__c : 0;
                                
                                string ContainerSize = (lo.FreightTM__Container_Size__c != null) ? lo.FreightTM__Container_Size__c : '';
                                string PickupFacilityCity = (lo.FreightTM__PickupFacility__r.FreightTM__City__c != null) ? lo.FreightTM__PickupFacility__r.FreightTM__City__c : '';
                                string PickupFacilitystate = (lo.FreightTM__PickupFacility__r.FreightTM__state__c != null) ? lo.FreightTM__PickupFacility__r.FreightTM__state__c : '';
                                string DeliveryFacilityCity = (lo.FreightTM__DeliveryFacility__r.FreightTM__City__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__City__c : '';
                                string DeliveryFacilitystate = (lo.FreightTM__DeliveryFacility__r.FreightTM__state__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__state__c : '';
                                
                                gen.writeStartObject();
                                gen.writeStringField('Description', ContainerSize + ' - ' + PickupFacilityCity + ', ' + PickupFacilitystate + ' to ' + DeliveryFacilityCity + ', ' + DeliveryFacilitystate); 
                                gen.writeNumberField('Amount', TotalPrice);
                                gen.writeStringField('DetailType', 'SalesItemLineDetail');
                                gen.writeFieldName('SalesItemLineDetail');
                                gen.writeStartObject();
                                gen.writeFieldName('ItemRef');
                                gen.writeStartObject();
                                gen.writeStringField('value', QBIDstring);
                                gen.writeStringField('name', 'Services' );
                                gen.writeEndObject();
                                gen.writeNumberField('UnitPrice', UnitPrice ); 
                                gen.writeNumberField('Qty', Quantity );
                                gen.writeFieldName('TaxCodeRef');
                                gen.writeStartObject();
                                gen.writeStringField('value', 'NON' );
                                gen.writeEndObject();
                                gen.writeEndObject();
                                gen.writeEndObject();
                            }
                        }
                        gen.writeEndArray();
                        
                        // CustomerRef / Ship / Email
                        string QBCustomerIDField = (lo.New_QB__c == True)
                            ? lo.FreightTM__Customer__r.QBCustomerIDNEW__c
                            : lo.FreightTM__Customer__r.QBCustomerID__c;
                        string CustomerEmailAdd = (lo.FreightTM__Customer__r.Accounting_Email__c != null)
                            ? lo.FreightTM__Customer__r.Accounting_Email__c : '';
                        
                        gen.writeFieldName('CustomerRef');
                        gen.writeStartObject();
                        gen.writeStringField('value', QBCustomerIDField );
                        gen.writeEndObject();
                        
                        string ShippingName = (lo.FreightTM__DeliveryFacility__r.Name != null) ? lo.FreightTM__DeliveryFacility__r.Name : '';
                        string ShippingStreet = (lo.FreightTM__DeliveryFacility__r.FreightTM__Street__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__Street__c : '';
                        string ShippingState = (lo.FreightTM__DeliveryFacility__r.FreightTM__State__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__State__c : '';
                        string ShippingCity = (lo.FreightTM__DeliveryFacility__r.FreightTM__City__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__City__c : '';
                        string ShippingCountry = (lo.FreightTM__DeliveryFacility__r.FreightTM__Country__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__Country__c : '';
                        string ShippingPostalCode = (lo.FreightTM__DeliveryFacility__r.FreightTM__Zip_Code__c != null) ? lo.FreightTM__DeliveryFacility__r.FreightTM__Zip_Code__c : '';
                        
                        gen.writeFieldName('ShipAddr');
                        gen.writeStartObject();
                        gen.writeStringField('Line1', ShippingName );
                        gen.writeStringField('Line2', ShippingStreet );
                        gen.writeStringField('City', ShippingCity );
                        gen.writeStringField('CountrySubDivisionCode', ShippingState );
                        gen.writeStringField('PostalCode', ShippingPostalCode);
                        gen.writeStringField('Country', ShippingCountry);
                        gen.writeEndObject();
                        
                        gen.writeFieldName('BillEmail');
                        gen.writeStartObject();
                        gen.writeStringField('Address', CustomerEmailAdd );
                        gen.writeEndObject();
                        
                        gen.writeStringField('DocNumber', lo.name );
                        gen.writeEndObject();
                        
                        String jsonS = gen.getAsString();
                        System.debug(jsonS);
                        HttpRequest req = new HttpRequest();
                        req.setEndpoint('callout:'+NEWQB+'/invoice');
                        req.setHeader('Content-Type','application/json'); 
                        req.setMethod('POST');
                        req.setTimeout(2000);
                        req.setbody(jsonS);
                        
                        Http http = new Http();
                        HTTPResponse res = http.send(req);    
                        
                        System.debug(res);
                        if(lo.New_QB__c == True) {
                            if ((res.getStatusCode() >= 200 && res.getStatusCode() < 300) && (lo.New_QuickBooks_ID__c == null || lo.New_QuickBooks_ID__c == '')) {
                                HttpRequest reqinv2 = new HttpRequest();
                                reqinv2.setEndpoint('callout:'+NEWQB+'/query?query=select%20%2a%20from%20Invoice%20where%20DocNumber%20%3d%20%27'+ lo.name +'%27');
                                reqinv2.setHeader('Accept','application/json');
                                reqinv2.setMethod('GET');
                                reqinv2.setTimeout(2000);
                                
                                Http httpinv2 = new Http();
                                HTTPResponse resinv2 = httpinv2.send(reqinv2);    
                                
                                String invbod = resinv2.getBody();
                                String invbod2 = invbod.replace('"BillAddr":', '"BillAddr_i":');
                                String invbod3 = invbod2.replace('"QueryResponse":', '"QueryResponse_i":');
                                String invbod4 = invbod3.replace('"MetaData":', '"MetaData_i":');
                                String invbod5 = invbod4.replace('"CurrencyRef":', '"CurrencyRef_i":');
                                String invbod_x = invbod5.replace('"time":', '"time_i":');
                                system.debug('III: ' + invbod_x);
                                
                                if (resinv2.getStatusCode() >= 200 && resinv2.getStatusCode() < 300) {
                                    InvoiceControllerUpdate.Inv invt2 = (InvoiceControllerUpdate.Inv)JSON.deserialize(invbod_x, InvoiceControllerUpdate.Inv.class);
                                    QueryResponse_i v2 = invt2.QueryResponse_i;
                                    List<Invoice> invlist2 = v2.Invoice;
                                    if (!Test.isRunningTest()) {
                                        for(Invoice qbc : invlist2) {
                                            qbInvoiceid = qbc.ID;
                                            updateLoad2(lo.Id,qbInvoiceid);
                                            ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Invoice has been added to QB.'));
                                        }
                                    }
                                }
                            } else {
                                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.ERROR, 'Invoice could NOT be created in QB. Please contact your Administrator.'));
                                return null;
                            }
                        } else {
                            if ((res.getStatusCode() >= 200 && res.getStatusCode() < 300) && (lo.LQB_QB_Id__c == null || lo.LQB_QB_Id__c == '')) {
                                HttpRequest reqinv2 = new HttpRequest();
                                reqinv2.setEndpoint('callout:'+NEWQB+'/query?query=select%20%2a%20from%20Invoice%20where%20DocNumber%20%3d%20%27'+ lo.name +'%27');
                                reqinv2.setHeader('Accept','application/json');
                                reqinv2.setMethod('GET');
                                reqinv2.setTimeout(2000);
                                
                                Http httpinv2 = new Http();
                                HTTPResponse resinv2 = httpinv2.send(reqinv2);    
                                
                                String invbod = resinv2.getBody();
                                String invbod2 = invbod.replace('"BillAddr":', '"BillAddr_i":');
                                String invbod3 = invbod2.replace('"QueryResponse":', '"QueryResponse_i":');
                                String invbod4 = invbod3.replace('"MetaData":', '"MetaData_i":');
                                String invbod5 = invbod4.replace('"CurrencyRef":', '"CurrencyRef_i":');
                                String invbod_x = invbod5.replace('"time":', '"time_i":');
                                system.debug('III: ' + invbod_x);
                                
                                if (resinv2.getStatusCode() >= 200 && resinv2.getStatusCode() < 300) {
                                    InvoiceControllerUpdate.Inv invt2 = (InvoiceControllerUpdate.Inv)JSON.deserialize(invbod_x, InvoiceControllerUpdate.Inv.class);
                                    QueryResponse_i v2 = invt2.QueryResponse_i;
                                    List<Invoice> invlist2 = v2.Invoice;
                                    if (!Test.isRunningTest()) {
                                        for(Invoice qbc : invlist2) {
                                            qbInvoiceid = qbc.ID;
                                            updateLoad(lo.Id,qbInvoiceid);
                                            ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.CONFIRM, 'Invoice has been added to QB.'));
                                        }
                                    }
                                }
                            } else {
                                ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.ERROR, 'Invoice could NOT be created in QB. Please contact your Administrator.'));
                                return null;
                            }
                        }
                    }
                } else {
                    ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.error, 'There was a query issue in QB. Please contact your Administrator.')); 
                }
            }
        }
        catch(DmlException ex){
            // ApexPages.addMessages(ex);
        }
        return null;
    }
    
    public String getName() {
        return 'InvoiceController';
    }
    
    private static void updateLoad(Id Loadid,String qbInvoiceid) {
        FreightTM__Load__c Loadrecupdate = new FreightTM__Load__c(Id = Loadid,Invoice_Sent_to_QB__c = true,LQB_QB_Id__c =qbInvoiceid,LQB_QB_Error__c = 'Success');
        update Loadrecupdate;
    }
    
    private static void updateLoad2(Id Loadid,String qbInvoiceid) {
        FreightTM__Load__c Loadrecupdate2 = new FreightTM__Load__c(Id = Loadid,Invoice_Sent_to_QB__c = true,New_QuickBooks_ID__c =qbInvoiceid,LQB_QB_Error__c = 'Success');
        update Loadrecupdate2;
    }
    }

Step 6: Automatic Balance Updates

FTM automatically syncs outstanding balances from QuickBooks every 15 minutes. This ensures your records remain up to date without manual intervention.

Troubleshooting

  • If the invoice is not showing a QuickBooks ID after syncing, refresh the page and ensure the Customer QB ID was present beforehand.
  • If using sandbox and it looks different from the guide, switch between classic and modern views using your profile icon (top right).
  • If credentials or verification codes are required for access, please coordinate with your IT administrator.

Questions

If you need help at any step, please reach out to your integration engineer or contact us at [email protected]

Leave a Reply

Your email address will not be published. Required fields are marked *


Let's Talk!

Thanks for stopping by! We're here to help, please don't hesitate to reach out.

Watch a Demo