RES: [firebase-br] "Semáforo" no Firebird - RESOLVIDO

Rodrigo A. de Freitas rodrigo em solucoeseinformatica.com.br
Qui Jul 19 19:20:32 -03 2007


Sim Otto, eu trabalho com partidas dobradas; Mas da forma como eu estruturei
o banco de dados, eu gero o número do lançamento (como se fosse o "RG" do
lançamento) primeiro, e depois disparo a gravação das partidas com este
número. 

Infelizmente eu não posso alterar o formato desse código, visto que a minha
aplicação substituiu outra que implementava esta formatação e o cliente não
quis nem discutir o assunto.

De qualquer forma, eu consegui resolver o problema; Obrigado a todos que
responderam e ajudaram de alguma forma. A solução foi a seguinte:

1) Criei uma tabela de semáforo para centralizar os números de lançamentos
de todo o sistema (na verdade eu me lembrei que o comportamento ocorria
também para contas a pagar, receber, financeiro, etc.) com a seguinte
estrutura:

CREATE TABLE MAN_SEMAFORO_LANCTOS (
    ID             INTEGER NOT NULL,
    COD_EMPRESA    INTEGER NOT NULL,
    NOME_MODULO    VARCHAR(30) NOT NULL,
    ANO            INTEGER DEFAULT 0 NOT NULL,
    NUMERO_LANCTO  INTEGER DEFAULT 0 NOT NULL
);

O Id é uma chave primária só para controle, baseada num generator.
Acrescentei uma coluna para especificar qual o nome do módulo eu irei obter
o número do lançamento.

2) Criei uma stored procedure que gerencie as requisições do sistema,
fazendo LOCK explícito e tratando esse travamento através de uma cláusula
WHEN DO, para que o erro não seja propagado para as outras stored
procedures:


CREATE PROCEDURE PR_RETORNA_NUMERO_LANCTO (
    COD_EMPRESA INTEGER,
    ANO INTEGER,
    NOME_MODULO VARCHAR(30))
RETURNS (
    NUMERO_LANCTO INTEGER)
AS
DECLARE VARIABLE COD_LANCAMENTO INTEGER;
BEGIN
   SELECT NUMERO_LANCTO FROM MAN_SEMAFORO_LANCTOS
   WHERE NOME_MODULO = :NOME_MODULO
     AND ANO = :ANO
     AND COD_EMPRESA = :COD_EMPRESA
   FOR UPDATE WITH LOCK
   INTO :COD_LANCAMENTO;

   COD_LANCAMENTO = COD_LANCAMENTO + 1;

   UPDATE MAN_SEMAFORO_LANCTOS SET NUMERO_LANCTO = :COD_LANCAMENTO
   WHERE NOME_MODULO = :NOME_MODULO
     AND ANO = :ANO
     AND COD_EMPRESA = :COD_EMPRESA;

   NUMERO_LANCTO = COD_LANCAMENTO;

   SUSPEND;

   WHEN ANY DO
   BEGIN
     NUMERO_LANCTO = 0;
     SUSPEND;
   END
END

3) Comecei a substituir as stored procedures no sistema nos módulos onde faz
referência ao número de lançamento. O exemplo abaixo é o fragmento de código
de uma procedure que gera a contabilidade à partir do contas a pagar:

...

CTB_COD_LANCAMENTO = 0;

WHILE (CTB_COD_LANCAMENTO = 0) DO
BEGIN
   SELECT NUMERO_LANCTO
   FROM PR_RETORNA_NUMERO_LANCTO (:COD_EMPRESA, :CTB_EXERCICIO,
'CONTABILIDADE')
   INTO :CTB_COD_LANCAMENTO;
END

...

4) Dentro da aplicação, nas rotinas que disparam as stored procedures eu
acrescentei uma janela com um gif animado; Se o processo demorar mais que 3
segundos eu dou uma mensagem dizendo pro usuário aguardar pois outro usuário
está efetuando um lançamento no sistema.

5) Uma rotina à parte de 'abertura de exercício' vai ser criada com o
propósito de gerar registros para as filiais com os novos exercícios na
tabela MAN_SEMAFORO_LANCTOS.

Dessa forma, eu consigo resolver o problema de mais de um usuário gerar o
mesmo número de identidade do lançamento. Se o Firebird permitisse, o ideal
seria prender a procedure que retorna o lançamento no contexto de uma
transação auxiliar, assim após gerar o ID eu commitaria a transação e libero
ela para o outro usuário, fazendo com que no caso de rotinas complexas (como
por exemplo, a emissão de cheques, onde eu tenho que fazer em um lote só
lançamentos contábeis para 20, 30, às vezes 40 cheques além dos outros
processos adicionais, como gravar financeiro, baixar contas à pagar, etc.).

[]'s

Rodrigo A. de Freitas
Análise & Desenvolvimento

Soluções & Informática

-----Mensagem original-----
De: lista-bounces em firebase.com.br [mailto:lista-bounces em firebase.com.br] Em
nome de Otto Fuchshuber
Enviada em: quinta-feira, 19 de julho de 2007 18:03
Para: FireBase
Assunto: Re: [firebase-br] "Semáforo" no Firebird

Na realidade a situação é ainda muito mais complexa. O método de 
contabilização é chamado de partidas dobradas, pois para cada conta 
debitada haverá pelo menos uma conta creditada. Um lançamento se compõe 
de partidas e contrapartidas. E temos, dessa forma, as quatro fórmulas 
de lançamento:

1a fórmula: uma conta debitada e uma conta creditada (uma partida e uma 
contrapartida)
2a fórmula uma conta debitada e várias contas creditadas (uma partida e 
muitas contrapartidas)
3a fórmula várias contas debitadas e uma conta creditada (muitas 
partidas e uma contrapartida)
4a fórmula várias contas creditadas e várias contas creditadas.(muitas 
partidas e muitas contrapartidas)

Na primeira fórmula, pelo menos os registros de duas contas tem que ser 
bloqueadas.
Nas demais fórmulas qualquer quantidade de contas - no mínimo duas - tem 
que ser bloqueada.

Mesmo num banco de dados que permite um bloqueio a nível de registro, 
isto é muito complexo, tanto para inclusões e alterações quanto para 
exclusões. Teóricamente, é possivel englobar todas partidas de um ano 
num único lançamento de quarta fórmula, o que significaria bloquear 
todas as contas analíticas do plano de contas.

Não existe nas normas contábeis a exigência da numeração das partidas 
sequencialmente sem lacunas. Assim tem-se mais liberdade para construir 
uma chave primária que tem por finalidade a unicidade, ou seja, que não 
haja a permissão de repetição. Embora o número fique enorme, nada impede 
que se inclua a empresa, o ano, o mês, o dia, o número do digitador, e 
hora, o minuto e o segundo. Difícilmente vai haver dois iguais, pois o 
número do digitador fará a diferença -- ele não conseguirá fazer dois 
lançamentos ao mesmo tempo. Isto é apenas um exemplo de uma possibilidade.

[]´s

Otto






Mais detalhes sobre a lista de discussão lista