⬆️ ⬇️

Not a regular programming language. Part 2





The second part of the trilogy about the language and the lsFusion platform. The first part can be found here .



It will deal with the logic of representations, namely about everything that is associated with the integration of data and display them to the user or other information systems.



It is clear that many may not be so interesting to look at the fighter’s presentation, and they would like to see a real fight, if possible with blood (and it will be good, the discussion of previous articles helped to better understand the unprotected places of potential competitors and where it should be beaten) . But there are two things to consider:

')

a) this is Habr. That is a technical resource, they do not like beautiful pictures and advertising slogans - in order to say something, you need details on how you are going to achieve it.

b) it is a market for the development of information systems, and it is very similar to the market for slimming products. Here everyone declares that we have fast and easy. But when it comes to details in which, as is well known, the devil is hidden, either the simplest CRUDs are cited as examples or they resort to various tricks: they show some snippets of code, and hide the main part with the words “it doesn't matter "," Done in a couple of minutes "and everything like that.



Actually, therefore, we had two options: either to start with the advantages and the risk of getting reproaches in the marketing bullshit, or to begin with a technical description and the questions “why do we need another language”. Theoretically, of course, all this could be done in one article, but such an article would be hard not only to read, but even just to scroll through. Accordingly, we chose the second option, although if it is still important for someone to find out about the reasons for the emergence and advantages of the language right now (and not in future articles), welcome to the site . It consists of only three pages: what, how and why not, and gives, in my opinion, quite enough information to answer all these questions. Plus, you can also try the online platform, including to make sure that there is no “piano in the bushes”, and the code from the examples in this article is really all the code needed to run the application.



But enough lyrical digressions, back to the presentation of the fighter description of the logic of representations.



As in the domain logic (the first article), all concepts of representation logic in lsFusion form a stack:







and it is in the order of this stack that I will talk about them.



Forms



A form is the most important (and in fact practically the only) concept in the logic of representations, which is responsible for everything - both for user interaction and for printing, exporting and importing data.



The form can logically be divided into two parts:





Form structure



We begin, of course, with the structure of the form.



Objects



When creating a form, you first need to determine which objects it will display. It is worth noting that there may be a little more confusion in terminology between form objects and objects that are displayed in these form objects. Therefore, in the future, if it is not obvious from the context, we will use the terms “form objects” (for the first case) and “objects in the database” (for the second).



For each form object you need to set its class. This class can be both primitive (built-in) and object (user).

FORM currentBalances ' '

OBJECTS s = Stock, i = Item // ,

;

In accordance with the order of adding objects to the form, an ordered list of objects is formed. Accordingly, the last object for some set of objects will be called an object from this set with the maximum sequence number in this list (that is, as late as possible).



Every object on the form at any given time has a current value. Its change occurs depending on the presentation, either as a result of the corresponding actions of the user in the interactive presentation, or “virtually” in the process of reading data in a static representation.



Properties and Actions



After defining the objects on the form, you can add properties and actions, substituting the objects described above with their input as arguments.



Note that adding actions is only relevant for an interactive presentation; they are ignored in reports and exports. Also, given that the behavior of properties and actions from the point of view of their display on the form is absolutely the same, in the future we will use only the term property (for actions, the behavior is absolutely similar).



Display object



Each property is displayed in exactly one object on the form (we will call it the object displaying this property). By default, the display object is the object, the last one for the set of objects that are passed to the input of this property. For example, if we have a current balance form with two objects — a warehouse and a commodity, and three properties — the names of the warehouse and the commodity and the remainder of the goods in the warehouse:

FORM currentBalances ' '

OBJECTS s = Stock, i = Item // ,

PROPERTIES name(s), name(i), currentBalance(s, i)

;

Then, for the name of the warehouse, the display object will be s (warehouse), and for the name of the product and the remainder, i (product).



However, if necessary, the developer can set the display object explicitly (that is, for example, in an interactive view, show the property with the remainder in the table of warehouses, and not goods).



Filters and sorting



For each form, the developer can specify filters and orders that will limit the list of objects available for viewing / selection on the form, as well as the order in which they are displayed.



To set a filter, you must specify a property that will be used as a filtering criterion. The filter will be applied to the table of the object that is the last for the set of objects passed to the input of this property (that is, similarly with the definition of the property display object). In this case, only those sets of objects (rows) will be shown for which the property values ​​are not equal to NULL. For example, if we add the currentBalance (s, i) OR isActive (i) filter to the form above:

FORM currentBalances ' '

OBJECTS s = Stock, i = Item // ,

PROPERTIES name(s), name(i), currentBalance(s, i)

FILTERS currentBalance(s, i) OR isActive(i)

;

Form Screenshot




then when displaying goods, only products that are on the balance or are marked as active will be displayed.



Sorts are defined as a list of properties on the form, in which order the objects should be shown. Otherwise, everything is similar to filters.



Groups of objects



The platform also has the ability to combine objects into a group of objects . In this case, the “Cartesian product” of these objects will be shown in the tables / lists (that is, for two objects - all pairs, three objects - triples, etc.).

FORM currentBalances ' '

OBJECTS (s = Stock, i = Item) //

PROPERTIES name(s), name(i), currentBalance(s, i)

FILTERS currentBalance(s, i) OR isActive(i)

;

Form Screenshot




Accordingly, groups of objects can be used almost everywhere, both before and after, instead of single objects of the form.



Actually, the documentation has done so: the more general term “group of objects” is used everywhere, but in order not to complicate things in this article (and groups of objects consisting of several objects are used much less frequently), it was decided to forget about groups of objects that a group of objects always consists of exactly one object and, accordingly, use the term “object” everywhere instead of the more complex “group of objects” and “set of objects”.



Property groups



Properties on the form, as well as objects, can also be combined into groups, which, in turn, are used in the interactive (default design) and hierarchical views of the form (about them later). By default, the binding of a property to a group is global (that is, it is set for a property for all forms at once), however, if necessary, this binding can be redefined for individual forms.



Column objects



By default, a property is displayed exactly once in its display object. At the same time, as the values ​​of objects other than the object of display of this property (let's call them upper), their current values ​​are used. However, it is also possible in the platform to display one property several times in such a way that the values ​​of some upper objects are not their current values, but all the objects in the database that are suitable for filters. With this mapping of properties, a kind of “matrix” is formed - (display object) x (upper objects). Accordingly, in order to create such a matrix, it is necessary, when adding a property to a form, to specify which particular upper objects should be used to create columns (we will call these objects column-in-objects).

FORM currentBalances ' '

// ,

//

OBJECTS s = Stock, i = Item

//

PROPERTIES name(i), currentBalance(s, i) COLUMNS (s) HEADER name(s)

FILTERS isActive(i), isActive(s)

;

Form Screenshot




So, with the fact that the form displays, more or less figured out, let's move on to how she can do it.



Form submissions



There are three form representations:







Screenshots of views
Interactive:







Printed:







Structured:









Interactive and printed representations are graphical, that is, display the data obtained in a two-dimensional space: paper or device screen. Accordingly, each of these representations has a design that, depending on the specific presentation, can be set using the appropriate mechanisms (about them a little later).



The printed and structured presentation is static, that is, all data is read at once at the time of opening the form (as opposed to the interactive, which reads the data as necessary).



The description of representations will begin, perhaps, with the most difficult - interactive presentation.



Interactive presentation



In the interactive view, form objects are displayed in a table. The rows in this table correspond to the objects in the database that satisfy the specified filters, the columns, in turn, correspond to the properties.



However, if necessary, the property can be displayed not in the form of a column of a table, that is, for all its rows, but as a separate field on the form, that is, only for the current value of the form object. For example:

currentBalance ' ' (Stock s) = GROUP SUM currentBalance(s, Item i);

FORM currentBalances ' '

OBJECTS s = Stock, i = Item

// currentBalance(s) ,

PROPERTIES name(s), currentBalance(s) PANEL ,

name(i), currentBalance(s, i)

FILTERS currentBalance(s, i)

;

Form Screenshot




The change in the current value of the form object occurs either as a result of the user changing the current row of the table, or as a result of performing an action created using a special search operator (SEEK).



Note that the way of displaying a property in a panel or table, as a rule, is set not for each property individually, but as a whole for the form object. Accordingly, if the form object is marked as PANEL, then all its properties are displayed in the panel (that is, for the current value), otherwise (by default) all its properties are displayed in the table. Properties without parameters and default actions are displayed in the panel.



All tables in the interactive view are dynamic by default, that is, only a limited number of objects in the database are read, and the rest are read as the current object changes in the table. The number of displayed objects can be determined automatically on the basis of the height of the visible part of the table, and the developer explicitly specified when creating the form.



Also, the form in the interactive view is completely reactive, that is, it automatically updates all the data on the form when changing any data that affects them (such React, only in the general case). Plus, all this is done not by full recalculation (as in the same React), but incrementally, and moreover in SQL server.



In general, it’s funny when comparing with other technologies you try to include the top three requirements in a task, people often make round eyes, as if they are asked to launch a person into space. Although the non-fulfillment of the second requirement by any normal user will be classified as a bug, the first and third requirements are that the developed form will work normally when there is at least some data in the database (several tens of thousands of records, for example).



The interactive view is supported both in the web client mode (that is, the web application in the browser) and in the desktop client mode (Java application). The desktop client, like any native client, has a slightly better interface responsiveness, but most importantly, it allows you to work with the equipment, as well as perform other operations that are not available in the browser (mainly due to security problems).



Trees objects



In addition to tables, the platform also allows you to organize the display of objects in the form of trees, as flat (“nested” in each other tables), and recursive (for example, “nested” in each other objects in the database).



Plane trees, in fact, are a generalization of tables, when several tables are “merged” into one table at once:

FORM currentBalances ' '

TREE tree s = Stock, i = Item

//

//

PROPERTIES name(s), currentBalance(s),

name(i), currentBalance(s, i)

;

Form Screenshot




This is a relatively complex mechanism and is rarely used in practice, so we will not dwell on it in detail.



But recursive trees, on the contrary, are used quite often (for example, to implement classifiers). To display a form object in the form of such a tree, it is necessary to set an additional filter for it — a property whose value for lower objects must be equal to the upper object. Initially it is considered that the top object is NULL.

parent = DATA ItemGroup (ItemGroup) IN base;

group = DATA ItemGroup (Item) IN base;

//

level '' (ItemGroup child, ItemGroup parent) = RECURSION 1 AND child IS ItemGroup AND parent = child STEP 1 IF parent = parent($parent);

currentBalance ' ' (ItemGroup ig, Stock s) = GROUP SUM currentBalance(s, Item i) IF level(ig, group(i));



FORM currentBalances ' '

OBJECTS s=Stock PANEL //

PROPERTIES (s) name

TREE tree ig = ItemGroup PARENT parent, i = Item // /

PROPERTIES name(ig), currentBalance(ig, s)

PROPERTIES name(i), currentBalance(s, i)

FILTERS group(i) = ig

;

Form Screenshot




User form management



In order to ensure the best ergonomics of the system (including not to create forms for each and every), the users themselves can perform part of operations for setting up an interactive form presentation. For example, such operations are:





Also, the developer can create so-called filter groups that the user can turn on / off independently. For example:

EXTEND FORM currentBalances //

FILTERGROUP stockActive // , , /

FILTER '' active(st) 'F11' // , F11

FILTERGROUP bal

FILTER ' ' currentBalance(st, sk) > 0 'F10'

FILTER ' ' currentBalance(st, sk) < 0 'F9'

FILTER ' ' currentBalance(st, sk) 'F8' DEFAULT

FILTER ' ' NOT currentBalance(st, sk) 'F7'

;

These are not all possibilities for setting up the system by the user, but we will return to the remaining possibilities in the third article, since most of them still have no direct relation to the logic of ideas.



Note that the functionality described above refers rather to the functionality of ERP platforms, which is completely dissonant with the article title. On the other hand, as mentioned in the first article, in the long term, the language / platform claims to replace, including this class of platforms, so it would be wrong to not mention these features at all.



Object Operators



One of the most frequent scenarios for working with a form is adding / deleting an object, as well as editing it in a new form. To implement such scenarios in the platform, there is a predefined set of operators that allow you to create the necessary actions in one word directly in the form creation operator:





Also, since it is very often necessary to perform these actions in a new session (if you need to separate the actions of creating objects from the actions on the form from which these objects are created), the corresponding syntactic sugar is supported in the platform - the NEWSESSION and NESTEDSESSION options that work similarly to the eponymous operators creating actions, but, like the operators themselves working with objects, do not require the developer to create and name new actions. For example:

FORM teams

OBJECTS t=Team

// /

PROPERTIES (t) NEWSESSION NEW , EDIT , DELETE

OBJECTS p=Player

FILTERS team(p)=t

//

PROPERTIES (p) NEW , DELETE

;

By default, when editing an object, an editing form is invoked, automatically generated for the class of the form object submitted. However, it is often necessary to redefine this form (for example, add additional information, change the design, etc.) In order to do this, it is enough to create the necessary editing form and indicate that it is the default form for editing objects of a given class:

FORM order ''

OBJECTS o = Order PANEL

PROPERTIES (o) date, number



OBJECTS d = OrderDetail

PROPERTIES (d) nameBook, quantity, price, NEW , DELETE

FILTERS order(d) = o



EDIT Order OBJECT o

;

Similarly, the forms for selecting objects of a given class are overridden.



Form Design



As with most existing GUIs, the design of an interactive form view is a hierarchy whose nodes are components. Components, in turn, can be:





The arrangement of the components inside the containers essentially repeats CSS Flexible Box Layout (and is implemented in the web client using it), so we will not dwell on this mechanism in great detail.



Note that the design of the form is usually not created from scratch (as this is quite laborious). As a rule, the form design is created automatically based on the form structure, and then the developer only slightly changes it: for example, adds a new container, and transfers existing components to it:



Default Form Design Example
FORM myForm 'myForm'

OBJECTS myObject = myClass

PROPERTIES (myObject) myProperty1, myProperty2 PANEL

FILTERGROUP myFilter

FILTER 'myFilter' myProperty1(myObject)

;

The hierarchy of containers and components in the default design will look like this:





FORM myForm ' '

OBJECTS u = CustomUser

PROPERTIES (u) name, NEW , DELETE



OBJECTS c = Chat

PROPERTIES (c) message, NEW , DELETE

FILTERS user(c) = u

;



DESIGN myForm {

NEW middle FIRST {

type = CONTAINERH ;

fill = 1 ; //

MOVE BOX (u);

MOVE BOX (c);

}

}

Form Screenshot




Form Design 2.0 (React)



Looking at the interactive presentation of forms in the above screenshots (or, for example, in an online demo), you can see that the current design of forms in the user interface, let's say, is very austere. This, of course, has never been a particular problem for information systems, where the main users are employees or partners of the company that owns these information systems. Moreover, despite the asceticism, the current mechanism of form design allows you to implement very difficult cases, for example, POS:



Form Screenshot




But if it comes to, say, SaaS B2B or even more B2C (for example, some online banking), then immediately begin to appear questions how to make the design more ergonomic.



At the current stage, a special javascript library has been developed to solve this problem. Its main task is to create and update a special js-object containing form data. Accordingly, this object can be used as a state for React components and thus create any design and any additional interactivity of the developed form. For example:



React form example (codesandbox)



Or a more complex example - with drop-down lists and using the REST (or rather Stateless) API for this:



Example of a form on React with drop-down lists (on codesandbox)



However, the problem with this approach is that the forms from the examples above will not be built into either the general interface of the platform or the general control flow of actions. Therefore, one of the most immediate tasks in the development of the platform is to transfer the form design mechanism to the scheme used in the design of the report (printed presentation):





Plus, this approach will allow to generate including React Native forms and thereby allow the creation of native mobile applications. Although this issue (with native mobile applications) we have not worked very deeply yet.



However, we note that the old design mechanism will also be supported, since, for the same business applications, it performs its function perfectly.



Form events



Events of the form - the second key mechanism after the events of the subject area, which is responsible for determining the moments when you need to perform actions.



In the platform there is a whole set of various form events resulting from various user actions, but in this article we will consider only one, the most frequently used of them - the CHANGE event. This event occurs when the user initiated a property change / action call, for example, by clicking on any non-system key on the keyboard, being in the field of the property being changed, or clicking on this field with the mouse.



As for domain events, you can specify processing for form events — an action that will be performed when a specified event occurs. Note that most form events already have some default processing that out of the box implements the most expected behavior from the user (for example, for the above-mentioned CHANGE event, request user input and change the property to the entered value). However, in practice sometimes there are still situations when for some event of the form it is necessary to specify some specific processing, for example:

changeQuantity (Order o, Book b) {

INPUT q = INTEGER DO { //

IF lastOrderDetail(o, b) THEN { // ,

IF q THEN //

quantity(OrderDetail d) <- q IF d = lastOrderDetail(o, b) WHERE order(d) = o AND book(d) = b; //

ELSE // -

DELETE OrderDetail d WHERE order(d) == o AND book(d) == b;

} ELSE

IF q THEN

NEW d = OrderDetail { //

order(d) <- o;

book(d) <- b;

quantity(d) <- q;

}

}

}



EXTEND FORM order

OBJECTS b = Book

PROPERTIES name(b) READONLY , quantity(o, b) ON CHANGE changeQuantity(o, b)

;

, – , ( , , , paste , ). . .





, , , , , , , , INPUT.



, , , , , , . For example:

FORM order

OBJECTS o = Order

PROPERTIES (o) customer ON CHANGE {

INPUT s = STRING DO {

customer(o) <- s;

IF s THEN

MESSAGE 'Customer changed to ' + s;

ELSE

MESSAGE 'Customer dropped' ;

}

}

;

, , :





It must be said that INPUT is not the only data entry operator. In addition to it, the dialog form of the message display operator (ASK) is also responsible for entering data:

DELETE Order o WHERE selected(o);

ASK ' ' + ( GROUP SUM 1 IF DROPPED (Order o)) + ' . ?' DO {

APPLY ;

}

as well as the dialog mode of the operator of the opening of the form (DIALOG) in the interactive presentation, but we return to this mode, as well as to the operator of the opening of the form itself, after we consider the other representations.



With this interactive presentation we finish and move on to static views.



Static views



, . , , . , «» . , A B, A B, A A, B (A, B) B, A B ( «» ).



, , :





For example:

FORM myForm 'myForm'

OBJECTS A, B SUBREPORT , C, D, E

PROPERTIES f(B, C), g(A, C)

FILTERS c(E) = C, h(B, D)

;





, , , , .





LGPL – JasperReports.



, JasperReports , . , :





«» , SUBREPORT ( , -):







:

FORM shipment

OBJECTS s=Shipment //

PROPERTIES (s) date, customer = nameCustomer, stock = nameStock // , ( customer) ( stock)

PROPERTIES total = ( GROUP SUM quantity(ShipmentDetail d)*price(d) IF shipment(d)=s) //

OBJECTS sd=ShipmentDetail //

FILTERS shipment(sd) = s //

PROPERTIES (sd) index, item = nameItem // , ( item)

PROPERTIES (sd) price, quantity // ,

PROPERTIES sum '' = (quantity(sd) * price(sd)) // - * ( sum)

;



run() {

// 12345

PRINT shipment OBJECTS s = ( GROUP MAX Shipment s IF number(s) = '12345' )

XLSX TO exportFile;

}







JasperReports (, lsFusion). , . , JasperSoft Studio.



, lsFusion- IDEA, Eclipse, ( Eclipse JasperReports ). IDEA , language injection, jrxml-, , , , , , . , , Eclipse GrammarKit autocomplete (, ), stub-, lazy chameleon- ( ), , . .



Structured presentation



All structured representations (formats) can be divided into two types:





Note that working with flat formats with a hierarchy depth greater than one is not very convenient (due to the need to support an additional column), therefore, as a rule, flat formats are used only for working with simple forms (with a hierarchy depth less than one). In other cases, hierarchical formats are usually used. Accordingly, we will begin with them.



Hierarchical formats



– XML, JSON. , , JSON ( XML ).



/ JSON , : JSON-, – , – . :



JSON
/ , / :



  • / : , .
  • X:
    • X , , . Wherein:

      • X ,
      • ( ) X.


, / :



 JSON  ::= { JSON  ,   /    } JSON  ,   /  ::= JSON  1 | JSON   1 | JSON   1 JSON  2 | JSON   2 | JSON   2 ... JSON  M | JSON   M | JSON   M JSON  ::= "   " :   JSON   ::= "  " : { JSON   ,   /  } JSON   ::= "  " : [ { JSON   ,   /  1 }, { JSON   ,   /  2 }, ... { JSON   ,   /  N }, ] 




:

GROUP money;



FORM shipment

OBJECTS dFrom= DATE , dTo= DATE

OBJECTS s=Shipment //

PROPERTIES (s) date, customer = nameCustomer, stock = nameStock // , ( customer) ( stock)

FILTERS dFrom <= date(s) AND date(s) <= dTo //

OBJECTS sd=ShipmentDetail //

FILTERS shipment(sd) = s //

PROPERTIES (sd) IN money index, item = nameItem, price, quantity // , ( item), , money

;



run() {

EXPORT shipment OBJECTS dFrom = 2019_02_20 , dTo = 2019_04_28 ; //

}

 { "s": [ { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 5, "index": 1 } } ], "stock": " 2", "customer": " 2" }, { "date": "15.03.19", "sd": [ { "money": { "item": " 1", "quantity": 1, "price": 5, "index": 1 } }, { "money": { "item": " 2", "quantity": 1, "price": 10, "index": 2 } }, { "money": { "item": " 3", "quantity": 1, "price": 15, "index": 3 } }, { "money": { "item": " 4", "quantity": 1, "price": 20, "index": 4 } }, { "money": { "item": "Milk", "quantity": 1, "price": 50, "index": 5 } } ], "stock": " 1", "customer": " 3" }, { "date": "04.03.19", "sd": [ { "money": { "item": " 1", "quantity": 2, "price": 4, "index": 1 } }, { "money": { "item": " 2", "quantity": 3, "price": 4, "index": 2 } }, { "money": { "item": " 1", "quantity": 2, "price": 5, "index": 3 } } ], "stock": " 1", "customer": " 2" }, { "date": "04.03.19", "sd": [ { "money": { "item": " 1", "quantity": 3, "price": 1, "index": 1 } }, { "money": { "item": " 2", "quantity": 2, "price": 1, "index": 2 } } ], "stock": " 1", "customer": " 2" }, { "date": "14.03.19", "sd": [ { "money": { "item": " 2", "quantity": 1, "price": 2, "index": 1 } } ], "stock": " 1", "customer": " 2" }, { "date": "17.04.19", "sd": [ { "money": { "item": " 2", "quantity": 5, "price": 6, "index": 1 } }, { "money": { "item": " 1", "quantity": 2, "price": 6, "index": 2 } } ], "stock": " 1", "customer": " 1" }, { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" }, { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" }, { "date": "20.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" } ] } 




, JSON JSON-. , , IDE JSON , – JSON. , ( , , JSON- ), . / JSON .





, :





, , , ( ). , , , / :

run() {

EXPORT XLSX FROM item = upper(name(Item i)), currentBalance(i, Stock s),

stock = name(s), barcode(i), salePrice(i)

WHERE (name(i) LIKE '%%' OR salePrice(i) > 10 ) AND currentBalance(i, s);

}

, SELECT SQL. , , , ( , ).



, , , – .





, , , :



run(Genre g) {

SHOW booksByGenre OBJECTS g=g;

PRINT booksByGenre OBJECTS g=g;

EXPORT booksByGenre OBJECTS g=g;

}

– ( ) . .



(SHOW, DIALOG)



:





.



, :





, – .



, , (DIALOG). (, , ), , , .



, , (INPUT), ( ), , , ( ), , , ( ).

FORM booksByGenre

OBJECTS g = Genre PANEL

PROPERTIES (g) name

OBJECTS b = Book

PROPERTIES (b) name

FILTERS genre(b) = g

;



EXTEND FORM ordersByGenre

PROPERTIES (o) nameBook

ON CHANGE {

DIALOG booksByGenre OBJECTS g = g, b = book(o) INPUT DO

book(o) <- b;

}

;

(PRINT)



( ) , JasperReports : DOC, DOCX, XLS, XLSX, PDF, HTML, RTF , JasperReports. , , , , ( , ).



, - (PREVIEW), , / . , , .



(EXPORT, IMPORT)



, , , : XML, JSON, DBF, CSV, XLS, XLSX. .



, , , – . () , () , .



– , , « », :



TRUE ( , , 0 , .., , , ).

// ,

inn = DATA LOCAL BPSTRING [ 9 ] (Shipment);

barcode = DATA LOCAL BPSTRING [ 13 ] (ShipmentDetail);



FORM shipments

OBJECTS s=Shipment EXTID 'shipments' // EXTID s, shipments

PROPERTIES (s) number, date, inn

OBJECTS sd=ShipmentDetail EXTID 'detail' // EXTID sd, detail

FILTERS shipment(sd) = s // shipment detail

PROPERTIES (sd) barcode, price, quantity

;



run() {

FOR jsonFile = JSONFILE ( '\{ shipments : [ ' + // jsonFile / run, {} escape'

' \{number : "13423", date : "01.01.2019", inn : "2", detail : [\{ barcode : "141", quantity : 5, price : 10 \}, \{ barcode : "545", quantity : 2, price : 11 \}] \},' +

' \{number : "12445", date : "01.02.2019", inn : "1", detail : [\{ barcode : "13", quantity : 1, price : 22 \}] \} ]\}' )

DO {

IMPORT shipments FROM jsonFile; //

FOR BPSTRING [ 9 ] inn = inn(Shipment s) DO { // inn

customer(s) <- legalEntityINN(inn); // INN

stock(s) <- GROUP MAX st AS Stock; // - ( id)

}

FOR barcode(Item item) = barcode(ShipmentDetail sd) DO //

item(sd) <- item;



APPLY ;

exportString() <- IF canceled() THEN applyMessage() ELSE ' ' ;

}

}



Navigator



, , . , , , . . ( ), .



In some sense, the navigator is a very specific kind of interactive form presentation, adapted for fast and convenient work with the hierarchy of elements in the large window (desktop) mode. Accordingly, a distinctive feature of the navigator is that it can display only actions without parameters (the most frequent of which are form openings). Plus the design of the navigator is also very different from the form design.



Navigator Design



. , — .



– , . , , . , , .







.







100x100 . , , . , «» . , ( ). , . , .



. , - , , .



:

FORM items;

FORM stocks;

FORM legalEntities;

FORM shipments;

hello() { MESSAGE 'Hello world' ; }

hi() { MESSAGE 'Hi' ; }



NAVIGATOR {

NEW FOLDER catalogs '' WINDOW toolbar { // ,

NEW items; // - items,

}

catalogs { //

NEW FORM stocksNavigator '' = stocks; // - stocksNavigator stocls catalogs

NEW legalEntities AFTER items; // - legalEntities catalogs items

NEW shipments;

}

NEW FOLDER documents '' WINDOW toolbar { // ,

// root,

//

NEW ACTION hi; // -

NEW ACTION h=hello; // -

MOVE shipments BEFORE h; // shipments catalogs document hello

}

}





, – , , , , , ERP-. , , « », : ? Seriously? ? , -, lsFusion language-based ( SQL ABAP), library-based ( Java 1C) / . , , – domain-specific , . -, , , , . : , , , . , , , ( ).



. – , :





, :





Conclusion



As is often the case, the second parts do not always turn out as interesting as the first ones (although, most likely, the first part was not so interesting, after all, this is a tutorial, and such material, if you need to fit it in a reasonable amount, will by definition rather dry). But, as mentioned in the introduction, the purpose of this article (like the first one) was not “selling”; the goal was to give the reader an opportunity to make at least a partial picture of what development on lsFusion is.



, , – . , . , , « . .», , ( - ).



. : « ». , . , , lsFusion SQL-. , , , – , - , . , SQL- ( ). , . , , ( , ), , , . «?», « ...?» «?».

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



All Articles