Due to my work in the past few months, I had to deal with the development of CRM infrastructure for a single publishing house. The company’s management chose the desktop version of CRM, since At that time, the web version of bpm'online was fairly raw. Prior to the development, the main entities specific to the publisher, which had no analogues out of the box (such as "Editions", "Formats", "Numbers", for advertising in the magazines "Orders", "Placements", etc., were worked out and agreed upon ., closely interacting with each other). But what was my surprise when, having started the development, I did not find adequate documentation for developers, or rather did not find anything at all. All that was possible to dig out is the answers to the questions and blogs of the CRM developers themselves, on the forum. Scanty documentation can be found at
terrasoft.ru/sdk , but at the very beginning of my acquaintance with the system it didn’t help me much. Therefore, I spent a lot of time to understand what works and how.
So, the whole system can be divided into sections (Contacts, Contractors, Banks and details to these sections, for example Addresses, Contracts, Career for Contacts, Brands, Payment details for Counterparties, etc.)

Here is an example of the section “Contacts” and details of the “Address”. It is worth noting that sections are created using a special utility.
How to create a sectionTo do this, you need to register a special key for the Terrasoft Client launch shortcut in the “Object” field:
')
\wnd=wnd_CreateNewWorkspace
It should get something like this:
"C:\Program Files (x86)\Terrasoft Press\Bin\TSClient.exe" \wnd=wnd_CreateNewWorkspace
By the way, in the same way, using a different key, you can turn on the database query profiler, which is indispensable when searching for errors:
"C:\Program Files (x86)\Terrasoft Press\Bin\TSClient.exe" /profiler
Actually, the most important thing for a developer is just the addition, editing and structuring of data, the study of the connections of all entities with each other. Therefore, we will create a partition (or also Workspace) of
MyWorkspace and
adjust the addition of data.

After that you need to add the newly created section to the workplace:
-> ->

After that we can open Terrasoft Administrator and find all the services necessary for the work of our section (workspace).
You can find it in the services tree:
Custom -> Workspaces -> myworkspace -> General -> Main Grid

Now briefly about the services that make up our workspace.
- tbl_myworkspace (tbl_ = Table) Table with a description of fields, indices, and relationships;
- sq_myworkspace (sq_ = Select Query) Query that retrieves data from a table (or several tables using JOINs);
- ds_myworkspace (ds_ = Data Set) The data set allows you to add, delete or change data in the table;
- wnd_myworkspaceEdit (wnd_ = Window) Editing window with which the user adds data to this section;
- wnd_myworkspaceGridArea This is a grid in which all of our entries from this section are displayed;
- wnd_myworkspaceWorkspace The main section window that contains both the main grid and the details associated with it. Also, if necessary, you can add new parts here (a one-to-many connection is obtained, that is, one record of the grid can have several records in the details, as I wrote earlier, “Contact” can have several addresses, etc.);
Adding data
Let's try to create a section with information about employees. Add some new fields:
First you need to create all these fields in the
tbl_myworkspace table. No difficulties should arise. After saving, the fields will be updated in the database you are using (I have Microsoft SQL). Here we can add indexes by fields or links if necessary.
Now you need to update the query
sq_myworkspace , which is responsible for
selecting data from this table.

Add the required fields and save. If you wish, you can view the SQL query by pressing ctrl + P.
At the end, we add all these fields to ds_myworkspace
so that you can work with the data through a special object.

The same can be done much easier, you need to find the directory of our section (myworkspace) and change the structure of the fields:
->
Now that we have added the required fields, we can display them on the
wnd_myworkspaceEdit data addition / editing
form .
From the set of available elements, select
TextDataControl. In the properties you must specify the following parameters:
- DatasetLink - usually it is only one on the dlData form (located on the “Non-visual” tab);
- DataFieldName - select the field we want to display (if our added fields are not visible, try closing and re-opening the wnd_myworkspaceEdit window)
- Name - you can leave the standard name, but it is better to use a specific style for such elements (for example, edtNameOfField).
For salary, select
IntegerDataControl and do the same. Now we can open the Terrasoft Client and add an entry using the section data editing window. Record is added without problems, but our custom fields are not displayed in the grid. Why? Yes, because we have not added them to this very grid. Therefore, open
wnd_myworkspaceGridArea , find the element grdData -> gdvData, right-click, select the item "Define columns" and add our fields. After that, they will be visible in the grid in the client (you may need to add them by clicking the plus sign in the upper right corner of the grid, where you can add or hide the necessary columns on the contrary).
Drop down lists
Quite often you have to deal with drop-down lists. It is much more convenient to make, for example, a list of posts once in the directory, and then just choose the name of the post from this list. This is much more convenient and correct from the point of view of database design.
Suppose we have already created a directory with a list of posts
MyWorkspaceAppointments . Create an AppointmentID field in the
tbl_myworkspace table where the post ID will be stored. Add it to the
sq_myworkspace , and also we will need to select the field "
Job Title", which will be displayed in the drop-down list. Therefore we do JOIN with the table of posts across the field AppointmentID. And we add this field to the SELECT block. Extract it as an AppointmentName.

Now we add the field “Reference field” to the dataset. Select the AppointmentID column, select the data directory of posts directory in the directory data source, and select the AppointmentName field that we selected earlier in the “Column for display” field. At the bottom, put a tick "Display as a drop-down list in the cards." Add a LookupDataControl element to the window and select it in the DataFieldName. Everything can be checked.

Actually, this is the very basis on which the Terrasoft system is built (in my personal opinion).
Work with Dataset dataset
As I already said, work with the database occurs through the dataset data set. You can get the current dataset for the window in which we work via DataLink:
var Dataset = dlData.Dataset;
You can also get any other data set by its name:
var TestDataset = Services.GetNewItemByUSI('ds_Test');
Adding an entry in the code TestDataset.Append(); TestDataset.Values('Name') = ' '; TestDataset.Values('Salary') = 30000; TestDataset.Post();
ID - it is not necessary to add a unique identifier, this will happen automatically.
Editing RecordTo edit a specific record, apply a filter (hereinafter, the filters will be discussed in more detail) to the data set:
ApplyDatasetFilter(TestDataset, 'ID', '{e2ad6e6f-481a-40d0-abb6-50e4b19a43d6}', true); TestDataset.Open();
If you need to edit more than one entry:
TestDataset.Open(); while (!TestDataset.IsEOF) { TestDataset.Edit(); TestDataset.Values('Salary') = 40000; TestDataset.Post(); TestDataset.GotoNext();
Deletion
You can count the number of records in a set using the RecordsCount property.
var Records = TestDataset.RecordsCount;
Select Query and Dataset: custom filters
Data obtained using datasets can be filtered using custom filters. They are set in
sq_ services. In our case, this is
sq_myworkspace . By default, a filter by ID will be created, which can be used immediately in the code.
The normal filter is applied using the ApplyDatasetFilter function:
ApplyDatasetFilter(Dataset, 'ID', id, true);
- Dataset - to which it is necessary to apply the filter
- 'ID' - the name of the filter in the WHERE block
- id - identifier by which the search will be performed
- true - this parameter indicates that the filter should be enabled
Create a simple filter on the field "Salary":
1) create a parameter that will be passed to the filter (SalarySum);
2) add the “Comparison Filter” to the WHERE block, specify the same name as the parameter (SalarySum). Select the field (
tbl_myworkspace.Salary ) to compare with the parameter that we will pass from the ApplyDatasetFilter function.

Now we can apply it in the code and find all employees with a salary of 30,000.
var ds = Services.GetNewItemByUSI('ds_myworkspace'); ApplyDatasetFilter(ds, 'SalarySum', 30000, true); ds.Open(); Log.Write(2, ds.ValAsStr('Name')); ds.Close();
You can also apply multiple filters. For example, to find all directors with a salary of more than 30,000, etc.
Dataset events
I would also like to mention the most used dataset events:
OnDatasetAfterOpen - occurs after opening a dataset;
OnDatasetAfterPositionChange - occurs after switching to another record (it is useful when tracking the change of the selected record in the grid);
OnDatasetAfterPost - occurs after adding / changing a record (it is useful when you need to change related data in another table);
OnDatasetBeforePost - occurs before adding / changing a record (it is useful for user data check, before saving). Also, the DoPost parameter is passed to the event, if for some reason you need to cancel the addition (for example, the data did not pass the user check) you simply do:
DoPost.Value = false;
OnDatasetDataChange - occurs when dataset data changes. For example, some kind of logic is needed when changing a field position from our drop-down list.
function dlDataOnDatasetDataChange(DataField) { var FieldName = DataField.Name; switch(FieldName) { case 'EditionID':
OnDatasetBeforeDelete - occurs before deleting a record (for example, you can hang a custom data check, and, if necessary, cancel delete using the DoDelete parameter).
DoDelete.Value = false;
OnDatasetAfterDelete - occurs after deleting a record (for example, you can check, update or delete associated data with this record).
Window events, accessing elements
Sometimes you need to register a certain logic when you open a window, you can do this in the
OnPrepare event.
By default, the window, in the event, will have the function
wnd_BaseDBEditOnPrepare , which must be called to properly initialize each window. But we need to add our logic, so we will rename the function to
wnd_MyWorkspaceDBEditOnPrepare and double click on it. A window script will open (or create a new one).
function wnd_MyWorkspaceDBEditOnPrepare(Window) { scr_BaseDBEdit.wnd_BaseDBEditOnPrepare(Window); }
Do not forget to call this function for correct initialization and add our own logic. Suppose that we need to calculate income tax from an employee’s salary. Add a new element NumericEdit to the window.
Defining events for the
Salary OnKeyDown and OnKeyUp fields, these events will be triggered when data is entered or deleted. Let us write a function that will update the
Salary field with each
Tax field. It should get something like this:
function edtSalaryOnKeyDown(Control, Key, Shift) { calculateTax(); } function edtSalaryOnKeyUp(Control, Key, Shift) { calculateTax(); } function calculateTax() { var Dataset = dlData.Dataset;
Everything works fine. However, when you open the window, the "Tax" field will not be filled. This can be fixed by using the OnPrepare window event.
function wnd_MyWorkspaceDBEditOnPrepare(Window) { scr_BaseDBEdit.wnd_BaseDBEditOnPrepare(Window); calculateTax();
In the process of work, some developments have appeared that have come in handy to me more than once.
Actually, I described all the key points on working with the system. I think that to start developing for this CRM (relevant for version 3.4, I don’t know about others) this will be quite enough.