📜 ⬆️ ⬇️

Creating a new module for open-source CRM EspoCRM

image In this article, I would like to acquaint readers with the very interesting open-source (GPL3) architecture of the EspoCRM project using the example of creating a new module for this system.
What is a CRM-system (Customer relationship management), I think many people have long known. The peculiarity of this CRM-system is that it is written as a Single Page Application and therefore is quite “smart”.
A simple design and modern programming technologies will appeal to many, and the performance of this CRM system will pleasantly surprise. A demo version is available on the site.
The caching of scripts and templates in Local Storage helped to achieve high speed. All views along with the children are collected in one large HTML, which is displayed on the screen of the user.
The system has a powerful API that uses JSON, and the web interface is essentially an API client.
The system is not overloaded with functionality, but it has everything you need, as well as being well configured.


Creating a new module


First you need to create a working directory of the new module (this will be the module package) and put it in application/Espo/Modules :
 application/Espo/Modules/PM 

The directory structure of our module should be as follows:
 application/Espo/Modules/PM/Controllers/ application/Espo/Modules/PM/Entities/ application/Espo/Modules/PM/Resources/ 


Metadata description


When developing PM, we need to describe two entities — Project and ProjectTask. To do this, you need to create two JSON-files with the following content:
')
application / Espo / Modules / PM / Resources / metadata / scopes / Project.json
 { "entity": true, "layouts": true, "tab": true, "acl": true, "module": "PM", "customizable": true, "stream": true, "importable": true } 



application / Espo / Modules / PM / Resources / metadata / scopes / ProjectTask.json
 { "entity": true, "layouts": true, "tab": false, "acl": true, "module": "PM", "customizable": true, "stream": true, "importable": true } 



After that, we have to describe the fields and relationships for our entities in the entityDefs metadata.

application / Espo / Modules / PM / Resources / metadata / entityDefs / Project.json
 { "fields": { "name": { "type": "varchar", "required": true }, "status": { "type": "enum", "options": [ "Draft", "Active", "Completed", "On Hold", "Dropped" ], "default": "Active" }, "description": { "type": "text" }, "account": { "type": "link" }, "createdAt": { "type": "datetime", "readOnly": true }, "modifiedAt": { "type": "datetime", "readOnly": true }, "createdBy": { "type": "link", "readOnly": true }, "modifiedBy": { "type": "link", "readOnly": true }, "assignedUser": { "type": "link", "required": true }, "teams": { "type": "linkMultiple" } }, "links": { "createdBy": { "type": "belongsTo", "entity": "User" }, "modifiedBy": { "type": "belongsTo", "entity": "User" }, "assignedUser": { "type": "belongsTo", "entity": "User" }, "teams": { "type": "hasMany", "entity": "Team", "relationName": "EntityTeam" }, "account": { "type": "belongsTo", "entity": "Account", "foreign": "projects" }, "projectTasks": { "type": "hasMany", "entity": "ProjectTask", "foreign": "project" } }, "collection": { "sortBy": "createdAt", "asc": false, "boolFilters": [ "onlyMy" ] } } 



application / Espo / Modules / PM / Resources / metadata / entityDefs / ProjectTask.json
 { "fields": { "name": { "type": "varchar", "required": true }, "status": { "type": "enum", "options": [ "Not Started", "Started", "Completed", "Canceled" ], "default": "Not Started" }, "dateStart": { "type": "date" }, "dateEnd": { "type": "date" }, "estimatedEffort": { "type": "float" }, "actualDuration": { "type": "float" }, "description": { "type": "text" }, "project": { "type": "link" }, "createdAt": { "type": "datetime", "readOnly": true }, "modifiedAt": { "type": "datetime", "readOnly": true }, "createdBy": { "type": "link", "readOnly": true }, "modifiedBy": { "type": "link", "readOnly": true }, "assignedUser": { "type": "link", "required": true }, "teams": { "type": "linkMultiple" } }, "links": { "createdBy": { "type": "belongsTo", "entity": "User" }, "modifiedBy": { "type": "belongsTo", "entity": "User" }, "assignedUser": { "type": "belongsTo", "entity": "User" }, "teams": { "type": "hasMany", "entity": "Team", "relationName": "EntityTeam" }, "project": { "type": "belongsTo", "entity": "Project", "foreign": "projectTasks" } }, "collection": { "sortBy": "createdAt", "asc": false, "boolFilters": [ "onlyMy" ] } } 



Specify that we will use the controller for CRUD. The file is placed in the clientDefs directory.
application / Espo / Modules / PM / Resources / metadata / clientDefs / Project.json
 { "controller": "Controllers.Record" } 



application / Espo / Modules / PM / Resources / metadata / clientDefs / ProjectTask.json
 { "controller": "Controllers.Record" } 



Class controllers


For entities to work, it is necessary to describe their controllers.

application / Espo / Modules / PM / Controllers / Project.php
 <?php namespace Espo\Modules\PM\Controllers; class Project extends \Espo\Core\Controllers\Record { } 



application / Espo / Modules / PM / Controllers / ProjectTask.php
 <?php namespace Espo\Modules\PM\Controllers; class ProjectTask extends \Espo\Core\Controllers\Record { } 



Entity Classes


In addition to defining controllers, it is also necessary to define classes for entities.

application / Espo / Modules / PM / Entities / Project.php
 <?php namespace Espo\Modules\PM\Entities; class Project extends \Espo\Core\ORM\Entity { } 



application / Espo / Modules / PM / Entities / ProjectTask.php
 <?php namespace Espo\Modules\PM\Entities; class ProjectTask extends \Espo\Core\ORM\Entity { } 



Localization (I18n)


Add the names of our entities to the global localization file Global.json :

application / Espo / Modules / PM / Resources / i18n / en_US / Global.json
 { "scopeNames": { "Project": "Project", "ProjectTask": "Project Task" }, "scopeNamesPlural": { "Project": "Projects", "ProjectTask": "Project Tasks" } } 



For translation of fields, drop-down lists, links, etc. In our entities you need to create separate localization files:

application / Espo / Modules / PM / Resources / i18n / en_US / Project.json
 { "labels": { "Create Project": "Create Project" }, "fields": { "name": "Name", "status": "Status", "account": "Account" }, "links": { "projectTasks": "Project Tasks" } } 



application / Espo / Modules / PM / Resources / i18n / en_US / ProjectTask.json
 { "labels": { "Create ProjectTask": "Create Project Task" }, "fields": { "name": "Name", "status": "Status", "project": "Project", "dateStart": "Date Start", "dateEnd": "Date End", "estimatedEffort": "Estimated Effort (hrs)", "actualDuration": "Actual Duration (hrs)" } } 



Finishing touch


After all the above steps have been completed, go to the Administrator panel and perform system recovery (Menu -> Administration -> Rebuild), and then refresh the page.
Now our module Projects is hidden by default; To display, go to the Administrator panel (Administration -> User Interface) and make it visible. With Field Manager and Layout Manager, you can easily add new fields and customize the appearance of the data display.

I essentially have no relation to the project, I'm just a satisfied user. The process of creating a module is described by the developers and published with their consent. Both the developers and I are ready to answer all your questions.

Source: https://habr.com/ru/post/230065/


All Articles