PDA

Ver la Versión Completa : Migrando triggers de Firebird a PostgreSQL


macugi
02-05-2013, 11:28:28
Hola a todos.

Antes que nada perdón por la extensión del mensaje.

Estoy creando una nueva aplicación en la cual utilizo PostgreSQL 9.2.
Estoy intentando utilizar la misma "logica" que utilizaba en Firebird, pero por lo visto no me funciona en PostgreSQL.

Tengo una tabla Master llamada Albaranes y otra Detail llamada AlbaMov. Tengo definido unos triggers con sus correspondientes funciones, los cuales me actualizan la tabla Master al modificar algo en la tabla Detail. Todo funciona perfectamente excepto cuando quiero eliminar un registro de la tabla Master.

Al eliminar un registro de la tabla Master, se eliminan todos los registros de la Detail y me actualiza un campo "Total" a 0 en la tabla Master, pero no se elimina el registro de la tabla Master. Si elimino el registro de la tabla Master sin registros en la tabla Detail, se elimina sin problemas.

He estado haciendo pruebas y he visto que el problema está en el UPDATE a la tabla Master que se hace en una funcion a la que llamo CalculoAlbaranVenta.

Este mismo sistema funciona perfectamente en Firebird.

Esta funcion devuelve una variable de tipo %ROWTYPE la cual utilizo para actualizar una pantalla en PHP.

Aqui os dejo la definicion de las tablas con sus triggers y funciones.

Saludos y gracias por adelantado.

Manuel Cuevas.

-----------------------------------<>---------------------------------
CREATE OR REPLACE FUNCTION public."CalculoAlbaranVenta"
(
IN "cSerie" public."Serie",
IN "nNumeroDoc" public."NumeroDocumento"
)
RETURNS SETOF public."Totales" AS
$$
declare nBasImp "Importes";
declare nIva "Importes";
declare nRE "Importes";
declare nTotalBase "Importes";
declare nTotalIVA "Importes";
declare nTotalRE "Importes";
declare nTotalDtoBase "Importes";
declare nTotalDtoResto "Importes";
declare nTotalDtos "Importes";
declare nTotalLinea "Importes";
declare rRow RECORD;
declare rTotales "Totales"%ROWTYPE;

begin
nBasImp := 0;
nIva := 0;
nRE := 0;
nTotalBase := 0;
nTotalIVA := 0;
nTotalRE := 0;
nTotalDtoBase := 0;
nTotalDtoResto := 0;
nTotalDtos := 0;
nTotalLinea := 0;

FOR rRow IN SELECT "TotalUnidades",
"Precio",
"PorcentajeIVA",
"PorcentajeRE",
"DescuentoBase",
"DescuentoResto"
FROM "AlbaMov"
WHERE ("Serie" = "cSerie") AND ("NumeroDoc" = "nNumeroDoc") AND
("Referencia" IS NOT NULL)
LOOP
nTotalLinea := Round((rRow."TotalUnidades" * rRow."Precio")::numeric, 3);
nTotalDtoBase := Round((nTotalLinea * (rRow."DescuentoBase" / 100))::numeric, 3);
nTotalLinea := nTotalLinea - nTotalDtoBase;
nTotalDtoResto := Round((nTotalLinea * (rRow."DescuentoResto" / 100))::numeric, 3);
nTotalLinea := nTotalLinea - nTotalDtoResto;
nTotalDtos := nTotalDtos + nTotalDtoBase + nTotalDtoResto;

nBasImp := Round(nTotalLinea::numeric, 2);

nTotalBase := nTotalBase + nBasImp;
nTotalIVA := nTotalIVA + (nBasImp * rRow."PorcentajeIVA" / 100);
nTotalRE := nTotalRE + (nBasImp * rRow."PorcentajeRE" / 100);

END LOOP;

nTotalIVA := Round(nTotalIVA::numeric, 2);
nTotalRE := Round(nTotalRE::numeric, 2);
nTotalDtos := Round(nTotalDtos::numeric, 2);

UPDATE "Albaran"
SET "BaseImponible" = nTotalBase,
"TotalDescuentos" = nTotalDtos,
"IVA" = nTotalIVA,
"RE" = nTotalRE,
"Total" = nTotalBase + nTotalIVA + nTotalRE
WHERE ("Serie" = "cSerie") AND ("NumeroDoc" = "nNumeroDoc");

rTotales."TotalDescuentos" := nTotalDtos;
rTotales."BaseImponible" := nTotalBase;
rTotales."TotalIVA" := nTotalIVA;
rTotales."TotalRE" := nTotalRE;
rTotales."Total" := nTotalBase + nTotalIVA + nTotalRE;

RETURN NEXT rTotales;

end
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1;

CREATE OR REPLACE FUNCTION public."AlbaranBeforeDelete"()
RETURNS trigger AS
$$
begin
DELETE FROM "AlbaMov"
WHERE ("Serie" = OLD."Serie") AND ("NumeroDoc" = OLD."NumeroDoc");

RETURN OLD;
end
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

CREATE OR REPLACE FUNCTION public."AlbaranBeforeUpdate"()
RETURNS trigger AS
$$
begin

NEW."Total" := Round((NEW."BaseImponible" + NEW."IVA" + NEW."RE")::numeric, 2);

RETURN NEW;

end
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;

CREATE OR REPLACE FUNCTION public."AlbaMovAfterDelete"()
RETURNS trigger AS
$$
declare nTotalBase "Importes";
declare nTotalIVA "Importes";
declare nTotalRE "Importes";
declare nTotalDtoBase "Importes";
declare nTotalDtoResto "Importes";
declare nTotalDtos "Importes";
declare nTotalLinea "Importes";
declare cCliente "CodigoCliente";

begin
PERFORM "CalculoAlbaranVenta"(OLD."Serie", OLD."NumeroDoc");

nTotalLinea := Round((OLD."TotalUnidades" * OLD."Precio")::numeric, 3);
nTotalDtoBase := Round((nTotalLinea * (OLD."DescuentoBase" / 100))::numeric, 3);
nTotalLinea := nTotalLinea - nTotalDtoBase;
nTotalDtoResto := Round((nTotalLinea * (OLD."DescuentoResto" / 100))::numeric, 3);
nTotalLinea := nTotalLinea - nTotalDtoResto;
nTotalDtos := nTotalDtos + nTotalDtoBase + nTotalDtoResto;

nTotalBase := Round(nTotalLinea::numeric, 2);
nTotalIVA := (nTotalBase * OLD."PorcentajeIVA" / 100);
nTotalRE := (nTotalBase * OLD."PorcentajeRE" / 100);

nTotalIVA := Round(nTotalIVA::numeric, 2);
nTotalRE := Round(nTotalRE::numeric, 2);
nTotalDtos := Round(nTotalDtos::numeric, 2);

PERFORM "SumaArticulo"(OLD."Referencia", OLD."TotalUnidades");

SELECT "Cliente" INTO cCliente FROM "Albaran"
WHERE ("Serie" = OLD."Serie") AND ("NumeroDoc" = OLD."NumeroDoc");

PERFORM "RestaCliente"(cCliente, nTotalBase + nTotalIVA + nTotalRE);

RETURN OLD;
end
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;


CREATE TABLE public."Albaran" (
"NumeroDoc" public."NumeroDocumento" NOT NULL,
"Serie" public."Serie" NOT NULL,
"Fecha" date NOT NULL,
"Cliente" public."CodigoCliProv" NOT NULL,
"Nombre" public."RazonSocial",
"BaseImponible" public."Importes",
"IVA" public."Importes",
"RE" public."Importes",
"Notas" public."Memo",
"CodigoDir" public."CodigoDireccion",
"Direccion" public."Direccion",
"Poblacion" public."Poblacion",
"CodigoPostal" public."CodigoPostal",
"Provincia" public."Provincia",
"Pais" public."Pais",
"CIF" public."CIF",
"Total" public."Importes",
"Agente" public."CodigoAgente",
"SuNumeroPedido" public."SuNumeroPedido",
"Telefono" public."Telefono",
"Fax" public."Telefono",
"FormaPago" public."FormaPago",
"Transportista" public."CodigoTransporte",
"Repartidor" public."CodigoRepartidor",
"Portes" public."Importes",
"DebidosPagados" public."Boolean",
"Gastos" public."Importes",
"TotalDescuentos" public."Importes",
"TotalPesoNeto" public."Peso",
"TotalPesoBruto" public."Peso",
"Facturado" public."Boolean",
"Modificado" public."Boolean"
/* Llaves */
CONSTRAINT "PK_Albaran"
PRIMARY KEY ("Serie", "NumeroDoc")
) WITH (
OIDS = FALSE
);

CREATE INDEX "IDX_Albaran_Nombre"
ON public."Albaran"
("Nombre");

CREATE TRIGGER "Albaran_BD"
BEFORE DELETE
ON public."Albaran"
FOR EACH ROW
EXECUTE PROCEDURE public."AlbaranBeforeDelete"();

CREATE TRIGGER "Albaran_BU"
BEFORE UPDATE
ON public."Albaran"
FOR EACH ROW
EXECUTE PROCEDURE public."AlbaranBeforeUpdate"();

CREATE TABLE public."AlbaMov" (
"RecNo" serial NOT NULL,
"Serie" public."Serie" NOT NULL,
"NumeroDoc" public."NumeroDocumento" NOT NULL,
"Referencia" public."CodigoArticulo" NOT NULL,
"Descripcion" public."Descripcion",
"Cantidad" public."Cantidad",
"Precio" public."Importes",
"PrecioCosto" public."Importes",
"PorcentajeIVA" public."Porcentaje",
"PorcentajeRE" public."Porcentaje",
"Almacen" public."CodigoAlmacen",
"Lote" public."Lote",
"Unidades" public."Cantidad",
"TotalUnidades" public."Cantidad",
"CodigoPromocion" public."CodigoArticuloOpcional",
"Promocion" public."Cantidad",
"DescuentoBase" public."Porcentaje",
"DescuentoResto" public."Porcentaje",
"PesoNeto" public."Peso",
"PesoBruto" public."Peso",
"ReferenciaCliente" public."CodigoArticuloOpcional",
"Modificado" public."Boolean",
"FechaCaducidad" date,
"TotalLinea" public."Importes",
"SeriePedido" public."Serie",
"NumeroPedido" public."NumeroDocumento",
/* Llaves */
CONSTRAINT "PK_AlbaMov"
PRIMARY KEY ("RecNo")
) WITH (
OIDS = FALSE
);

CREATE INDEX "IDX_AlbaMov_SerieNumeroDoc"
ON public."AlbaMov"
("Serie", "NumeroDoc", "RecNo");

CREATE TRIGGER "AlbaMov_AD"
AFTER DELETE
ON public."AlbaMov"
FOR EACH ROW
EXECUTE PROCEDURE public."AlbaMovAfterDelete"();


-----------------------------------<>---------------------------------

Casimiro Notevi
02-05-2013, 12:13:41
Recuerda poner los tags al código fuente, ejemplo:

http://www.clubdelphi.com/images/UtilizarTAGs.png

Gracias :)

macugi
02-05-2013, 15:20:57
Recuerda poner los tags al código fuente

Gracias por la observación, no volvera a ocurrir :)

Continuando con el tema del borrado... Tengo una funcion la cual pasa un Albaran a una Factura y al final del todo elimina el Albaran, pues esa eliminacion funciona perfectamente.

Despues de ver esto me he creado una funcion BorrarAlbaran en la cual solo tengo la SQL necesaria para borrarlo y NO FUNCIONA, pasa igual que si ejecuto la SQL desde cualquier cliente.:confused:

¿Por que funcionará desde dentro de esa funcion y no desde otra ?

Saludos,

Manuel Cuevas

Casimiro Notevi
02-05-2013, 16:38:37
Despues de ver esto me he creado una funcion BorrarAlbaran en la cual solo tengo la SQL necesaria para borrarlo y NO FUNCIONA, pasa igual que si ejecuto la SQL desde cualquier cliente.
Te aconsejo que leas nuestra guía de estilo (http://www.clubdelphi.com/foros/guiaestilo.php), gracias.
¿Cómo quieres que te ayuden con esa explicación? :confused:

macugi
02-05-2013, 19:00:30
¿Cómo quieres que te ayuden con esa explicación? :confused:

Pues me gustaria saber como solucionar el problema que explico al inicio de este hilo.

Al González
02-05-2013, 19:23:39
Te recomiendo que desactives todos los disparadores de la tabla detalle, entonces hagas más pruebas de eliminación de registros de la tabla maestra, y vayas activando uno a uno los disparadores, hasta que encuentres cuál disparador detona el problema y nos muestres su código aquí.

Resulta extraño que no surja ningún mensaje error (o al menos no nos has dicho si surje). Pareciera que uno de esos Updates actúa como Insert cuando el registro maestro ya no existe.

Saludos.

macugi
02-05-2013, 19:47:57
Te recomiendo que desactives todos los disparadores de la tabla detalle, entonces hagas más pruebas de eliminación de registros de la tabla maestra, y vayas activando uno a uno los disparadores, hasta que encuentres cuál disparador detona el problema y nos muestres su código aquí.

CREATE OR REPLACE FUNCTION public."CalculoAlbaranVenta"
(
IN "cSerie" public."Serie",
IN "nNumeroDoc" public."NumeroDocumento"
)
RETURNS SETOF public."Totales" AS
$$

[...]

UPDATE "Albaran"
SET "BaseImponible" = nTotalBase,
"TotalDescuentos" = nTotalDtos,
"IVA" = nTotalIVA,
"RE" = nTotalRE,
"Total" = nTotalBase + nTotalIVA + nTotalRE
WHERE ("Serie" = "cSerie") AND ("NumeroDoc" = "nNumeroDoc");

[...]

end
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1;


El problema está en este UPDATE, pero funciona bien ya que la tabla Maestra me la deja con los campos a 0. Este mismo codigo funciona perfectamente si voy eliminando las lineas de detalle una a una y no desde dentro del trigger BEFORE DELETE de la tabla maestra.

Parece como si intentara un ultimo UPDATE cuando ya no existe el registro Maestro.

Resulta extraño que no surja ningún mensaje error (o al menos no nos has dicho si surje). Pareciera que uno de esos Updates actúa como Insert cuando el registro maestro ya no existe.

No da ningun error.

Tambien como he puesto en otro mensaje, tengo esta funcion desde la que se elimina el registro de la tabla Maestra sin ningun problema.

CREATE OR REPLACE FUNCTION public."Albaran2Factura"
(
IN "cSerieAlbaran" public."SerieDocumento",
IN "nNumeroAlbaran" public."NumeroDocumento"
)
RETURNS SETOF public."SerieNumeroDocumento" AS
$$
declare rDocumento "SerieNumeroDocumento"%ROWTYPE;
declare rMaster RECORD;
declare rDetail RECORD;
declare rConfig RECORD;
declare rIVA RECORD;
declare cRegimenIVA CHAR;
declare nNumeroFactura "NumeroDocumento";
declare nPorcentajeIVAPortes "Importes";
declare nPorcentajeREPortes "Importes";

begin
rDocumento."Serie" := '';
rDocumento."NumeroDoc" := -1;

SELECT * INTO rConfig FROM "Empresa" LIMIT 1;

SELECT "PorcentajeIVA", "PorcentajeRE" INTO rIVA FROM "Iva"
WHERE "Tipo" = rConfig."TipoIVAPortes";

nPorcentajeIVAPortes := rIVA."PorcentajeIVA";
nPorcentajeREPortes := rIVA."PorcentajeRE";

UPDATE "Numera"
SET "NumeroDoc" = "NumeroDoc" + 1
WHERE ("TipoDocumento" = 'FV') AND ("Serie" = "cSerieAlbaran");

SELECT "NumeroDoc" INTO nNumeroFactura FROM "Numera"
WHERE ("TipoDocumento" = 'FV') AND ("Serie" = "cSerieAlbaran");

SELECT * INTO rMaster FROM "Albaran"
WHERE ("Serie" = "cSerieAlbaran") AND ("NumeroDoc" = "nNumeroAlbaran");

SELECT "RegimenIVA" INTO cRegimenIVA FROM "Clientes"
WHERE "Codigo" = rMaster."Cliente";

IF ("cSerieAlbaran" <> 'ZZZ') THEN

IF (cRegimenIVA = 'G') THEN
nPorcentajeREPortes := 0;
ELSIF (cRegimenIVA = 'E') THEN
nPorcentajeIVAPortes := 0;
nPorcentajeREPortes := 0;
END IF; /* IF (cRegimenIVA = 'G') */

ELSE
nPorcentajeIVAPortes := 0;
nPorcentajeREPortes := 0;
END IF; /* IF ("cSerieAlbaran" <> 'ZZZ') */

INSERT INTO "Factura" ("NumeroDoc",
"Serie",
"Fecha",
"Cliente",
"Nombre",
"BaseImponible",
"IVA",
"RE",
"Notas",
"Direccion",
"Poblacion",
"CodigoPostal",
"Provincia",
"CIF",
"Total",
"Agente",
"CodigoDir",
"Pais",
"SuNumeroPedido",
"Telefono",
"Fax",
"FormaPago",
"Transportista",
"Repartidor",
"Portes",
"DebidosPagados",
"Gastos",
"TotalDescuentos",
"TotalPesoNeto",
"TotalPesoBruto",
"PorcentajeIVAPortes",
"PorcentajeREPortes",
"Albaranes",
"Exportada",
"Rapel",
"Cobrada",
"Modificado")
VALUES (nNumeroFactura,
"cSerieAlbaran",
current_date,
rMaster."Cliente",
rMaster."Nombre",
rMaster."BaseImponible",
rMaster."IVA",
rMaster."RE",
rMaster."Notas",
rMaster."Direccion",
rMaster."Poblacion",
rMaster."CodigoPostal",
rMaster."Provincia",
rMaster."CIF",
rMaster."Total",
rMaster."Agente",
rMaster."CodigoDir",
rMaster."Pais",
rMaster."SuNumeroPedido",
rMaster."Telefono",
rMaster."Fax",
rMaster."FormaPago",
rMaster."Transportista",
rMaster."Repartidor",
rMaster."Portes",
rMaster."DebidosPagados",
rMaster."Gastos",
rMaster."TotalDescuentos",
rMaster."TotalPesoNeto",
rMaster."TotalPesoBruto",
nPorcentajeIVAPortes,
nPorcentajeREPortes,
'Albaran ' || "nNumeroAlbaran" || '/' || "cSerieAlbaran",
'0',
'0',
'0',
'1');

FOR rDetail IN SELECT * FROM "AlbaMov"
WHERE ("Serie" = "cSerieAlbaran") AND ("NumeroDoc" = "nNumeroAlbaran")
ORDER BY "RecNo"
LOOP

INSERT INTO "FacMov" ("Serie",
"NumeroDoc",
"Referencia",
"Descripcion",
"Cantidad",
"Precio",
"PorcentajeIVA",
"PorcentajeRE",
"NumeroAlbaran",
"SerieAlbaran",
"FechaAlbaran",
"NumeroPedido",
"SeriePedido",
"PrecioCosto",
"Almacen",
"Lote",
"Unidades",
"TotalUnidades",
"CodigoPromocion",
"Promocion",
"DescuentoBase",
"DescuentoResto",
"PesoNeto",
"PesoBruto",
"ReferenciaCliente",
"Modificado",
"FechaCaducidad",
"NoDescontar",
"Agente",
"Repartidor")
VALUES ("cSerieAlbaran",
nNumeroFactura,
rDetail."Referencia",
rDetail."Descripcion",
rDetail."Cantidad",
rDetail."Precio",
rDetail."PorcentajeIVA",
rDetail."PorcentajeRE",
rMaster."NumeroDoc",
rMaster."Serie",
rMaster."Fecha",
rDetail."NumeroPedido",
rDetail."SeriePedido",
rDetail."PrecioCosto",
rDetail."Almacen",
rDetail."Lote",
rDetail."Unidades",
rDetail."TotalUnidades",
rDetail."CodigoPromocion",
rDetail."Promocion",
rDetail."DescuentoBase",
rDetail."DescuentoResto",
rDetail."PesoNeto",
rDetail."PesoBruto",
rDetail."ReferenciaCliente",
'1',
rDetail."FechaCaducidad",
'0',
rMaster."Agente",
rMaster."Repartidor");

END LOOP;


/********************** Eliminadolo desde aqui funciona sin nigun problema ****************/

DELETE FROM "Albaran"
WHERE ("Serie" = "cSerieAlbaran") AND ("NumeroDoc" = "nNumeroAlbaran");

/**************************************/

rDocumento."Serie" := "cSerieAlbaran";
rDocumento."NumeroDoc" := nNumeroFactura;

RETURN NEXT rDocumento;


end
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;


Saludos,