Estamos construindo uma interface para inserir documentos através da seleção

imagem

Em vários aplicativos de negócios, a tarefa de entrada de documentos geralmente surge. Normalmente, um documento consiste em um cabeçalho e algumas linhas, cada uma das quais se refere a algum objeto (por exemplo, produto). Na maioria das vezes, uma tabela regular é usada para inserir registros em um documento, no qual o usuário pode adicionar e excluir linhas, além de alterar seu conteúdo.

No entanto, em alguns casos, esse esquema nem sempre é conveniente para os usuários. Neste artigo, mostrarei como criar uma interface de usuário conveniente usando a técnica de seleção de produtos.

Desafio


Como regra, ao inserir documentos para o usuário, há uma restrição no conjunto de objetos que podem ser adicionados a ele. Nesse caso, é lógico que o usuário mostre uma lista desses objetos com a capacidade de incluí-los (e excluí-los) rapidamente no documento. Nas colunas, para cada um dos objetos, também é conveniente mostrar os dados necessários para tomar uma decisão sobre a necessidade de incluí-los no documento. E, finalmente, junto com o objeto, muitas vezes é necessário inserir sua quantidade.

Vamos considerar três tipos de documentos frequentemente encontrados em aplicativos de negócios:

  1. Pedido de compra . Nesses documentos, é lógico que o usuário mostre uma lista de todos os produtos disponíveis para pedido do fornecedor. Nas colunas, é conveniente mostrar o saldo atual, as vendas por um determinado intervalo, a quantidade solicitada para compra e venda.
  2. Ordem de venda . Aqui, na maioria das vezes, é exibida uma lista de mercadorias que estão na balança do armazém selecionado e disponíveis para venda ao cliente selecionado. Os preços atuais também devem ser mostrados.
  3. Mudança nos saldos . Este documento é usado para ajustar os saldos atuais em caso de discrepâncias com o valor real. Todos os produtos geralmente são mostrados na seleção, com a capacidade de inserir o saldo real para qualquer um deles. Ao mesmo tempo, um produto é adicionado ao documento com um valor igual à diferença entre o saldo atual e o atual.

Solução


A seguir, mostrarei como implementar rápida e facilmente essa lógica com base na plataforma aberta e gratuita da lsFusion . Como exemplo, vamos criar lógica para inserir um pedido.

Para começar, adicione um guia do produto através da interface CRUD padrão:
CLASS Product '' ;
name '' = DATA STRING [ 50 ] (Product);

FORM product ''
OBJECTS p = Product PANEL
PROPERTIES (p) name

EDIT Product OBJECT p //
;

FORM products ''
OBJECTS p = Product
PROPERTIES (p) READONLY name
PROPERTIES (p) NEWSESSION NEW , EDIT , DELETE

LIST Product OBJECT p // ,
;

NAVIGATOR {
NEW products;
}

Criamos o conceito de fornecedor e, na forma de edição, damos a oportunidade de escolher os produtos com os quais ele trabalha:
CLASS Supplier '' ;
name '' = DATA STRING [ 50 ] (Supplier);

in '' = DATA BOOLEAN (Supplier, Product); // TRUE,

FORM supplier ''
OBJECTS s = Supplier PANEL
PROPERTIES (s) name

OBJECTS p = Product //
PROPERTIES in(s, p), name(p) READONLY //

EDIT Supplier OBJECT s
;

FORM suppliers ''
OBJECTS s = Supplier
PROPERTIES (s) READONLY name
PROPERTIES (s) NEWSESSION NEW , EDIT , DELETE

LIST Supplier OBJECT s
;

NAVIGATOR {
NEW suppliers;
}

Declare a lógica da ordem com as linhas:
CLASS Order '' ;
date '' = DATA DATE (Order);
number '' = DATA INTEGER (Order);

supplier '' = DATA Supplier (Order);
nameSupplier '' (Order o) = name(supplier(o));

CLASS OrderDetail ' ' ;
order '' = DATA Order (OrderDetail) NONULL DELETE ;

Adicione produtos e quantidade às linhas:
product '' = DATA Product (OrderDetail);
nameProduct '' (OrderDetail d) = name(product(d));

quantity '-' = DATA NUMERIC [ 14 , 3 ] (OrderDetail);

Prosseguimos diretamente na construção do formulário de edição de pedidos de que precisamos. Primeiro, adicione o próprio pedido e suas linhas a ele:
FORM order ''
OBJECTS o = Order PANEL
PROPERTIES (o) date, number, nameSupplier

OBJECTS d = OrderDetail
PROPERTIES (d) nameProduct, quantity, NEW , DELETE
FILTERS order(d) = o

EDIT Order OBJECT o
;

Assim, obtemos uma interface padrão para trabalhar com linhas de pedidos através da adição e remoção.
Em seguida, adicionamos a funcionalidade de seleção necessária neste formulário. Para fazer isso, primeiro crie um objeto no formulário com uma lista de todos os produtos, nos quais filtramos apenas aqueles que podem pedir ao fornecedor selecionado:
EXTEND FORM order
OBJECTS p = Product
PROPERTIES name(p) READONLY
FILTERS in(supplier(o), p) //
;

Configuramos o design para que as linhas de pedido e a seleção sejam exibidas como guias de um contêiner:
DESIGN order {
OBJECTS {
NEW pane { //
fill = 1 ; //
type = TABBED ; // ,
MOVE BOX (d); //
MOVE BOX (p) { //
caption = '' ;
}
}
}
}

Construímos propriedades auxiliares para exibir e inserir a quantidade pelo usuário na coluna correspondente:
quantity '-' (Order o, Product p) =
GROUP SUM quantity(OrderDetail d) BY order(d), product(d);
lastOrderDetail ' ' (Order o, Product p) =
GROUP LAST OrderDetail d ORDER d BY order(d), product(d);

A primeira propriedade considera a quantidade para este pedido e produto neste documento. O segundo encontra a última linha (o usuário pode inserir várias linhas com um produto).
Em seguida, criamos uma ação que processará o usuário digitando o valor na coluna correspondente na guia de seleção:
changeQuantity ' -' (Order o, Product p) {
INPUT q = NUMERIC [ 14 , 3 ] DO { //
IF lastOrderDetail(o, p) THEN { // ,
IF q THEN //
quantity(OrderDetail d) <- q IF d = lastOrderDetail(o, p)
WHERE order(d) = o AND product(d) = p; //
ELSE // -
DELETE OrderDetail d WHERE order(d) = o AND product(d) == p;
} ELSE
IF q THEN
NEW d = OrderDetail { //
order(d) <- o;
product(d) <- p;
quantity(d) <- q;
}
}
}

Por fim, adicionamos uma coluna ao formulário, indicando a ação que deve ser executada quando o usuário tenta alterar seu valor:
EXTEND FORM order
PROPERTIES (o, p) quantity ON CHANGE changeQuantity(o, p)
;

Resta apenas desenhar um formulário com uma lista de pedidos e adicioná-lo ao navegador:
FORM orders ''
OBJECTS o = Order
PROPERTIES (o) READONLY date, number
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

NAVIGATOR {
NEW orders;
}

O formulário resultante será mais ou menos assim:

imagem

Quaisquer alterações feitas em qualquer uma das guias serão refletidas automaticamente na outra.

Em sistemas ERP reais, significativamente mais colunas e outros elementos são adicionados ao formulário. Por exemplo, em uma dessas implementações, fica assim:

imagem


Vamos ver como essa funcionalidade é implementada em outros aplicativos de negócios.

1C


Nas diferentes configurações de 1C, a lógica de seleção tem certas diferenças, mas o princípio é mais ou menos o mesmo. Há um botão Selecionar no formulário de edição de documento, que abre uma caixa de diálogo com a escolha de mercadorias:

imagem

Nesta caixa de diálogo, você pode selecionar as mercadorias da seguinte forma:

imagem

Há pelo menos dois inconvenientes nesse mecanismo.

Em primeiro lugar, para adicionar a quantidade do produto ao documento (e na maioria das vezes o usuário já sabe a quantidade que deseja adicionar), primeiro clique duas vezes no produto, adicione-o às linhas e altere a quantidade nas linhas. Além disso, o usuário não vê na lista de produtos se esse produto já foi adicionado e com qual quantidade. Além disso, esse esquema “consome” espaço adicional, pois é necessário exibir duas tabelas em uma tela em vez de uma.

Em segundo lugar, depois que as linhas forem adicionadas diretamente ao documento, se você clicar no botão Selecionar novamente, a seleção começará "do zero". Não aparecerá nenhuma linha na tabela inferior e, no momento da re-seleção, não ficará claro o que já está no documento e o que não está. Ele também viola uma das regras importantes ao criar a interface: se o usuário estiver enganado, ele precisará ter a oportunidade de voltar e corrigir o erro. Aqui, ele não poderá retornar à seleção de mercadorias com os mesmos dados de antes de pressionar o botão Adicionar ao documento.

No entanto, é possível no 1C você poder implementar o esquema acima, mas, por algum motivo, nas configurações típicas disponíveis no site com demos, outra é usada.

Microsoft Dynamics 365


Como solução, peguei o Retail (apenas encontrei funcionalidades semelhantes disponíveis em https://trials.dynamics.com ). Nele, você pode chamar a seleção no formulário Pedidos de compra de duas maneiras:

imagem

O primeiro botão é implementado de acordo com o esquema 1C (embora haja muito menos colunas), por isso não vou insistir nele.

O segundo botão exibe uma caixa de diálogo com uma lista de produtos onde a quantidade já pode ser inserida em colunas. Mas isso é feito de uma maneira muito peculiar.

Primeiro, por padrão, apenas os códigos do produto são exibidos lá (mesmo sem nomes). Claro, eu entendo que você pode criar extensões e incluir outras colunas lá, mas ver esse comportamento na versão básica é um pouco estranho.

Em segundo lugar, a Microsoft aparentemente ficou tão empolgada com o design que, por algum motivo, obteve pontuação no processamento normal de rolagem de dados, truques e eventos de processamento. Parece algo como isto:

imagem

Durante a operação normal do teclado, as séries atuais são constantemente trocadas ao reler dados. Ao mesmo tempo, você pode começar a inserir somente depois de aguardar todo o processamento do evento, caso contrário, a quantidade não será inserida ou inserida na série incorreta. Como usuários reais trabalham com isso continua sendo um mistério para mim. Aparentemente, poucas pessoas usam essa funcionalidade especificamente. Bem, ou são as limitações da demonstração, mas em operação tudo funciona bem.

Conclusão


O esquema de entrada de documentos descrito através de uma guia de seleção separada foi bem apreciado por nossos usuários.

Na prática, esse conceito pode ser complicado de várias maneiras. Por exemplo, em vez de uma lista de produtos, exiba uma lista de artigos com a capacidade de inserir quantidades diferentes para características individuais. Ou em um dos documentos havia armazéns nas linhas e era necessário definir as quantidades para cada um desses armazéns nas colunas.

Esse esquema pode ser implementado, por exemplo, no mesmo React, usando as linhas atuais do documento como um estado e exibindo-o com dois componentes em duas guias diferentes. Mas deve-se lembrar que pode haver muitas entradas na tabela com mercadorias, e você terá que implementar o chamado "rolagem infinita". Além disso, é aconselhável adicionar ao usuário a capacidade de classificar e filtrar produtos nesta lista (e isso deve ser feito no servidor e não no cliente, para não arrastar dados extras para lá). Tudo isso se torna uma tarefa não trivial para o desenvolvedor.

Na plataforma lsFusion, essa funcionalidade é implementada imediatamente e requer apenas o código descrito acima. Você pode tentar como funciona e, se desejar, pode modificar o código on-line na página correspondente. Aqui está o código fonte inteiro, que pode ser inserido na guia Plataforma e, em seguida, clicando em Reproduzir:

Código fonte
CLASS Product '' ;
name '' = DATA STRING [ 50 ] (Product);

FORM product ''
OBJECTS p = Product PANEL
PROPERTIES (p) name

EDIT Product OBJECT p //
;

FORM products ''
OBJECTS p = Product
PROPERTIES (p) READONLY name
PROPERTIES (p) NEWSESSION NEW , EDIT , DELETE

LIST Product OBJECT p // ,
;

NAVIGATOR {
NEW products;
}

CLASS Supplier '' ;
name '' = DATA STRING [ 50 ] (Supplier);

in '' = DATA BOOLEAN (Supplier, Product); // TRUE,

FORM supplier ''
OBJECTS s = Supplier PANEL
PROPERTIES (s) name

OBJECTS p = Product //
PROPERTIES in(s, p), name(p) READONLY //

EDIT Supplier OBJECT s
;

FORM suppliers ''
OBJECTS s = Supplier
PROPERTIES (s) READONLY name
PROPERTIES (s) NEWSESSION NEW , EDIT , DELETE

LIST Supplier OBJECT s
;

NAVIGATOR {
NEW suppliers;
}

CLASS Order '' ;
date '' = DATA DATE (Order);
number '' = DATA INTEGER (Order);

supplier '' = DATA Supplier (Order);
nameSupplier '' (Order o) = name(supplier(o));

CLASS OrderDetail ' ' ;
order '' = DATA Order (OrderDetail) NONULL DELETE ;

product '' = DATA Product (OrderDetail);
nameProduct '' (OrderDetail d) = name(product(d));

quantity '-' = DATA NUMERIC [ 14 , 3 ] (OrderDetail);

FORM order ''
OBJECTS o = Order PANEL
PROPERTIES (o) date, number, nameSupplier

OBJECTS d = OrderDetail
PROPERTIES (d) nameProduct, quantity, NEW , DELETE
FILTERS order(d) = o

EDIT Order OBJECT o
;

EXTEND FORM order
OBJECTS p = Product
PROPERTIES name(p) READONLY
FILTERS in(supplier(o), p) //
;

DESIGN order {
OBJECTS {
NEW pane { //
fill = 1 ; //
type = TABBED ; // ,
MOVE BOX (d); //
MOVE BOX (p) { //
caption = '' ;
}
}
}
}

quantity '-' (Order o, Product p) =
GROUP SUM quantity(OrderDetail d) BY order(d), product(d);
lastOrderDetail ' ' (Order o, Product p) =
GROUP LAST OrderDetail d ORDER d BY order(d), product(d);

changeQuantity ' -' (Order o, Product p) {
INPUT q = NUMERIC [ 14 , 3 ] DO { //
IF lastOrderDetail(o, p) THEN { // ,
IF q THEN //
quantity(OrderDetail d) <- q IF d = lastOrderDetail(o, p)
WHERE order(d) = o AND product(d) = p; //
ELSE // -
DELETE OrderDetail d WHERE order(d) = o AND product(d) == p;
} ELSE
IF q THEN
NEW d = OrderDetail { //
order(d) <- o;
product(d) <- p;
quantity(d) <- q;
}
}
}

EXTEND FORM order
PROPERTIES (o, p) quantity ON CHANGE changeQuantity(o, p)
;

FORM orders ''
OBJECTS o = Order
PROPERTIES (o) READONLY date, number
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

NAVIGATOR {
NEW orders;
}

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


All Articles