Technical¶
Components¶
ERP Connector consists of the following components:
- Data structure (existing + new tables and fields) in Lime CRM.
- Lime Bootstrap apps.
- Endpoints.
- Web component.
- Syncify's on-premise service.
- Syncify's cloud service.
Table and Field Definitions Lime CRM¶
These tables and fields must be created before you can use the ERP Connector.
Company¶
On the company card, some fields included in the Lime CRM Base Solution are used. Please note that database field names are hardcoded in Syncify's migration scripts and Syncify's default integration field mappings. This means that if the fields are not named as ERP Connector expects them to be you have to notify Syncify.
The following fields are expected to exist:
Table database name: company
Database Field Name | Field Type |
---|---|
name | Text field |
registrationno | Text field |
coworker | Relation field |
postaladdress1 | Text field |
postaladdress2 | Text field |
postalzipcode | Text field |
postalcity | Text field |
country | Text field |
phone | Text field |
visitingaddress1 | Text field |
visitingaddress2 | Text field |
visitingzipcode | Text field |
visitingcity | Text field |
The following fields are added by ERP Connector.
Database Field Name | Field Type | Length | Field Label | Description |
---|---|---|---|---|
invoiceaddress1 | Text field | 72 | N/A | Populated by the integration. |
invoiceaddress2 | Text field | 72 | N/A | Populated by the integration. |
invoicezipcode | Text field | 10 | N/A | Populated by the integration. |
invoicecity | Text field | 72 | N/A | Populated by the integration. |
fullinvoiceaddress | Text field | 256 | N/A | SQL for update, hidden on forms. |
erp_id | Text field | 16 | Key | The integration key. Set label key to avoid duplicates. |
erp_status | Option field | N/A | N/A | Status to keep track of the sync process. Readonly. |
erp_errormessage | Text field | 512 | N/A | Populated by the integration on error. Readonly. |
emailinvoice | Link field | 100 | N/A | Populated by the integration. |
erp_firstsynced | Time field | N/A | N/A | Populated by the integration first time an object is synced. Readonly, Null allowed, no default value. |
erp_lastsynced | Time field | N/A | N/A | Populated by the integration every time an object is synced. Readonly, Null allowed, no default value. |
erp_turnover_yearnow | Decimal | N/A | N/A | Populated by the sales trend calculation task. |
erp_turnover_lastyear | Decimal | N/A | N/A | Populated by the sales trend calculation task. |
erp_turnover_lastyeartodate | Decimal | N/A | N/A | Populated by the sales trend calculation task. |
erp_salestrend | Option field | N/A | N/A | Calculated trend based on turnover, populated by the sales trend calculation task. Readonly. |
erp_ytdgrowth | Decimal | N/A | N/A | Calculated growth in % based on turnover, populated by the sales trend calculation task. Readonly. |
erp_connector_graph | HTML (tab) | N/A | N/A | HTML tab for showing the graph for paid invoices the last 5 years. Only for the Lime CRM Desktop Client. |
invoice | Relation (tab) | N/A | N/A | Populated by the integration. |
invoicerow | Relation (tab) | N/A | N/A | Populated by the integration. |
Required Fields¶
Having a field in Lime CRM mapped to the name
property in the ERP systems' API:s is required.
Invoice¶
Invoices are connected to the company using erp_id
when generated in Lime CRM from the ERP system. The following table and fields are added by ERP Connector:
Table database name: invoice
Database Field Name | Field Type | Length | Description |
---|---|---|---|
invoice_number | Text field | 64 | Unique invoice number |
company | Relation (field) | 256 | Relation to company |
customerid | Text field | 128 | Customer ID for matching to company |
customer_reference | Text field | N/A | Customer reference from ERP |
coworker | Relation (field) | N/A | Company reference (Our reference) |
invoice_type | Text field | 64 | Invoice type |
invoice_date | Time (date) | N/A | When invoice is booked |
invoice_expires | Time (date) | N/A | When invoice expires |
invoice_sum | Decimal | N/A | Sum ex VAT (moms) |
invoice_vat | Decimal | N/A | VAT (moms) |
invoice_total_sum | Decimal | N/A | Sum incl VAT |
currency | Text field | 32 | Currency |
conversionrate | Decimal | N/A | Conversion rate |
invoice_balance | Decimal | N/A | Invoice balance (saldo) |
paid | Yes/no | N/A | Yes if it is paid |
paid_date | Time (date) | N/A | The date when payment is registered |
invoice shredded | Yes/no | N/A | Yes if the payment is shredded (makulerad) |
invoicerow | Relation (tab) | N/A | All invoice rows belonging to the invoice |
Invoice Row¶
Invoice rows are connected to an invoice using the invoice_number
. The following table and fields are added by ERP Connector:
Table database name: invoicerow
Database Field Name | Field Type | Length | Description |
---|---|---|---|
invoice | Relation (field) | N/A | Relation to invoice |
company | Relation (field) | N/A | Relation to company |
item | Text field | 128 | Item |
description | Text field | 128 | Description of Items |
units | Decimal | N/A | Number of items |
row_margin | Decimal | N/A | Contribution margin |
row_value | Decimal | N/A | Sum of row |
rowid | Text field | 32 | Used to make a row unique (hidden) |
position | Integer | N/A | Position of the invoice row |
Lime Bootstrap Apps¶
These are only used when using the ERP Connector from the Lime CRM Desktop Client.
The first Lime Bootstrap app is for the button to send a new customer to the ERP system, as well as showing the current sync status. It is using the same backend as the web client counterpart. The second LBS app is for the invoice overview graph shown in the HTML tab on the company card.
Web Component¶
This is only used when using the ERP Connector from the Lime CRM Web Client.
ERP Status¶
Will show the button to send company information to the ERP system, as well as the current sync status.
Sales Trend¶
Will visualize the turnover growth this year compared to the same period last fiscal year for each company in the table view.
Syncify Services¶
Syncify has built two different services. One for cloud based ERP systems and one for on-premise ERP Systems. It is these two services that connect to the ERP systems' API.
If the ERP system is installed on-premise, the Syncify service must be installed and configured on the ERP system server (or a server with access to the ERP server). Syncify is responsible for installations of their service.
If the ERP system is cloud based there is no need for a local Syncify service. The integration is instead registered in Syncify's cloud service through their web portal. This is also done by Syncify.
The Syncify services connects to the ERP systems as described below. Some ERP systems are cloud based and some are installed on-premise.
ERP system | Connects through | cloud | on-premise |
---|---|---|---|
BL Administration | ? | ✔️ | |
e-conomic | REST API | ✔️ | |
Exact Online | ? | ✔️ | |
Fortnox | REST API | ✔️ | |
Maconomy | ? | ||
Business Central | cloud: REST API and OData, on-premise: Webservice (SOAP) | ✔️ | ✔️ |
Microsoft Dynamics NAV | Webservice (SOAP) | ✔️ | |
Monitor G5 | ? | ✔️ | ✔️ |
Power Office Go | ? | ✔️ | |
Tripletex | ? | ✔️ | |
Uniconta | ? | ✔️ | |
Visma Administration | adk.dll | ✔️ | |
Visma Business | VBS (Visma Business Services) | ✔️ | |
Visma eEkonomi | ? | ✔️ | |
Visma Global | ? | ✔️ | |
Visma.Net | REST API | ✔️ | |
Visma Nova | ? |
Lime Services¶
The add-on consumes four different services in the Lime server. The most obvious service is the Web server, but the Event handler, Task handler and Task scheduler are also involved. In many cases several services are used in the same flow, meaning that logs also will be split depending on what service executed the particular part of the flow. The flows with their corresponding services are described below.
User uses WC/LBS app to set limeobject ready for first sync¶
When the button in the app is pressed to set "Ready for first sync", a request goes to a custom endpoint to set the ERP status. Only the Web server is involved.
Limeobject is updated by user or other integration than Syncify¶
The add-on is continously looking for updated limeobjects using the Event handler. Each time a limeobject is updated, the event handler checks whether the limetype of that limeobject is configured in any ERP instance(s). If there's a match on the limetype, a check whether any of the properties that are configured as propertiesSynced
in these instances determines if that should lead to an ERP status update. If that's the case, an update instance status task is sent to the Task handler that eventually will update the limeobject's ERP status(es) asynchronously. Notes:
- All limeobject updates triggered by the erpsync@lime user are ignored by the event handler.
- Rules for the ERP status flow are here.
- Due to the large amount of update events, the run time configuration is cached to avoid loading it from the database at each event.
Limeobjects are fetched by Syncify¶
Syncify uses custom endpoints to get data. The endpoints are using Lime query and they're excuted synchronously on the Web server.
Limeobjects are updated by Syncify¶
Syncify uses custom endpoints to update/insert data. The Web server receives the payload and directly delegates all work to a task that will be executed asynchronously by the Task handler. The endpoint returns a task id so Syncify can poll the status of the task.
Sales trend is calculated¶
If any sales trend instances are configured, the Task scheduler will pick them up and run a daily calculations task at the time configured in the Environment Config. If the current date is the date that the fiscal year starts, a yearly reset function will be run prior to the daily calculations in the same scheduled task.
Endpoints¶
Objects¶
/<string:limetype_name>/
Gets all objects based on limetype. Useful for getting data for migrations.
Limit number of results with optional limit
arg.
Start from a specific id with optional offset
arg.
Properties can be requested in the payload (lime_query syntax).
Example:
/deal/?limit=2&offset=1004
Payload:
{
"name": "",
"value": "",
"coworker": {
"name": "",
"office": {
"name": ""
}
}
}
Response:
{
"objects": [
{
"_id": 1004,
"_limetype": "deal",
"name": "Ice cream",
"value": 50000,
"coworker": {
"_id": 1010,
"_limetype": "coworker",
"name": "Terry I",
"office": null
}
},
{
"_id": 1005,
"_limetype": "deal",
"name": "Meat",
"value": 299,
"coworker": {
"_id": 1023,
"_limetype": "coworker",
"name": "Carlito Calzone",
"office": {
"_id": 4003,
"_limetype": "building",
"name": "Havana",
}
}
},
],
"next": 1006
}
next
can be used as offset for the next request. If no more objects are available, next is null.
Objects from limetype and erp_id property¶
GET¶
/<string:limetype_name>/<string:erp_id_name>/
Gets all objects based on limetype and the name of the property that has the erp_id. This combination must exist as an instance in the configuration. This endpoint should be used to get objects based on status.
Filter on status with optional status
arg.
Limit number of results with optional limit
arg.
Start from a specific id with optional offset
arg.
Properties can be requested in the payload (lime_query syntax).
Example:
/company/erp_id/?status=updatesmade&status=new&limit=2&offset=1009
Payload:
{
"name": "",
"phone": "",
"postalcity": "",
"erp_id": "",
"coworker": {
"name": "",
"office": {
"name": ""
}
}
}
Response:
{
"objects": [
{
"_id": 1009,
"_limetype": "company",
"_status": "updatesmade",
"_first_synced": "2021-01-21T13:20:24.863000+01:00",
"_last_synced": "2021-04-27T16:44:22.400000+02:00",
"name": "Basemetrics Oy (demo)",
"phone": "0417784718",
"postalcity": "HELSINKI",
"erp_id": "7779311",
"coworker": {
"_id": "1023",
"_limetype": "coworker",
"name": "Carlito Calzone",
"office": {
"_id": "4003",
"_limetype": "building",
"name": "Havana",
}
}
},
{
"_id": 1022,
"_limetype": "company",
"_status": "new",
"_first_synced": "2021-01-05T00:00:00+01:00",
"_last_synced": "2021-04-27T16:44:22.403000+02:00",
"name": "PH Glas (DEMO)",
"phone": "+4521324545",
"postalcity": "\u00c5rhus V",
"erp_id": null,
"coworker": {
"_id": "1010",
"_limetype": "coworker",
"name": "Terry I",
"office": null
}
}
],
"instance": "company-visma",
"system": "vismabusiness",
"erp_id_property": "erp_id",
"status_property": "erp_status",
"next": 1023
}
next
can be used as offset for the next request. If no more objects are available, next is null.
PUT¶
/
Updates multiple objects asynchronously. Returns a task id to be used with the task endpoint. Related objects can be created/updated just as their parents.
Payload:
[
{
"_limetype": "company",
"_id": 1009,
"erp_status": "synced",
"name": "Batch updated OY"
},
{
"_limetype": "company",
"_search": {
"property": "erp_id",
"value": "4422",
},
"erp_status": "synced",
"name": "Identified by erp_id, will be created if it doesn't exist",
"person": {
"_search": {
"property": "name",
"value": "Klas Buffel"
},
"phone": "88599",
"firstname": "Claes",
"lastname": "Buffel"
}
},
{
"_limetype": "company",
"_id": 1450,
"erp_status": "synced",
"_search": {
"property": "erp_id",
"value": "4423",
},
"name": "_id will be priorized over search"
},
{
"_limetype": "company",
"_id": 1019,
"erp_status": "synced",
"buyingstatus": "prospect",
"wwa": "https://www.wrestler.se/"
},
{
"_limetype": "company",
"_id": 10101,
"erp_status": "synced",
"name": "Unknown company"
},
{
"_limetype": "deal",
"_id": 1009,
"name": "A new deal"
},
{
"_limetype": "person",
"_search": {
"property": "lastname",
"value": "Tunberg"
},
"lastname": "Thunberg"
},
{
"_limetype": "person",
"_search": {
"property": "email",
"value": "[email protected]",
"create": false
},
"name": "Carlito Calzone"
}
]
Response:
{
"id": "5b607d37-c9bd-4722-8ce9-7fb71517930f",
"status": "PENDING",
"result": null
}
Search¶
The _search
object allows limeobjects to be searched for using the name of a property and its value. A search should only be used on a property that has a unique value. In case there are several matches, only the first match is returned. If no limeobject is found, it's created by default. This behaviour can be overriden by setting create
to false.
{
"_search": {
"property": "email",
"value": "[email protected]",
"create": false // Default behaviour is true
}
}
Notes¶
- Any limeobject can be updated, whether it's configured in an instance or not.
_id
will always be prioritized over_search
._id
and optionally_search
are always available in the response from the task to be able to relate back to the data that was sent to this endpoint.- If any properties updated are configured in an instance as either the erp_id or erp_status property, the name of the instance(s) will also be returned.
- The
_first_synced
and_last_synced
properties are automatically set for all updated objects where instances were found. See above.
Task¶
GET¶
/task/<string:task_id>/
Poll to get the status on a task.
Example:
/task/5b607d37-c9bd-4722-8ce9-7fb71517930f/
Response:
{
"id": "5b607d37-c9bd-4722-8ce9-7fb71517930f",
"status": "SUCCESS",
"result": [
{
"_success": true,
"_error": null,
"_limetype": "company",
"_id": 1009,
"_action": "updated",
"erp_status": "synced",
"name": "Batch updated OY"
},
{
"_success": true,
"_error": null,
"_limetype": "company",
"_id": 2000,
"_action": "created",
"_search": {
"property": "erp_id",
"value": "4422"
},
"_instances": [
"company-visma"
],
"erp_id": "4422",
"erp_status": "synced",
"name": "Identified by erp_id, will be created if it doesn't exist",
"person": {
"_success": true,
"_error": null,
"_limetype": "person",
"_id": 1013,
"_action": "updated",
"_search": {
"property": "name",
"value": "Klas Buffel"
},
"phone": "88599",
"firstname": "Claes",
"lastname": "Buffel"
}
},
{
"_success": true,
"_error": null,
"_limetype": "company",
"_id": 1450,
"_action": "updated",
"_search": {
"property": "erp_id",
"value": "4423",
},
"_instances": [
"company-visma"
],
"name": "_id will be priorized over search"
},
{
"_success": false,
"_error": "The property \"wwa\" does not exist",
"_limetype": "company",
"_id": 1019,
"_action": null,
"_instances": [
"company-visma"
]
},
{
"_success": false,
"_error": "The company \"10101\" does not exist.",
"_limetype": "company",
"_id": null,
"_action": null,
"_instances": [
"company-visma"
]
},
{
"_success": true,
"_error": null,
"_limetype": "deal",
"_id": 1009,
"_action": "updated",
"name": "A new deal"
},
{
"_success": true,
"_error": null,
"_limetype": "person",
"_id": 1999,
"_action": "updated",
"_search": {
"property": "lastname",
"value": "Tunberg"
},
"lastname": "Thunberg"
},
{
"_success": true,
"_error": null,
"_limetype": "person",
"_action": "not_created",
"_search": {
"property": "email",
"value": "[email protected]",
"create": false
},
"name": "Carlito Calzone"
}
]
}
The outer status
is that of the task. Values can be PENDING
, SUCCESS
or FAILED
. When PENDING
, it's ideal to keep polling. The other statuses are final. result
contains the result of the task, and is depending on what the task was for. In this case for a PUT of multiple objects.
Info
If an object update was not successful, a new PUT request should be made updating the object's ERP status and ERP error message.
Status¶
GET¶
/status/<string:limetype_name>/<int:id>/
Gets the statuses for the instance(s) on a limeobject. Used by the web component and LBS app.
Example:
/status/company/1009/
Response:
[
{
"status": "updatesmade",
"statusText": "Updates made",
"firstSynced": "2020-06-30T17:36:31.307000+02:00",
"lastSynced": "2021-04-14T11:59:08.180000+02:00",
"system": "vismabusiness",
"instance": "company-visma",
"systemDisplayName": "Visma Business",
"readPermission": true,
"updatePermission": true
},
{
"status": "empty",
"statusText": "",
"firstSynced": "",
"lastSynced": "",
"system": "fortnox",
"instance": "company-fortnox",
"systemDisplayName": "Fortnox",
"readPermission": true,
"updatePermission": false
}
]
PUT¶
/<string:instance_name>/<int:id>/
Updates status on a single object. Used by the web component and LBS app.
Payload:
{
"status": "STRING"
}
Example:
/company-visma/1009/
Payload:
{
"status": "synced"
}
Response:
{
"status": "synced",
"statusText": "Synkad",
"firstSynced": "2020-06-30T17:36:31.307000+02:00",
"lastSynced": "2021-04-14T12:39:55.130000+02:00",
"system": "vismabusiness",
"instance": "company-visma",
"systemDisplayName": "Visma Business",
"readPermission": true,
"updatePermission": true
}
Translations¶
GET¶
/translations/
Gets all translations for the ERP Connector. Language is based on the Accept-Language
header. Used by the LBS app.
Events¶
All events that are either published or handled by the add-on.
erp_connector.config_updated¶
Custom event that is published by the add-on whenever the run time config is saved. This is used to refresh the run time config cache.
core.limeobject.{instance_limetype}.update.v1¶
Lime core event. Whenever the run time config is updated, event handlers will be registered for all limetypes in that are configured in ERP instances.
Tasks¶
create_or_update_objects_task¶
Creates or updates limeobjects.
update_instance_object_status_task¶
Updates the ERP status on a limeobject.
Scheduled Tasks¶
calculate_sales_trend¶
Calculates the sales trend on a daily basis. If the date is the start of the fiscal year, a yearly reset function (that resets old sales trend data) will be run prior to the daily calculations.