Ver Mensaje Individual
  #7  
Antiguo 14-09-2008
lbuelvas lbuelvas is offline
Miembro
 
Registrado: may 2003
Ubicación: Colombia
Posts: 378
Reputación: 24
lbuelvas Va por buen camino
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 [-]
/******************************************************************************/
/*                                   Tables                                   */
/******************************************************************************/

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
);

/******************************************************************************/
/*                                Primary Keys                                */
/******************************************************************************/
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);

/******************************************************************************/
/*                            Triggers for tables                             */
/******************************************************************************/

SET TERM ^ ;

/* Trigger: MOVIMIENTO_BI0 */
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
^


/* Trigger: MOVIMIENTO_BU0 */
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 ; ^

/******************************************************************************/
/*                            DATOS                                           */
/******************************************************************************/
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.
__________________
Luis Fernando Buelvas T.
Responder Con Cita