Em várias aplicações, surge regularmente a tarefa de apoiar a lógica da mudança no tempo de algum atributo de um objeto em relação a um determinado assunto (ou assuntos). Por exemplo, isso pode ser uma alteração no preço de varejo de mercadorias nas lojas ou indicadores de KPI para funcionários.
Neste artigo, mostrarei que lógica e interfaces de domínio podem ser criadas para resolver esse problema. Farei imediatamente uma reserva de que isso envolverá a influência gerencial do usuário no atributo, e não o reflexo da mudança histórica.
A implementação será apresentada com base na plataforma aberta e gratuita da
lsFusion , mas um esquema semelhante pode ser aplicado ao usar qualquer outra tecnologia.
1. Introdução
Para uma apresentação e compreensão mais simples do artigo, consideramos o preço como um atributo, o produto como o objeto e o armazém será o assunto. Nesse caso, o intervalo mínimo possível para definir o atributo será a data. Assim, o usuário poderá determinar qual será o preço para uma data específica para qualquer produto e armazém.
O esquema de entrada do usuário para alterações de preço será semelhante ao usado nos sistemas de controle de versão clássicos. Qualquer alteração, do ponto de vista da lógica do domínio, será uma única
confirmação , com base na qual o status para uma determinada data será calculado. Em muitas áreas, tais confirmações são chamadas de documentos ou transações. Nesse caso, com esse commit, queremos dizer a chamada lista de preços. Cada lista de preços especificará as mercadorias e os depósitos incluídos nela, bem como o período de validade.
O esquema descrito possui as seguintes vantagens:
- Atomicidade . Cada alteração é emitida como um documento separado. Portanto, esses documentos podem ser salvos temporariamente, mas não lançados. Com uma entrada incorreta, é fácil reverter toda a alteração.
- Transparência É fácil determinar quem fez a alteração e quando, além de indicar o motivo, fazendo um comentário no documento.
A principal diferença do sistema de controle de versão é que confirmações explícitas são independentes uma da outra. Assim, é possível excluir todos os commits de maneira relativamente indolor a qualquer momento. Além disso, cada confirmação desse tipo pode ser configurada para terminar quando deixar de funcionar, o que obviamente não está no sistema de controle de versão.
Implementação
Iniciamos a definição de lógica de domínio com armazéns. Vamos complicar um pouco a solução combinando armazéns em uma hierarquia de um grupo de profundidade dinâmica. De acordo com o princípio, isso é descrito no
artigo correspondente, portanto, fornecerei um código que declara grupos e cria formulários para editá-los:
Anúncio do Grupo de Armazém Exemplo de hierarquia de grupo Em seguida, declare armazéns que podem ser vinculados a qualquer um dos grupos:
E, finalmente, declare a lógica dos bens:
Prosseguimos diretamente na criação da lógica das listas de preços. Primeiro, definimos a própria classe de
lista de preços , bem como seu período de validade:
Acreditamos que, se nenhuma
data foi definida, a lista de preços é interminável.
Adicionamos um evento que, ao criar a lista de preços, anulará automaticamente a data atual a partir da qual começará a operar.
A palavra-chave
LOCAL significa que o evento não será acionado quando o salvamento for aplicado ao banco de dados, mas imediatamente quando a alteração for feita.
Em seguida, adicione o usuário que o criou e o horário da criação:
Agora crie um evento que os preencherá automaticamente:
Este evento, diferente do anterior, será acionado apenas quando o botão Salvar for clicado. Ou seja, durante uma transação salva no banco de dados.
Em seguida, crie as linhas da lista de preços nas quais os bens e os preços serão definidos:
O atributo
NONULL indica que a propriedade
priceList sempre
deve ser definida e
DELETE indica que quando o valor da propriedade é zerado (por exemplo, ao excluir a lista de preços), a linha correspondente deve ser excluída automaticamente.
Para uso futuro, criaremos propriedades que determinarão o período de validade das linhas da lista de preços:
Agora, vincularemos a lista de preços aos armazéns para os quais operará. Primeiro, adicione a propriedade principal, o que será verdadeiro se todo o grupo de armazéns estiver incluído na lista de preços:
Calculamos a “inclusão” do grupo levando em consideração os pais selecionados (conforme descrito no artigo sobre hierarquias):
Adicione a propriedade principal, com a qual você pode especificar que a lista de preços atue em um armazém específico:
Calculamos a propriedade final, que determinará que a lista de preços altere os preços no armazém correspondente, levando em consideração os grupos:
Crie uma propriedade que mostre os nomes de todos os grupos e depósitos selecionados da lista de preços, para que um usuário mais conveniente exiba a lista de listas de preços:
A etapa final na descrição da lógica do domínio calculará diretamente o preço atual das mercadorias no armazém. Para fazer isso, crie uma propriedade que encontre a última linha de data da lista de preços com as mercadorias, o armazém e o período de validade desejados:
Na lógica do cálculo dessa propriedade, várias variações são possíveis. Você pode alterar o filtro para acessar as linhas (por exemplo, adicionar uma condição em
WHERE que a lista de preços seja lançada) e o pedido. Deve-se notar que o próprio objeto, ou melhor, seu identificador interno, foi adicionado à ordem de seleção pelo segundo parâmetro. Isso é necessário para que o valor do preço seja sempre determinado de uma maneira única.
Com base na linha de lista de preços recebida, determinamos o valor do preço e seu período de validade:
Eles serão mais usados em tabelas de interface do usuário.
Em seguida, passamos à criação da interface do usuário. Primeiro, desenhamos um formulário para editar a lista de preços. Crie um formulário e adicione o "cabeçalho" do documento lá:
Adicione a linha da lista de preços ao formulário:
Em seguida, adicione uma árvore na qual haverá grupos e armazéns:
Propriedades para grupos e armazéns são adicionadas à árvore ao mesmo tempo. A plataforma, dependendo do objeto, mostrará essa ou aquela propriedade na ordem em que forem adicionadas ao formulário.
Personalizamos o design do formulário para que mercadorias e armazéns sejam desenhados em guias separadas:
O formulário de edição ficará assim:
Resta criar a forma básica de gerenciamento de preços. Ele será composto por duas guias. O primeiro mostrará uma lista de todas as listas de preços (semelhante à lista de confirmações). A segunda guia exibirá os preços atuais de um armazém específico para a data selecionada.
Para implementar a primeira guia, adicione ao formulário uma lista de listas de preços com linhas para uma visualização rápida:
Para a segunda guia, primeiro adicionamos a data em que os preços são exibidos, a árvore dos grupos de depósitos e os próprios armazéns:
A lista de armazéns mostrará todos os armazéns descendentes do grupo selecionado na parte superior.
Em seguida, adicione ao formulário uma lista de mercadorias para as quais existem preços válidos para o depósito na data selecionada:
O preço em si e o período de validade são adicionados às colunas. Você também pode adicionar o número da lista de preços - essa tabela será semelhante à lógica das anotações nos sistemas de controle de versão.
Para que o usuário entenda de onde veio esse preço, adicionamos a lista de linhas da lista de preços com produtos e armazéns adequados:
Usando o atributo
BACKGROUND, destaque a linha que determinou o preço mostrado na tabela.
Além disso, para conveniência do usuário, adicionaremos a capacidade de abrir o formulário de edição da lista de preços correspondente em uma nova sessão imediatamente a partir desta história:
Para conseguir isso, você precisa especificar a ação que será executada ao tentar editar uma linha implementando a ação de
edição interna. Em seguida, um botão padrão para editar um objeto por meio de uma chamada de diálogo é adicionado ao formulário da maneira padrão.
E, finalmente, formamos o design final do formulário:
Aqui, o contêiner do
painel é adicionado primeiro, que consiste em duas guias:
priceLists e
prices . O primeiro deles apenas adiciona uma lista de listas de preços e linhas. No segundo, dois painéis são criados:
leftPane e
rightPane . O painel esquerdo contém a data e os armazéns, e o painel direito contém os bens e o histórico de preços.
Resultado
Considere as principais opções para usar a lógica resultante.
Suponha que tenhamos duas listas de preços separadas para diferentes grupos de mercadorias. Em seguida, dependendo do armazém selecionado, na guia preços, apenas os produtos das listas de preços correspondentes serão exibidos:
Agora crie uma nova lista de preços com um período de validade limitado, uma lista simplificada de armazéns e um novo preço. Na segunda guia, se selecionarmos uma data no intervalo da nova lista de preços, obteremos um novo preço. Assim que o período de validade expirar, o preço antigo retornará novamente do preço original:
Usando o mesmo mecanismo, você pode "cancelar" a ação de preços específicos a partir de uma determinada data. Por exemplo, se você inserir um novo preço sem especificar um preço, o preço será redefinido e as mercadorias desaparecerão do filtro. Nesse caso, ao excluir o documento digitado, tudo volta ao estado antigo:
A propriedade resultante com o preço das mercadorias pelo armazém na data pode ser usada ainda mais em vários eventos ou outras formas. Por exemplo, você pode fazer preços automáticos em um pedido com base nesta lógica de preços:
Um bom bônus nessa lógica é que, quando você adiciona um novo armazém ao grupo, os preços das listas de preços já criadas se aplicam automaticamente a ele. O mesmo acontecerá quando você mudar o grupo para o armazém.
Se desejar, você pode tornar a coluna com o preço na guia com preços atuais editável e adicionar um botão que criará um novo commit para os preços alterados.
Conclusão
Na solução no nível da plataforma, nem os livros de referência, nem os documentos com seqüências de caracteres, nem registros, nem relatórios e outras abstrações desnecessárias são usados. Tudo é feito exclusivamente nos conceitos de classes e propriedades. Observe que essa lógica bastante complexa foi implementada em aproximadamente 150 linhas de código significativas no lsFusion. Implementá-lo na mesma formulação em outras plataformas (por exemplo, 1C) é uma tarefa muito mais difícil.
O esquema descrito acima é amplamente utilizado na
solução ERP baseada em lsFusion. Utilizando-o, com várias modificações, são suportadas listas de preços de fornecedores, preços de varejo de gerenciamento, ações e muitos outros parâmetros de gerenciamento.
O modelo pode ser complicado adicionando várias entidades ao documento (por exemplo, um fornecedor pode ser adicionado ao armazém), além de definir vários atributos em um documento ao mesmo tempo. Em particular, você pode adicionar a entidade Tipo de preço e, na linha do documento, definir o preço da tupla da linha e o tipo de preço correspondente. Na lógica descrita acima, você só precisa adicionar alguns parâmetros adicionais a algumas propriedades.
Com a ajuda de várias linhas de código adicionais, é possível desnormalizar todos os registros de alterações em uma tabela na qual criar o índice correspondente. Em seguida, a seleção de qualquer valor para qualquer data será feita em um horário logarítmico. Essa otimização é necessária quando existem várias centenas de milhões de registros nesta tabela.
Você pode experimentar o exemplo criado online na
página correspondente
do site (seção Plataforma). Aqui está o código fonte completo que você precisa colar no campo desejado: