How to create an integration using CRM connector
![]() | This feature is available starting from 3.0PR20Q3. |
Contents |
Introduction
This documents describes how to create an integration with an external system using the new feature CRM Connector available in 20Q3.
To do that we will use this gitlab repository as a base
Different approaches to implement an integration with CRM
There are 2 different ways to implement an integration with CRM system. Below we analyze both.
Web Service integration
It is the recommended one because Openbravo server is removed from the equation whne communication with the CRM system happens. It is important to ensure that CRM server should support [CORS https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS] headers to allow cross-domain communication.
Use Openbravo as a proxy
In that case, all the request to the CRM API will pass through Openbravo Server. This is interesting in case that your integration does not allow CORS or if you need to generate data in openbravo or manipulate the data received by CRM API. However, using this approach, Openbravo Server will require more resources, that's why it is recommended to prioritize "Web Server" approach
Integration Module Source Code
Sample module implements the 2 approaches explained above, however we will focus in this case in proxy one because Openbravo Server acts as a external Web Service build inside Openbravo. This is a good introduction for both approaches.
- Web Service: Code in Openbravo server will be removed and executed by CRM API
- Proxy: Code which currently returns data in Openbravo Server should redirect request to the external CRM API
Component provider implementation
Since a relevant part of CRM integration is written in javascript, a component provider should be created to inject into Openbravo Web POS source code the files that are part of this module.
Here you can check the component provider.
In the case of that module 3 javascript files and 1 css are added
- SampleBpIntegration: Implements the API functions required by our integration, based on Web Service paradigm. You can use it as a reference if you are implementing this kind of approach, but we recommend to be focused on the implementation done for proxy.
- SampleBPIntegrationProxy: Implements the API functions required by our integration, based on Proxy paradigm.
- SampleBpIntegrationDqm: This file contains our own Data Quality Manager that we have developed for this module. In this file the validations executed when a business partner is created/updated are written.
- extbps_styles: Implements some css classes to give a custom aspect to some UI elements
Dataset
A dataset with all the information ready to be imported to window "CRM Configuration Window" is part of this module. We recommend to use a dataset to store this information. Doing that tasks like import/export API configuration will be easier and secure.
Here you have more details about how to create a dataset and how to import a dataset
To continue with this how-to it is needed to import the dataset which is already included in this module. (If your instance was created using Openbravo Sample data for modules this step is not required)
After applying the dataset at client level (*) using Enterprise_module_management window you should be able to see the data in "CRM Connector Configuration" window (see image below)
The final configuration step is to enable and define the CRM connector in the Client definition. This is done inside the Client's Information tab.
CRM connector javascript API
To implement your custom CRM integration the class ExternalBusinessPartnerRepository should be extended
class SampleBpIntegrationProxy extends OB.App.Class .ExternalBusinessPartnerRepository {
There are some methods that can be (or not, depending on CRM needs) implemented.
Basic CRUD operations
- async getBusinessPartner(id, config)
- Retrieves a single business partner using an identifier value
- @param {string} id - The identifier of the business partner
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<Object>} - The resulting business partner as a plain JSON object.
- Retrieves a single business partner using an identifier value
![]() |
In this module, an ajax call was implemented to retrieve an object from CRM API. Check code here |
- async getBusinessPartners(filters, config)
- Returns a list of business partners using the given criteria
- @param {object} filters - An object containing the filter as the key and the query value.
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<Object[]>} - The resulting list of business partner matching the given filter
- Returns a list of business partners using the given criteria
![]() | In this module, an ajax call was implemented to retrieve an array of results from CRM API. Check code here |
- async insertBusinessPartner(businessPartner, config)
- Inserts a new customer in the external system
- @param {Object} businessPartner - An object containing all business partner's data
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<Object>} - Returns a copy of the inserted object
- Inserts a new customer in the external system
![]() | In this module, an ajax call was implemented to send the Object to the CRM API and create the new customer. Check code here |
- async updateBusinessPartner(businessPartner, config)
- Updates an existing customer with the provided data
- @param {Object} businessPartner - An object containing at least the BP identifier and the data to be updated.
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<Object>} - Returns a copy of the updated object
- Updates an existing customer with the provided data
![]() | In this module, an ajax call was implemented to send the Object to the CRM API and update the customer. Check code here |
- async getDefaultBusinessPartnerForNew(config)
- This function can be called to receive a default object to be used for insert
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<Object>} - A plain js object with the information of the new ExternalBusinessPartner candidate
- This function can be called to receive a default object to be used for insert
![]() | In this module, an ajax call was implemented to retrieve from API the defaukt values to be shown in the form when a customer is going to be created. Check code here |
User interaction operations
- async onPropertyValueChange(businessPartner, property, config)
- This function can be implemented to modify the property values when a property value changes
- @param {ExternalBusinessPartner} businessPartner
- @param {Object} property - The property object that changes
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<ExternalBusinessPartner>} - A copy of the received ExternalBusinessPartner with changes applied
- This function can be implemented to modify the property values when a property value changes
![]() | In this module, this method was implemented to show in the UI properties based on the value inserted in a property. Being more concrete, we have a property defined as boolean called "Athlete". When this property is filled, some additional properties appear in the form. Check code here |
![]() | In this module, this method was implemented to autofill some properties when the value of other property changes. Being more concrete, when the name provided is GAL, using asynchrony (simulated with a promise and setTimeout) values for other properties are retrieved and filed. Check code here |
- async onViewDetailsBpViewLoad(businessPartner, config)
- This function can be implemented to modify the property values of the Business Partner when it is loaded in view mode
- @param {ExternalBusinessPartner} businessPartner
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<ExternalBusinessPartner>} - A copy of the received ExternalBusinessPartner with changes applied
- This function can be implemented to modify the property values of the Business Partner when it is loaded in view mode
![]() | In this module, this method was implemented to add a value for a property defined as combo. Doing it, instead of show the value of the combo we show the label.Check code here |
- async onEditBpViewLoad(businessPartner, config)
- This function can be implemented to modify the property values of the Business Partner when it is loaded in edit mode
- @param {ExternalBusinessPartner} businessPartner
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<ExternalBusinessPartner>} - A copy of the received ExternalBusinessPartner with changes applied
- This function can be implemented to modify the property values of the Business Partner when it is loaded in edit mode
![]() | In this module, this method was implemented to add options for a property defined as combo. We are loading country regions from a mocked external datasource and inserting them as options for a certain property.Check code here |
- async onInsertBpViewLoad(businessPartner, config)
- This function can be implemented to act over the component which draws the results after a search operation
- @param {ExternalBusinessPartner} businessPartner
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<ExternalBusinessPartner>} - A copy of the received ExternalBusinessPartner with changes applied
- This function can be implemented to act over the component which draws the results after a search operation
![]() | In this module, this method was implemented to add options for a property defined as combo. We are loading country regions from a mocked external datasource and inserting them as options for a certain property. Apart from that, based on the default values we are showing some properties which by default are hidden. Check code here |
- async onBpListItemViewLoad(businessPartner, bpLineUiComponents)
- This function can be implemented to modify the property values of the Business Partner when it is loaded in insert mode
- @param {ExternalBusinessPartner} businessPartner
- @param {Object} bpLineUiComponents - An object containing the enyo component line
- @return {Promise<ExternalBusinessPartner>} - A copy of the received components array suplied
- This function can be implemented to modify the property values of the Business Partner when it is loaded in insert mode
![]() | In this module, this method was implemented to emphasize results which are marked as VIP. In that results we are adding a component to reflect it.Check code here |
- async onBusinessPartnerSelected
- This Function to be implmented to execute actions when a business partner is selected for an order
- @param {Object} businessPartner - The business partner object as a plain javascript object.
- @param {Object} order - The Order as a plain javascript object.
- @param {Object} config - An object containing the CRM configuration
- @return {Promise<void>
- This Function to be implmented to execute actions when a business partner is selected for an order
![]() | In this module, this method was implemented to add a property to the order when the BP is assigned to the order Check code here |
DQM - Data Quality Managment
CRM connector is ready to be extended by adding a custom DQM provider. This document explains How to create a data quality management provider.
After creating it, it should be configured at the organization level (see image above) adding the desired priority to the created DQM provider.
![]() | In this module, we have created validations for 2 properties (xEmail and xName) using an asynchronous approach for xName and synchronous approach for xEmail.Check code here |
Offline Support
![]() | This feature is available starting from 23Q4. |
It is possible to configure the CRM connector to work in offline (without network connectivity with the CRM server) as long as the implementation provides support for it.
Implementation Support
Working with a CRM that support offline has some important implications:
- The customer creation is an asynchronous process. When creating a new customer, a new message is created in the application state that will be eventually processed and synchronized with the CRM using the insertBusinessPartner method of the CRM connector javascript API. The response of the external system is not handled: this is a fire and forget mechanism.
- The value of the key property for the newly created customers is always generated in the Openbravo side. By default this value is a UUID. But the mechanism for generating key values can be overridden by the CRM implementation using the generateKey method:
- generateKey()
- Generates the key of the default object created for insert when the CRM configuration indicates that the CRM supports working in offline
- @return {string} The value for the key property
- Generates the key of the default object created for insert when the CRM configuration indicates that the CRM supports working in offline
Besides, to support working in offline with the CRM connector, the implementation must meet some conditions:
- The implementation must ensure that the customer creation form can be built properly in offline mode, without the data that is supposed to be loaded with network requests coming from the external CRM system.
- All the network requests done by the CRM connector should use the Request API because this way the infrastructure will be able to control the connectivity status with the CRM server.
- A default remote server implementation is provided to manage the online/offline state of the CRM server. But if a more complex logic than the default one is required by the CRM, this class can be extended to provide an ad-hoc remote server.
The remote server implementation is a class that extends OB.App.Class.CRMServer and implements the following methods:
- isAttendedURL(url)
- Identifies the requests that are done to the CRM server
- @param {string} url - The URL of a request fired in the system
- @return {boolean} true if the if it is URL of the CRM server. Otherwise false is returned.
- Identifies the requests that are done to the CRM server
- async checkServerAvailability()
- Pings the CRM server to identify if it is online or offline
- @return {boolean} true if CRM server is online. Otherwise false is returned.
- Pings the CRM server to identify if it is online or offline
- async handleRequestError()
- Determines the action to be done when a request to attended URL fails. By default the CRM server will start the transition to offline process. But this default behavior can be extended overriding this method. For example, we usually do not want to start the transition to offline if the ping request fails, because it is just a request we use to identify the status of the server.
- @param {string} url - The URL of the failed request
- @param {object} response - The response of the failed request
- Determines the action to be done when a request to attended URL fails. By default the CRM server will start the transition to offline process. But this default behavior can be extended overriding this method. For example, we usually do not want to start the transition to offline if the ping request fails, because it is just a request we use to identify the status of the server.
Once defined, the CRM server must be also registered:
OB.App.RemoteServerController.registerRemoteServer(new OB.App.Class.MyCRMServer(), { priority: 50 });
See here to see the implementation and registration of a sample CRM remote server.
Configuration
Once the CRM implementation supports working in offline, the CRM can be configured to work in that way. This is done in the CRM Connector Configuration window, with the Connectivity Type field, the Online and Offline option must be selected.