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