Estimada Lista,

Tengo una funcion que me actualiza saldos de varios productos, le envio como parametros un nro de pedido y la funcion lee todos los productos relacionados con ese nro de pedido descontando uno a uno de la tabla de saldos la cantidad solicitada.

Mi problema es que si alguna de las cantidades solicitadas excede al stock, la funcion debe hacer un ROLLBACK a los registros que previamente han sido procesados como validos dentro del mismo nro de pedido. He intentado ponerle un ROLLBACK, pero me devuelve un error y cuelga mi aplicativo.

Ejemplo: El pedido 100 tiene los siguientes registros:

CodProd Descripcion   Cantidad      Stock Disponible
1001    Producto A     20            50
1002    Producto B     10            37
1003    Producto C     25            12
1015    Producto X     15            10

La funcion procesa los dos primeros registros sin problema, pero el tercero no debe procesarlo xq no hay stock disponible, es ahi que deseo hacer un ROLLBACK para deshacer los registros anteriores y abortar la funcion.

Por favor, les agradecere mucho me puedan ayudar a resolver este problema.

Saludos,

Marcos Rios Rodriguez
Arequipa - Peru

CREATE OR REPLACE FUNCTION actualizar_saldos_online(integer, integer, integer, integer, character varying)
  RETURNS smallint AS
$BODY$
DECLARE
        v_empresa            ALIAS FOR $1;
        v_sucursal           ALIAS FOR $2;
        v_interno            ALIAS FOR $3;
        v_movimiento         ALIAS FOR $4;
        v_usuario            ALIAS FOR $5;
        reg_pedidos          RECORD;
        reg_saldos           RECORD;
        reg_lotes            RECORD;
        reg_movimientos      RECORD;
        reg_saldos_online    RECORD;
        v_saldo_pd           int4 NOT NULL DEFAULT 0;
        v_saldo_pdlote       int4 NOT NULL DEFAULT 0;
BEGIN
   RAISE NOTICE 'Filtrando Salidas';
IF v_movimiento IN (2,3,7,15,17,18) THEN --// Ventas Oficina, Obsequios, Traslados entre Depositos, Consignaciones, Autoventa y Preventa //--
      FOR reg_pedidos IN SELECT
            a.cod_producto
          , a.cantidad
          , b.fecha_emision
      FROM
            detalle_pedidos a
          , pedidos b
      WHERE a.empresa = v_empresa
        AND a.sucursal = v_sucursal
        AND a.pedido = v_interno
        AND NOT a.procesado
        AND NOT a.anulado
        AND b.empresa = a.empresa
        AND b.sucursal = a.sucursal
        AND b.pedido = a.pedido
      LOOP
               SELECT INTO reg_saldos *
           FROM saldos_online
                WHERE empresa = v_empresa
                  AND sucursal = v_sucursal
                  AND cod_producto = reg_pedidos.cod_producto;
               IF reg_saldos.saldo_apto >= reg_pedidos.cantidad THEN
                  UPDATE saldos_online
               SET saldo_apto = saldo_apto - reg_pedidos.cantidad,
                   ultima_salida = reg_pedidos.cantidad
             WHERE empresa = v_empresa
               AND sucursal = v_sucursal
               AND cod_producto = reg_pedidos.cod_producto;
         ELSE
            ROLLBACK;  -- ME ARROJA UN ERROR.
RETURN 0; --// No existe Saldo suficiente para atender el pedido //--
         END IF;
         RAISE NOTICE 'Actualizando detalle_pedidos';
         UPDATE detalle_pedidos
            SET procesado = true
          WHERE empresa = v_empresa
            AND sucursal = v_sucursal
            AND pedido = v_interno
            AND cod_producto = reg_pedidos.cod_producto;
         RAISE NOTICE 'Actualizando pedidos';
         UPDATE pedidos
            SET procesado = true
          WHERE empresa = v_empresa
            AND sucursal = v_sucursal
            AND pedido = v_interno;
         RAISE NOTICE 'Actualizando productos';
         IF v_movimiento IN (2,3,15,17,18) THEN
            UPDATE productos
               SET fecha_ultima_venta = reg_pedidos.fecha_emision
             WHERE empresa = v_empresa
               AND cod_producto = reg_pedidos.cod_producto;
         END IF;
         RAISE NOTICE 'Grabando movi_prod';
               INSERT INTO movi_prod
         VALUES
              (
                nextval('serie_moviprod')
              , v_empresa
              , v_sucursal
              , reg_pedidos.fecha_emision
              , reg_pedidos.cod_producto
              , reg_pedidos.cantidad
              , 0
              , 0
, CASE WHEN v_movimiento = 2 THEN 'VENTAS OFICINA' WHEN v_movimiento = 3 THEN 'TRANSFERENCIA GRATUITA' WHEN v_movimiento = 7 THEN 'TRASLADO ENTRE DEPOSITOS' WHEN v_movimiento = 15 THEN 'CONSIGNACIONES' WHEN v_movimiento = 17 THEN 'FACTURACION DE AUTOVENTA' ELSE 'FACTURACION DE PREVENTA' END
              , 90
              , 1
              , v_interno
              , 'A'
              , v_movimiento
              , false
              , v_usuario
              , CURRENT_DATE
              , CURRENT_TIME
              );
         RAISE NOTICE 'Filtrando Lotes';
         v_saldo_pd := reg_pedidos.cantidad;
         v_saldo_pdlote := reg_pedidos.cantidad;
         FOR reg_lotes IN SELECT
              a.lote
            , a.vencimiento
            , a.saldo_final_apto
         FROM
              vencimientos a
            , productos b
         WHERE a.empresa = v_empresa
           AND a.sucursal = v_sucursal
           AND a.cod_producto = reg_pedidos.cod_producto
           AND a.saldo_final_apto > 0
           AND b.empresa = a.empresa
           AND b.cod_producto = a.cod_producto
           AND b.controla_lote
         ORDER BY a.empresa, a.sucursal, a.vencimiento, a.lote ASC
         LOOP
            IF v_saldo_pd > 0 THEN
v_saldo_pd := CASE WHEN reg_lotes.saldo_final_apto <= v_saldo_pd THEN v_saldo_pd - reg_lotes.saldo_final_apto ELSE 0 END;
               RAISE NOTICE 'Actualizar vencimientos';
               UPDATE vencimientos
SET saldo_final_apto = CASE WHEN reg_lotes.saldo_final_apto >= v_saldo_pdlote THEN reg_lotes.saldo_final_apto - v_saldo_pdlote ELSE 0 END
                WHERE empresa = v_empresa
                  AND sucursal = v_sucursal
                  AND cod_producto = reg_pedidos.cod_producto
                  AND vencimiento = reg_lotes.vencimiento
                  AND lote = reg_lotes.lote;
               SELECT INTO reg_movimientos *
                 FROM mov_vencimientos
                WHERE empresa = v_empresa
                  AND sucursal = v_sucursal
                  AND cod_movimiento = v_movimiento
                  AND pedido = v_interno
                  AND cod_producto = reg_pedidos.cod_producto
                  AND vencimiento = reg_lotes.vencimiento
                  AND lote = reg_lotes.lote;
               IF FOUND THEN
                  RAISE NOTICE 'Actualizar movimiento';
                  UPDATE mov_vencimientos
                     SET cantidad = cantidad + (v_saldo_pdlote - v_saldo_pd)
                   WHERE empresa =v_empresa
                     AND sucursal = v_sucursal
                     AND cod_movimiento = v_movimiento
                     AND pedido = v_interno
                     AND cod_producto = reg_pedidos.cod_producto
                     AND vencimiento = reg_lotes.vencimiento
                     AND lote = reg_lotes.lote;
               ELSE
                  RAISE NOTICE 'Grabar movimiento';
                  INSERT INTO mov_vencimientos
                  VALUES(
                          v_empresa
                        , v_sucursal
                        , v_movimiento
                        , v_interno
                        , reg_pedidos.cod_producto
                  , v_saldo_pdlote - v_saldo_pd
                        , reg_lotes.vencimiento
                        , reg_lotes.lote
                        );
               END IF;
            END IF;
            v_saldo_pdlote := v_saldo_pd;
         END LOOP;
      END LOOP;
   END IF;
   RAISE NOTICE 'Proceso Finalizado';
   RETURN 1;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION actualizar_saldos_online(integer, integer, integer, integer, character varying) OWNER TO postgres;


--
TIP 5: ¿Has leído nuestro extenso FAQ?
        http://www.postgresql.org/docs/faqs.FAQ.html

Responder a