Skip to content

Repository files navigation

Integration of Excel calculations with Salesforce

This repository contains a Salesforce project that integrates with the SpreadsheetWeb API to perform Excel calculations. It features an Excel spreadsheet for calculating loan payments and generating an amortization schedule, though it can be applied to any Excel file containing worksheet formulas. The project includes Apex classes, a Visualforce page, and necessary configuration to perform API calls and display the results in a user-friendly manner.

Components

Apex Classes

  • CalculationController2: Handles the loan calculation logic and prepares data for the Visualforce page.
  • CalculationService2: Manages API requests to SpreadsheetWeb.
  • TokenService: Handles token retrieval for API authentication.

Visualforce Page

  • LoanCalculatorPage: Collects user input and displays the loan calculation results.

Setup and Configuration

Prerequisites

  • Salesforce Developer Org
  • SpreadsheetWeb Account with access to the API

Salesforce Setup

  1. Apex Classes:

    • Navigate to Setup > Apex Classes.
    • Create new Apex classes with the content provided in the CalculationController2, CalculationService2, and TokenService sections below.
  2. Visualforce Page:

    • Navigate to Setup > Visualforce Pages.
    • Create a new Visualforce page with the content provided in the LoanCalculatorPage section below.
  3. Configuration:

    • Update the workspaceId, applicationId, clientId, and clientSecret in the Apex classes with your SpreadsheetWeb credentials.

Example Configuration

// In CalculationController2StringworkspaceId='YourWorkspaceID';
StringapplicationId='YourApplicationID';
// In TokenServiceStringclientId='YourClientID';
StringclientSecret='YourClientSecret';

Usage

  1. Navigate to the Visualforce Page:

    • Open the newly created Visualforce page (LoanCalculatorPage) in your Salesforce org.
  2. Enter Loan Details:

    • Input the loan amount, interest rate, loan period, and start date.
  3. Calculate:

    • Click the "Calculate" button to perform the calculation.
  4. View Results:

    • The scheduled payment amount and amortization chart will be displayed below the input form.

Detailed Explanation of Components

CalculationController2

The CalculationController2 Apex class handles user inputs, prepares API requests, processes API responses, and stores the results for display on the Visualforce page.

Key Methods:

  • calculate(): Prepares input data, makes an API call, and processes the response to extract the scheduled payment amount and amortization chart.
publicclassCalculationController2 {
publicStringloanAmount { get; set; }
publicStringinterestRate { get; set; }
publicStringloanPeriod { get; set; }
publicStringloanStartDate { get; set; }
publicStringscheduledPaymentAmt { get; set; }
publicList<AmortizationRow> amortizationChart { get; set; }
publicStringrequestBody { get; set; }
publicStringresponseBody { get; set; }
publicStringrawResponse { get; set; }
publicCalculationController2() {
amortizationChart=newList<AmortizationRow>();
}
publicvoidcalculate() {
List<CalculationService.CalculationInput> inputs=newList<CalculationService.CalculationInput>();
CalculationService.CalculationInputloanAmountInput=newCalculationService.CalculationInput();
loanAmountInput.reference='loan_amount';
loanAmountInput.value=newList<List<Map<String, String>>>{ newList<Map<String, String>>{ newMap<String, String>{ 'value'=>loanAmount }}};
inputs.add(loanAmountInput);
CalculationService.CalculationInputinterestRateInput=newCalculationService.CalculationInput();
interestRateInput.reference='interest_rate';
interestRateInput.value=newList<List<Map<String, String>>>{ newList<Map<String, String>>{ newMap<String, String>{ 'value'=>interestRate }}};
inputs.add(interestRateInput);
CalculationService.CalculationInputloanPeriodInput=newCalculationService.CalculationInput();
loanPeriodInput.reference='loan_period';
loanPeriodInput.value=newList<List<Map<String, String>>>{ newList<Map<String, String>>{ newMap<String, String>{ 'value'=>loanPeriod }}};
inputs.add(loanPeriodInput);
CalculationService.CalculationInputloanStartDateInput=newCalculationService.CalculationInput();
loanStartDateInput.reference='loan_start_date';
loanStartDateInput.value=newList<List<Map<String, String>>>{ newList<Map<String, String>>{ newMap<String, String>{ 'value'=>loanStartDate }}};
inputs.add(loanStartDateInput);
List<String> outputs=newList<String>{ 'scheduled_payment_amt', 'amortization_chart' };
try {
StringworkspaceId='YourWorkspaceID';
StringapplicationId='YourApplicationID';
CalculationService.CalculationRequestcalculationRequest=newCalculationService.CalculationRequest(workspaceId, applicationId, inputs, outputs);
requestBody=JSON.serialize(calculationRequest);
CalculationService.CalculationResponseresponse=CalculationService.calculate(workspaceId, applicationId, inputs, outputs);
responseBody=JSON.serialize(response);
rawResponse=response.rawResponse;
Map<String, Object> rawResponseMap= (Map<String, Object>) JSON.deserializeUntyped(rawResponse);
Map<String, Object> responseMap= (Map<String, Object>) rawResponseMap.get('response');
Map<String, Object> responseInnerMap= (Map<String, Object>) responseMap.get('response');
Map<String, Object> outputMap= (Map<String, Object>) responseInnerMap.get('output');
Map<String, Object> calculationMap= (Map<String, Object>) outputMap.get('calculation');
if (calculationMap!=null&&calculationMap.containsKey('outputs')) {
List<Object> outputsList= (List<Object>) calculationMap.get('outputs');
for (ObjectoutputObj:outputsList) {
Map<String, Object> outputItem= (Map<String, Object>) outputObj;
if (outputItem.get('reference') =='scheduled_payment_amt') {
List<Object> valueList= (List<Object>) outputItem.get('value');
if (valueList!=null&&!valueList.isEmpty()) {
List<Object> innerValueList= (List<Object>) valueList.get(0);
if (innerValueList!=null&&!innerValueList.isEmpty()) {
Map<String, Object> valueItem= (Map<String, Object>) innerValueList.get(0);
scheduledPaymentAmt= (String) valueItem.get('text');
}
}
} elseif (outputItem.get('reference') =='amortization_chart') {
List<Object> valueList= (List<Object>) outputItem.get('value');
if (valueList!=null&&!valueList.isEmpty()) {
for (Integeri=1; i<valueList.size(); i++) { // Skip header rowList<Object> rowList= (List<Object>) valueList.get(i);
AmortizationRowrow=newAmortizationRow();
row.Year= (String) ((Map<String, Object>) rowList.get(0)).get('text');
row.Remaining= (String) ((Map<String, Object>) rowList.get(1)).get('text');
row.InterestPaid= (String) ((Map<String, Object>) rowList.get(2)).get('text');
row.PrincipalPaid= (String) ((Map<String, Object>) rowList.get(3)).get('text');
amortizationChart.add(row);
}
}
}
}
} else {
scheduledPaymentAmt='Error calculating scheduled payment amount. Full response: '+responseBody;
}
} catch (Exceptione) {
responseBody=e.getMessage();
scheduledPaymentAmt='Request: '+requestBody+' | Response: '+rawResponse+' | Error: '+responseBody;
}
}
publicclassAmortizationRow {
publicStringYear { get; set; }
publicStringRemaining { get; set; }
publicStringInterestPaid { get; set; }
publicStringPrincipalPaid { get; set; }
}
}

CalculationService2

The CalculationService2 Apex class manages the interaction with the SpreadsheetWeb API, including constructing API requests and handling responses.

Key Methods:

  • calculate(): Makes a call to the SpreadsheetWeb API with the provided inputs and returns the calculation results.
publicclassCalculationService2 {
publicclassCalculationInput {
publicStringreference;
publicList<List<Map<String, String>>> value;
}
publicclassCalculationRequest {
publicMap<String, Object> request;
publicCalculationRequest(StringworkspaceId, StringapplicationId, List<CalculationInput> inputs, List<String> outputs) {
Map<String, Object> calculation=newMap<String, Object>();
calculation.put('inputs', inputs);
calculation.put('outputs', outputs);
Map<String, Object> input=newMap<String, Object>();
input.put('calculation', calculation);
Map<String, Object> innerRequest=newMap<String, Object>();
innerRequest.put('input', input);
Map<String, Object> outerRequest=newMap<String, Object>();
outerRequest.put('workspaceId', workspaceId);
outerRequest.put('applicationId', applicationId);
outerRequest.put('request', innerRequest);
this.request=outerRequest;
}
}
publicclassOutputValue {
publicStringtype;
publicStringformatType;
publicStringformat;
publicStringtext;
publicStringvalue;
publicStringoverwrite;
}
publicclassOutput {
publicStringreference;
publicList<List<OutputValue>> value;
publicStringoverwrite;
}
publicclassCalculation {
publicBooleansuccess;
publicList<Output> outputs;
publicList<String> validations;
publicList<String> messages;
}
publicclassOutputResponse {
publicBooleansuccess;
publicCalculationcalculation;
publicStringgoalSeek;
publicStringsolver;
}
publicclassResponse {
publicStringapplicationId;
publicOutputResponseresponse;
publicStringsaveResult;
publicIntegerusedTransactionSequenceId;
publicStringrequestId;
publicBooleansuccess;
publicStringeventCreationDate;
publicStringretryIndex;
publicStringdebugRetryAllowFailureCount;
}
publicclassCalculationResponse {
publicResponseresponse;
publicMap<String, Double> timingsSeconds;
publicStringperformanceInformation;
publicBooleanisError;
publicList<String> messages;
publicStringrawResponse;
}
publicstaticCalculationResponsecalculate(StringworkspaceId, StringapplicationId, List<CalculationInput> inputs, List<String> outputs) {
Stringtoken=TokenService.getBearerToken();
if(token==null) {
thrownewCalloutException('Token is null. Cannot proceed with the API call.');
}
Httphttp=newHttp();
HttpRequestrequest=newHttpRequest();
request.setEndpoint('https://api.spreadsheetweb.com/calculations/calculatesingle');
request.setMethod('POST');
request.setHeader('Authorization', 'Bearer '+token);
request.setHeader('Content-Type', 'application/json');
CalculationRequestcalculationRequest=newCalculationRequest(workspaceId, applicationId, inputs, outputs);
Stringbody=JSON.serialize(calculationRequest);
request.setBody(body);
HttpResponseresponse=http.send(request);
if (response.getStatusCode() ==200) {
StringresponseBody=response.getBody();
CalculationResponsecalculationResponse= (CalculationResponse) JSON.deserialize(responseBody, CalculationResponse.class);
calculationResponse.rawResponse=responseBody;
returncalculationResponse;
} else {
thrownewCalloutException('Failed to perform calculation: '+response.getBody());
}
}
}

TokenService

The TokenService Apex class retrieves an access token from the SpreadsheetWeb identity service, which is used to authenticate API requests.

Key Methods:

  • getBearerToken(): Sends a request to the SpreadsheetWeb identity service to retrieve an access token.
publicclassTokenService {
publicclassTokenResponse {
publicStringaccess_token;
publicStringtoken_type;
publicIntegerexpires_in;
}
publicstaticStringgetBearerToken() {
Httphttp=newHttp();
HttpRequestrequest=newHttpRequest();
request.setEndpoint('https://identity.spreadsheetweb.com/connect/token');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/x-www-form-urlencoded');
StringclientId='YourClientID';
StringclientSecret='YourClientSecret';
Stringbody='grant_type=client_credentials&client_id='+clientId+'&client_secret='+clientSecret;
request.setBody(body);
HttpResponseresponse=http.send(request);
if (response.getStatusCode() ==200) {
TokenResponsetokenResponse= (TokenResponse) JSON.deserialize(response.getBody(), TokenResponse.class);
returntokenResponse.access_token;
} else {
thrownewCalloutException('Failed to get token: '+response.getBody());
}
}
}

Visualforce Page (LoanCalculatorPage)

The Visualforce page provides a user interface for inputting loan details and displaying the results.

<apex:pagecontroller="CalculationController2"><apex:form><apex:pageBlocktitle="Loan Calculator"><apex:pageBlockSectiontitle="Loan Calculator" columns="2"><apex:inputTextvalue="{!loanAmount}" label="Loan Amount"/><apex:inputTextvalue="{!interestRate}" label="Interest Rate"/><apex:inputTextvalue="{!loanPeriod}" label="Loan Period"/><apex:inputTextvalue="{!loanStartDate}" label="Loan Start Date"/><apex:commandButtonvalue="Calculate" action="{!calculate}"/></apex:pageBlockSection></apex:pageBlock><apex:pageBlocktitle="Scheduled Payment Amount"><apex:outputTextvalue="{!scheduledPaymentAmt}" rendered="{!scheduledPaymentAmt != null}"/></apex:pageBlock><apex:pageBlocktitle="Amortization Chart"><apex:pageBlockTablevalue="{!amortizationChart}" var="row" rendered="{!amortizationChart != null}"><apex:columnvalue="{!row.Year}" headerValue="Year"/><apex:columnvalue="{!row.Remaining}" headerValue="Remaining"/><apex:columnvalue="{!row.InterestPaid}" headerValue="Interest Paid"/><apex:columnvalue="{!row.PrincipalPaid}" headerValue="Principal Paid"/></apex:pageBlockTable></apex:pageBlock></apex:form></apex:page>

Example Charts

Here are examples of how the amortization chart and scheduled payment amount will be displayed:

Scheduled Payment Amount: Scheduled Payment Amount: $2,684

Amortization Chart

Here is an example of how the amortization chart will be displayed:

YearRemaining BalanceInterest PaidPrincipal Paid
0$500,000$2,083$1,601
1$480,344$26,555$21,338
2$459,683$50,017$42,086
3$437,965$72,417$63,895
............

Conclusion

This project demonstrates a practical integration between Salesforce and the SpreadsheetWeb API for loan calculations. By following the setup instructions and using the provided components, you can easily implement similar functionality in your own Salesforce org.

For further assistance or questions, please refer to the SpreadsheetWeb API documentation or contact support.

About

This repository contains a Salesforce project that integrates with the SpreadsheetWeb API to perform Excel calculations. It features an Excel spreadsheet for calculating loan payments and generating an amortization schedule, though it can be applied to any Excel file containing worksheet formulas. The project includes APEX classes.

Topics

Resources

Stars

1 star

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages