📜 ⬆️ ⬇️

Terrasoft CRM: Developer's Guide

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 section
To 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.


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:


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 Record
To 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(!TestDataset.IsEmptyPage) { TestDataset.Edit(); TestDataset.Values('Salary') = 35000; //    TestDataset.Post(); //  } TestDataset.Close(); //     

If you need to edit more than one entry:

 TestDataset.Open(); while (!TestDataset.IsEOF) { TestDataset.Edit(); TestDataset.Values('Salary') = 40000; TestDataset.Post(); TestDataset.GotoNext(); //     } TestDataset.Close(); 


Deletion

 //     ,      TestDataset.Open(); while(!TestDataset.IsEOF) { TestDataset.Delete(); TestDataset.Open(); } TestDataset.Close(); 

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); 



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': //        break; } } 

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; //       edtTax.Value = dlData.Dataset.ValAsInt('Salary') * 0.13; } 

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.
Look
 //       DataGrid function grdDataOnGetRowDrawInfo(DataGrid, Color, TextColor, ImageName, Font) { if (/*  */) { Color.Value= clRed; //   TextColor.Value = clYellow; //   } } 


 //       function edtContactOnPrepareSelectWindow(LookupDataControl, SelectWindow) { ApplyDatasetFilter(LookupDataControl.DataField.LookupDataset, 'IsActive', true, true); } //   ,        OnPrepareSelectWindow< (        ,  ,    ) edtContact.UnprepareDropDownList(); 


 //    var EditWindowUSI = 'wnd_OpportunityEdit'; var Attributes = GetNewDictionary(); Attributes.Add('RecordID', GUID_NULL); //    RecordID      ,  ,    ID = RecordID    var DefaultValues = GetNewDictionary(); //    DefaultValues.Add('CustomerID', AccountID); ShowEditWindowEx(EditWindowUSI, Attributes, DefaultValues); 


 // -    ,           Connector.Attributes('DatasetToSave') = object; 


 //     (       ) var ParentWindow = Self.Attributes('NotifyObject').ParentContainer.ParentWindow.ComponentsByName('edtPriceListsEditionID'); //      (    ) var ParentWindow = Self.ParentContainer.ParentWindow.ComponentsByName('edtName'); //        workspace var grd = Self.ParentContainer.ParentWindow.ComponentsByName('wndGridData').Window.ComponentsByName('grdData'); 


 // ,     ,      Window.Attributes('IsNewRecordAppend') 


 //     var ArrayIDs = GetArrayByCollection(grdData.SelectedIDs); 


 //       Log.Write(2, 'Text'); //   ,    try { //   } catch(e) { Log.Write(2, e.message); } 


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.

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


All Articles