Bueno, ya terminé.
Solo coloqué la tabla de movimientos por simplicidad, ustedes pueden imaginar como deseen la tabla de bodegas, productos, las facturas, las entradas, etc.
Vamos a suponer 3 productos (X, Y, Z), dos bodegas (A, B), algunos movimientos de entrada y otros de salida.
El campo tipo de movimiento es E para entrada y S para salida.
Código SQL
[-]
CREATE GENERATOR GEN_MOVIMIENTO;
CREATE TABLE MOVIMIENTO (
ID_MOVIMIENTO INTEGER NOT NULL,
CODIGO_BODEGA VARCHAR(10) NOT NULL,
CODIGO_PRODUCTO VARCHAR(10) NOT NULL,
TIPO_MOVIMIENTO VARCHAR(1) NOT NULL,
FECHA_MOVIMIENTO DATE NOT NULL,
CANTIDAD INTEGER NOT NULL,
VALOR_UNITARIO NUMERIC(15,2) NOT NULL,
TOTAL NUMERIC(15,2) NOT NULL
);
CREATE TABLE SALDO (
CODIGO_BODEGA VARCHAR(10) NOT NULL,
CODIGO_PRODUCTO VARCHAR(10) NOT NULL,
FECHA DATE NOT NULL,
CANTIDAD INTEGER NOT NULL
);
ALTER TABLE MOVIMIENTO ADD CONSTRAINT PK_MOVIMIENTO PRIMARY KEY (ID_MOVIMIENTO);
ALTER TABLE SALDO ADD CONSTRAINT PK_SALDO PRIMARY KEY (CODIGO_BODEGA, CODIGO_PRODUCTO, FECHA);
SET TERM ^ ;
CREATE OR ALTER TRIGGER MOVIMIENTO_BI0 FOR MOVIMIENTO
ACTIVE BEFORE INSERT POSITION 0
AS
begin
-- El trigger siempre coloca un valor para el identificador
new.id_movimiento = gen_id(gen_movimiento, 1);
-- Calculo del total
new.total = new.cantidad * new.valor_unitario;
end
^
CREATE OR ALTER TRIGGER MOVIMIENTO_BU0 FOR MOVIMIENTO
ACTIVE BEFORE UPDATE POSITION 0
AS
begin.
-- Calculo del total
new.total = new.cantidad * new.valor_unitario;
end
^
SET TERM ; ^
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (1, 'A', 'X', 'E', '2008-01-01', 100, 1000, 100000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (2, 'A', 'Y', 'E', '2008-01-01', 200, 400, 80000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (3, 'B', 'Y', 'E', '2008-02-02', 500, 200, 100000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (4, 'B', 'Z', 'E', '2008-02-02', 400, 100, 40000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (5, 'B', 'Z', 'E', '2008-02-02', 200, 150, 30000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (6, 'A', 'Y', 'S', '2008-02-02', 80, 450, 36000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (7, 'A', 'Y', 'S', '2008-02-02', 10, 450, 4500);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (8, 'B', 'Z', 'S', '2008-02-02', 100, 1200, 120000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (9, 'B', 'Y', 'S', '2008-02-02', 5, 450, 2250);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (10, 'A', 'Z', 'E', '2008-03-03', 400, 120, 48000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (11, 'B', 'X', 'E', '2008-03-03', 300, 950, 285000);
INSERT INTO MOVIMIENTO (ID_MOVIMIENTO, CODIGO_BODEGA, CODIGO_PRODUCTO, TIPO_MOVIMIENTO, FECHA_MOVIMIENTO, CANTIDAD, VALOR_UNITARIO, TOTAL) VALUES (12, 'B', 'Z', 'S', '2008-03-03', 100, 130, 13000);
COMMIT WORK;
Este es el procedimiento almacenado
Código SQL
[-]
SET TERM ^ ;
CREATE OR ALTER PROCEDURE SP_RECONSTRUIR_SALDOS
as
declare variable codigo_bodega varchar(10);
declare variable codigo_producto varchar(10);
declare variable fecha_movimiento date;
declare variable tipo_movimiento varchar(1);
declare variable cantidad integer;
declare variable flag_producto varchar(10);
declare variable flag_bodega varchar(10);
declare variable saldo integer;
begin
-- Procedimiento que reconstruye la tabla de saldos, para que este
-- procedimietno funcione correctamente debe eliminarse todos los registros
-- de la tabla saldos.
-- Se limpian los registros en la tabla de saldos
delete from saldo;
-- Inicializacion de variables
flag_producto = null;
flag_bodega = null;
-- Normalmente en un inventario, en la misma fecha deben considerarse primero
-- las entradas y luego las salidas, la consulta que se utiliza como el orden
-- natural de las letras es primero E(ntrada) y luego S(alida) no hay que
-- hacerle nada especial al order by.
for select movimiento.codigo_bodega,
movimiento.codigo_producto,
movimiento.fecha_movimiento,
movimiento.tipo_movimiento,
sum(movimiento.cantidad)
from movimiento
group by movimiento.codigo_bodega,
movimiento.codigo_producto,
movimiento.fecha_movimiento,
movimiento.tipo_movimiento
order by movimiento.codigo_bodega,
movimiento.codigo_producto,
movimiento.fecha_movimiento,
movimiento.tipo_movimiento
into :codigo_bodega,
:codigo_producto,
:fecha_movimiento,
:tipo_movimiento,
:cantidad
do begin
-- Verifica si hubo cambio de producto o bodega, en cuyo caso se inicializa
-- una variable para saldo.
if ((flag_producto is null) or
(flag_producto <> codigo_producto) or
(flag_bodega is null) or
(flag_bodega <> codigo_bodega)) then
begin
flag_producto = codigo_producto;
flag_bodega = codigo_bodega;
saldo = 0;
end
-- Dependiendo de la naturaleza del movimiento se actualiza el saldo
if (tipo_movimiento = 'E') then
saldo = saldo + cantidad;
if (tipo_movimiento = 'S') then
saldo = saldo - cantidad;
-- Si no existe un registro para esa bodega, producto y fecha se crea el
-- registro, de lo contrario se actualiza.
-- El "select first 1 '*' ..." es una forma personal de verificar si existe
-- un registro en la tabla con las condiciones contenidas en el where, lo
-- coloco entre comillas pues el asterisco sin comillas genera más
-- trafico de datos.
if (not exists (select first 1 '*'
from saldo
where saldo.codigo_bodega = :codigo_bodega
and saldo.codigo_producto = :codigo_producto
and saldo.fecha = :fecha_movimiento
)) then
insert into saldo
(
codigo_bodega,
codigo_producto,
fecha,
cantidad
)
values
(
:codigo_bodega,
:codigo_producto,
:fecha_movimiento,
:saldo
);
else
-- Habia una registro previo
update saldo
set cantidad = :saldo
where saldo.codigo_bodega = :codigo_bodega
and saldo.codigo_producto = :codigo_producto
and saldo.fecha = :fecha_movimiento;
end
end^
SET TERM ; ^
GRANT SELECT,INSERT,DELETE,UPDATE ON SALDO TO PROCEDURE SP_RECONSTRUIR_SALDOS;
GRANT SELECT ON MOVIMIENTO TO PROCEDURE SP_RECONSTRUIR_SALDOS;
GRANT EXECUTE ON PROCEDURE SP_RECONSTRUIR_SALDOS TO SYSDBA;
Ejecuten el procedimiento almacenado para reconstruir la tabla de saldos, hechenle una revisada a ver si hay alguna inconsistencia.
Espero que sea un buen inicio y que les sea de utilidad.