Não é outra linguagem de programação. Parte 2: Lógica de Representação



A segunda parte da linguagem lsFusion e da trilogia de plataformas. A primeira parte pode ser encontrada aqui .

Ele se concentrará na lógica das representações, ou seja, em tudo relacionado à combinação de dados e exibição ao usuário ou a outros sistemas de informação.

É claro que muitos podem não estar tão interessados ​​em olhar para a apresentação do lutador, e gostariam de ver uma luta real, se possível com sangue (e será, já que a discussão dos artigos anteriores ajudou a entender melhor os lugares desprotegidos dos concorrentes em potencial e onde vencê-los) . Mas há duas coisas a considerar:

a) este é Habr. Ou seja, um recurso técnico, eles não gostam de belas fotos e slogans publicitários aqui - para dizer algo, você precisa de detalhes sobre como conseguir isso.
b) é um mercado para o desenvolvimento de sistemas de informação e é muito semelhante ao mercado de produtos para perda de peso. Aqui, todo mundo diz que temos um rápido e fácil. Mas quando se trata de detalhes, nos quais, como você sabe, o diabo está, os CRUDs mais simples são usados ​​como exemplos ou recorrem a vários truques: eles mostram alguns trechos de código e ocultam a parte principal com as palavras "isso não importa" "," Feito em alguns minutos "e tudo mais.

Na verdade, é por isso que tínhamos duas opções: começar com os benefícios e o risco de ser criticado por besteiras de marketing ou começar com uma descrição técnica e as perguntas "por que precisamos de mais um idioma". Teoricamente, é claro, tudo isso poderia ser feito em um artigo, mas esse artigo seria difícil não apenas de ler, mas também de rolar. Dessa forma, escolhemos a segunda opção, embora seja importante que alguém aprenda sobre os motivos da aparência e vantagens do idioma no momento (e não em artigos futuros), seja bem-vindo ao site . Consiste em apenas três páginas: o que, como e por que não, e fornece, na minha opinião, informações suficientes para responder a todas essas perguntas. Além disso, você também pode experimentar a plataforma on-line , inclusive para garantir que não haja "piano nos arbustos", e o código dos exemplos neste artigo é realmente todo o código necessário para executar o aplicativo.

Mas basta digressões líricas, voltamos à apresentação da descrição do lutador da lógica das representações.

Como na lógica de domínio (primeiro artigo), todos os conceitos de lógica de apresentação no lsFusion formam uma pilha:



e é na ordem dessa pilha que falarei sobre eles.


Formulários


Um formulário é o conceito mais importante (e de fato praticamente o único) na lógica de apresentação, responsável por tudo - tanto pela interação do usuário quanto pela impressão, exportação e importação de dados.

O formulário pode ser logicamente dividido em duas partes:

  • A estrutura do formulário determina quais dados o formulário mostra.
  • A apresentação do formulário determina como ele exibe esses dados.

Estrutura de forma


Começamos, naturalmente, com a estrutura do formulário.

Os objetos


Ao criar um formulário, você deve primeiro determinar quais objetos serão exibidos. Vale a pena notar que pode haver uma ligeira confusão na terminologia entre os objetos do formulário e os objetos exibidos nesses objetos do formulário. Portanto, no futuro, se isso não for óbvio no contexto, usaremos os termos “objetos de formulário” (para o primeiro caso) e “objetos no banco de dados” (para o segundo).

Para cada objeto de formulário, você precisa definir sua classe. Essa classe pode ser primitiva (interna) ou objeto (personalizada).
FORM currentBalances ' '
OBJECTS s = Stock, i = Item // ,
;
De acordo com a ordem de adicionar objetos ao formulário, é formada uma lista ordenada de objetos. Dessa forma, o último objeto para um determinado conjunto de objetos será chamado de objeto desse conjunto com o número de série máximo nesta lista (ou seja, o mais recente).

Cada objeto no formulário em um determinado momento tem um valor atual. Sua alteração ocorre dependendo da apresentação, como resultado das ações correspondentes do usuário na apresentação interativa ou "virtualmente" no processo de leitura de dados em uma representação estática.

Propriedades e ações


Depois de definir os objetos no formulário, você pode adicionar propriedades e ações, substituindo os objetos descritos acima por eles como entrada para os argumentos.

Observe que a adição de ações é relevante apenas para apresentações interativas, pois elas são ignoradas nos relatórios e nas exportações. Além disso, considerando que o comportamento das propriedades e ações do ponto de vista de sua exibição no formulário é exatamente o mesmo, no futuro usaremos apenas o termo propriedade (para ações, o comportamento é exatamente o mesmo).

Objeto de exibição


Cada propriedade é exibida em exatamente um objeto no formulário (nós o chamaremos de objeto de exibição dessa propriedade). Por padrão, o objeto de exibição é um objeto, o último para o conjunto de objetos que são passados ​​para a entrada dessa propriedade. Por exemplo, se tivermos uma forma de saldos atuais com dois objetos - um armazém e mercadorias e três propriedades - nomes do armazém e mercadorias e o saldo de mercadorias no armazém:
FORM currentBalances ' '
OBJECTS s = Stock, i = Item // ,
PROPERTIES name(s), name(i), currentBalance(s, i)
;
Então, para o nome do depósito, o objeto de exibição será s (depósito), e para o nome da mercadoria e o saldo, i (mercadoria).

No entanto, se necessário, o desenvolvedor pode especificar o objeto de exibição explicitamente (por exemplo, em uma exibição interativa, mostre a propriedade com o restante na tabela de armazéns, não mercadorias).

Filtros e Classificação


Para cada formulário, o desenvolvedor pode definir filtros e pedidos que limitarão a lista de objetos disponíveis para visualização / seleção no formulário, bem como a ordem em que são exibidos.

Para definir um filtro, você deve especificar uma propriedade que será usada como critério de filtro. O filtro será aplicado à tabela desse objeto, que é a última para o conjunto de objetos transmitidos para a entrada dessa propriedade (ou seja, da mesma forma que a definição do objeto de exibição da propriedade). Nesse caso, apenas os conjuntos de objetos (séries) serão mostrados para os quais os valores da propriedade não são NULL. Por exemplo, se adicionarmos o filtro currentBalance (s, i) OR isActive (i) ao formulário acima:
FORM currentBalances ' '
OBJECTS s = Stock, i = Item // ,
PROPERTIES name(s), name(i), currentBalance(s, i)
FILTERS currentBalance(s, i) OR isActive(i)
;
Captura de tela do formulário


ao exibir produtos, apenas os produtos que estão na balança ou marcados como ativos serão exibidos.

As classificações são definidas como uma lista de propriedades no formulário em que os objetos de ordem devem ser exibidos. Caso contrário, tudo é semelhante aos filtros.

Grupos de objetos


A plataforma também tem a capacidade de combinar objetos em um grupo de objetos . Nesse caso, o “produto cartesiano” desses objetos será mostrado nas tabelas / listas (ou seja, para dois objetos - todos os pares, três objetos - triplos, etc.).
FORM currentBalances ' '
OBJECTS (s = Stock, i = Item) //
PROPERTIES name(s), name(i), currentBalance(s, i)
FILTERS currentBalance(s, i) OR isActive(i)
;
Captura de tela do formulário


Assim, em quase todos os lugares, antes e depois, em vez de objetos únicos do formulário, você pode usar grupos de objetos.

Na verdade, foi o que foi feito na documentação: o termo mais geral "grupo de objetos" é usado em todos os lugares, mas para não complicar as coisas neste artigo (e grupos de objetos compostos por vários objetos são usados ​​com muito menos frequência), foi decidido esquecer e contar sobre grupos de objetos, que um grupo de objetos sempre consiste em exatamente um objeto e, portanto, use o termo "objeto" em qualquer lugar, em vez do mais complexo "grupo de objetos" e "conjunto de objetos".

Grupos de propriedades


As propriedades no formulário, como objetos, também podem ser combinadas em grupos, que, por sua vez, são usados ​​nas representações interativas (design padrão) e hierárquicas do formulário (sobre elas um pouco mais tarde). Por padrão, a ligação de uma propriedade a um grupo é global (ou seja, é definida para uma propriedade para todos os formulários de uma só vez); no entanto, se necessário, para formulários individuais, essa ligação pode ser redefinida.

Objetos de coluna


Por padrão, uma propriedade é exibida exatamente uma vez em seu objeto de exibição. Nesse caso, como os valores de objetos diferentes do objeto de exibição desta propriedade (os chamamos de superiores), seus valores atuais são usados. No entanto, a plataforma também pode exibir uma propriedade várias vezes, para que os valores de alguns objetos principais não sejam usados ​​pelos valores atuais, mas todos os objetos no banco de dados adequados para filtros. Com esse mapeamento de propriedades, um tipo de "matriz" é formado - (objeto de exibição) x (objetos superiores). Portanto, para criar uma matriz, ao adicionar uma propriedade ao formulário, é necessário indicar quais objetos superiores devem ser usados ​​para criar colunas (chamaremos esses objetos de objetos em colunas).
FORM currentBalances ' '
// ,
//
OBJECTS s = Stock, i = Item
//
PROPERTIES name(i), currentBalance(s, i) COLUMNS (s) HEADER name(s)
FILTERS isActive(i), isActive(s)
;
Captura de tela do formulário


Então, com o que o formulário exibe, mais ou menos descoberto, vamos seguir como ele pode fazer isso.

Submissões de formulário


Existem três envios de formulário:



Visualizar capturas de tela
Interativo:



Impresso:



Estruturado:



  • Interativo. Uma visão com a qual o usuário pode interagir é alterar dados e objetos atuais, disparando vários eventos. Na verdade, essa representação é geralmente chamada de formulário.
  • Impresso. Geralmente, isso é chamado de relatório - upload de todos os dados do formulário e apresentação em forma gráfica. Inclusive com a possibilidade de imprimi-los (de onde tirou o nome).
  • Estruturado - representação do formulário em vários formatos estruturados (JSON, XML, DBF, etc.). Normalmente usado para maior integração com outros sistemas.

As representações interativas e impressas são gráficas, ou seja, exibem os dados recebidos no espaço bidimensional: papel ou a tela do dispositivo. Consequentemente, cada uma dessas representações possui um design que, dependendo da representação específica, pode ser configurado usando mecanismos apropriados (sobre eles um pouco mais tarde).

A apresentação impressa e estruturada é estática, ou seja, eles lêem todos os dados no momento em que o formulário é aberto (em oposição à interativa, que lê os dados conforme necessário).

A descrição das performances começará, talvez, com a mais difícil - a apresentação interativa.

Apresentação interativa


Em uma exibição interativa, os objetos de formulário são exibidos no formato de tabela. As linhas nesta tabela correspondem aos objetos no banco de dados que satisfazem os filtros especificados, as colunas, por sua vez, correspondem às propriedades.

No entanto, se necessário, a propriedade pode ser exibida não como uma coluna da tabela, ou seja, para todas as suas linhas, mas como um campo separado no formulário, ou seja, apenas para o valor atual do objeto de formulário. Por exemplo:
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)
;
Captura de tela do formulário


A alteração do valor atual de um objeto de formulário ocorre como resultado de um usuário alterando a linha atual da tabela ou como resultado da execução de uma ação criada usando um operador de pesquisa especial (SEEK).

Observe que a maneira como uma propriedade é exibida em um painel ou tabela, como regra, é configurada não para cada propriedade separadamente, mas como um todo para um objeto de formulário. Portanto, se o objeto de formulário estiver marcado como PANEL, todas as suas propriedades serão exibidas no painel (ou seja, para o valor atual), caso contrário (por padrão) todas as suas propriedades serão exibidas na tabela. Propriedades sem parâmetros e ações padrão são exibidas no painel.

Todas as tabelas na exibição interativa são dinâmicas por padrão, ou seja, apenas um número limitado de objetos no banco de dados é lido e o restante é lido à medida que o objeto atual na tabela é alterado. O número de objetos exibidos nesse caso pode ser determinado automaticamente com base na altura da parte visível da tabela ou definido explicitamente pelo desenvolvedor ao criar o formulário.

Além disso, o formulário na apresentação interativa é completamente reativo, ou seja, atualiza automaticamente todos os dados no formulário quando quaisquer dados que os afetam são alterados (como React, apenas no caso geral). Além disso, tudo isso é feito não por recálculo completo (como no mesmo React), mas, além disso, de maneira incremental, no servidor SQL.

Em geral, é engraçado quando, ao comparar com outras tecnologias, você tenta incluir os três principais requisitos na tarefa, as pessoas geralmente olham em volta, como se lhes pedissem para lançar uma pessoa no espaço. Embora o não atendimento do segundo requisito por qualquer usuário normal seja classificado como um bug, o primeiro e o terceiro requisito é que o formulário desenvolvido funcione normalmente quando pelo menos um pouco de dados aparecer no banco de dados (várias dezenas de milhares de registros, por exemplo).

A apresentação interativa é suportada no modo de cliente da web (ou seja, aplicativos da web em um navegador) e no modo de cliente de desktop (aplicativos Java). O cliente de desktop, como qualquer cliente nativo, possui uma capacidade de resposta um pouco melhor da interface, mas, o mais importante, permite trabalhar com o equipamento e executar outras operações que não estão disponíveis no navegador (principalmente devido a problemas de segurança).

Árvores de Objetos


Além das tabelas, a plataforma também permite organizar a exibição de objetos na forma de árvores, planas ("aninhadas" umas nas outras tabelas) e recursivas (por exemplo, objetos "aninhadas" no banco de dados).

Árvores planas, de fato, são uma generalização de tabelas, quando várias tabelas são "combinadas" em uma tabela ao mesmo tempo:
FORM currentBalances ' '
TREE tree s = Stock, i = Item
//
//
PROPERTIES name(s), currentBalance(s),
name(i), currentBalance(s, i)
;
Captura de tela do formulário


Este é um mecanismo relativamente complexo e raramente é usado na prática; portanto, não iremos nos aprofundar nele em detalhes.

Mas as árvores recursivas, pelo contrário, são usadas com bastante frequência (por exemplo, para implementar classificadores). Para exibir um objeto de formulário na forma de uma árvore, é necessário definir um filtro adicional para ele - uma propriedade cujo valor para os objetos inferiores deve ser igual ao objeto superior. Inicialmente, o objeto superior é considerado 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
;
Captura de tela do formulário


Gerenciamento de formulário de usuário


Para garantir uma melhor ergonomia do sistema (inclusive para não criar formulários para todos), parte das operações para configurar a apresentação interativa do formulário pode ser realizada pelos próprios usuários. Por exemplo, essas operações são:

  • configuração de tabelas (colunas visíveis, ordem, fontes, etc.),
  • criando filtros e tipos personalizados,
  • agrupando dados por valores de coluna,
  • imprima uma tabela e faça o upload para o Excel.

Além disso, o desenvolvedor pode criar os chamados grupos de filtros, que o usuário pode ativar / desativar independentemente. Por exemplo:
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'
;
Essas não são todas as possibilidades de personalização do sistema pelo usuário, mas retornaremos às outras possibilidades no terceiro artigo, pois a maioria delas ainda não possui relação direta com a lógica de apresentação.

Observe que a funcionalidade descrita acima se refere mais provavelmente à funcionalidade das plataformas ERP, que já é completamente discordante com o título do artigo. Por outro lado, como mencionado no primeiro artigo, no futuro, o idioma / plataforma alega ser um substituto, incluindo essa classe de plataformas, portanto seria errado não mencionar esses recursos.

Operadores de Objetos


Um dos cenários mais comuns de trabalhar com um formulário é adicionar / excluir um objeto e editá-lo em um novo formulário. Para implementar esses cenários, a plataforma possui um conjunto predefinido de operadores que permite criar as ações necessárias em uma palavra diretamente no operador de criação de formulário:

  • NOVO - criar um objeto
  • EDIT - editando um objeto
  • NEWEDIT - criando e editando um objeto
  • DELETE - excluir um objeto

Além disso, como muitas vezes é necessário executar essas ações em uma nova sessão (se você precisar separar as ações de criação de objetos das ações no formulário a partir do qual esses objetos são criados), a plataforma suporta o açúcar sintático correspondente - as opções NEWSESSION e NESTEDSESSION, que funcionam de maneira semelhante aos operadores com o mesmo nome criando ações, mas, como os próprios operadores que trabalham com objetos, não exige que o desenvolvedor crie e nomeie novas ações. Por exemplo:
FORM teams
OBJECTS t=Team
// /
PROPERTIES (t) NEWSESSION NEW , EDIT , DELETE
OBJECTS p=Player
FILTERS team(p)=t
//
PROPERTIES (p) NEW , DELETE
;
Por padrão, ao editar um objeto, o formulário de edição é chamado, que é gerado automaticamente para a classe do objeto de formulário passado. No entanto, muitas vezes é necessário redefinir este formulário (por exemplo, adicionar informações adicionais, alterar o design etc.) Para fazer isso, basta criar o formulário de edição necessário e indicar que é o formulário padrão para editar objetos de uma determinada classe:
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
;
Da mesma forma, os formulários para selecionar objetos de uma determinada classe são redefinidos.

Projeto da forma


Como na maioria das GUIs existentes, o design de uma apresentação interativa de um formulário é uma hierarquia cujos nós são componentes. Os componentes, por sua vez, podem ser:

  • containers - componentes que contêm outros componentes.
  • componentes básicos - representações gráficas de elementos básicos: tabelas, painéis de propriedades, grupos de filtros, etc.

O mecanismo para organizar os componentes dentro dos contêineres repete essencialmente o CSS Flexible Box Layout (e é implementado no Web client com ele), portanto, não vamos nos deter sobre esse mecanismo em detalhes.

Observe que o design do formulário geralmente não é criado a partir do zero (pois é bastante demorado). Normalmente, um design de formulário é criado automaticamente com base na estrutura do formulário e, em seguida, o desenvolvedor o altera apenas um pouco: por exemplo, adiciona um novo contêiner e transfere componentes existentes para ele:

Exemplo de design de formulário padrão
FORM myForm 'myForm'
OBJECTS myObject = myClass
PROPERTIES (myObject) myProperty1, myProperty2 PANEL
FILTERGROUP myFilter
FILTER 'myFilter' myProperty1(myObject)
;
A hierarquia de contêineres e componentes no design padrão terá a seguinte aparência:


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);
}
}
Captura de tela do formulário


Design de Formulário 2.0 (Reagir)


Observando as representações interativas dos formulários nas capturas de tela acima (ou, por exemplo, em uma demonstração online), você pode ver que o design atual do formulário na interface do usuário é, digamos, bastante ascético. Obviamente, isso nunca foi um problema específico para sistemas de informação, onde os principais usuários são funcionários ou parceiros da empresa que possui esses sistemas de informação. Além disso, apesar do ascetismo, o mecanismo atual de criação de formulários permite implementar casos muito difíceis, por exemplo, POS:

Captura de tela do formulário


Mas se, digamos, SaaS B2B ou B2C, quanto mais serviços bancários na Internet, por exemplo, então começam a surgir perguntas sobre como tornar o design mais ergonômico.

No estágio atual, para resolver esse problema, uma biblioteca javascript especial foi desenvolvida, cuja principal tarefa é criar e atualizar um objeto js especial contendo dados do formulário. Assim, esse objeto pode ser usado como estado para o componente React e, assim, criar qualquer design e qualquer interatividade adicional do formulário que está sendo desenvolvido. Por exemplo:

Reagir exemplo de formulário (no codesandbox)

Ou um exemplo mais complexo - com listas suspensas e usando a API REST (ou melhor, Stateless) para isso:

Reagir exemplo de formulário com listas suspensas (na codesandbox)

É verdade que o problema dessa abordagem é que os formulários dos exemplos acima não serão incorporados nem na interface geral da plataforma nem no fluxo geral de ações de controle. Portanto, uma das tarefas mais imediatas no desenvolvimento da plataforma é a tradução do mecanismo de design do formulário no esquema usado no design do relatório (apresentação impressa):

  • A plataforma gera automaticamente um design de reação com base na estrutura do formulário, como no exemplo acima.
  • Se necessário, o desenvolvedor pode salvá-lo e editar como quiser. Assim, a plataforma usará esse design editado ao abrir o formulário.

Além disso, essa abordagem permitirá gerar formulários React Native e, assim, permitirá criar aplicativos móveis nativos. Embora esse problema (com aplicativos móveis nativos) ainda não tenhamos trabalhado muito profundamente.

É verdade que observamos que o mecanismo de design antigo também será suportado, pois, para os mesmos aplicativos de negócios, ele desempenha perfeitamente sua função.

Eventos de formulário


Eventos de formulário são o segundo mecanismo chave após eventos de domínio, responsável por determinar quando executar ações.

A plataforma possui todo um conjunto de vários eventos de formulário que surgem como resultado de determinadas ações do usuário, mas neste artigo consideraremos apenas um, o mais usado deles - o evento CHANGE. Esse evento ocorre quando o usuário iniciou uma chamada de alteração / ação de propriedade, por exemplo, pressionando qualquer tecla que não seja do sistema no teclado, no campo da propriedade que está sendo alterada ou clicando nesse campo com o mouse.

Quanto aos eventos da área de assunto, para eventos do formulário, você pode especificar o processamento - a ação que será executada quando o evento especificado ocorrer. Observe que a maioria dos eventos de formulário já possui algum processamento padrão que, imediatamente, implementa o comportamento mais esperado pelo usuário (por exemplo, com o evento CHANGE mencionado acima, solicite a entrada do usuário e altere a propriedade para o valor inserido). No entanto, na prática, às vezes surgem situações em que, para algum evento de formulário, é necessário especificar algum processamento específico, por exemplo:
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.

, , , , , , . Por exemplo:
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' ;
}
}
;
, , :

  • ,
  • ( ),
  • ,
  • .

, INPUT – . (ASK):
DELETE Order o WHERE selected(o);
ASK ' ' + ( GROUP SUM 1 IF DROPPED (Order o)) + ' . ?' DO {
APPLY ;
}
(DIALOG) , , , , .

.


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

, , :

  • :
  • , , A B, A ( ).

Por exemplo:
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 , . , :

  • «» ( , O1, O2, O3,… On, O2 – O1, O3 – O2 ..) ;
  • , .

«» , 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- ( ), , . .


() :

  • (XML, JSON) – , () -.
  • (DBF, CSV, XLS) – - . parent, «» -.

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


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

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

JSON
/ , / :

  • / : , .
  • X:
    • X , , . Nesse caso:
      • 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)


:

  • (WAIT) – , , , , .
  • (NOWAIT) – .

.

, :

  • (FLOAT) – .
  • (DOCKED) – System.forms.

, – .

, , (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 f(a) = b – f(a) b)

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 ' ' ;
}
}


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

- , ( ). , , ( ). .


. , — .

– , . , , . , , .



.



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

. , - , , .

:
  • , – , , .
  • – forms, log, status, root, toolbar, tree, (, root, , )
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-. , , « », : ? Sério? ? , -, lsFusion language-based ( SQL ABAP), library-based ( Java 1C) / . , , – domain-specific , . -, , , , . : , , , . , , , ( ).

. – , :

  • : , , , , , ;
  • : , .

, :

  • . «» : , , / . , , , , , , .
  • . control flow . , – .
  • SQL ( ORM). , , , .

Conclusão


, , (, , , , tutorial, , , ). , , ( ) «», , lsFusion.

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

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

UPD: .

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


All Articles