まず、私の下手な英語で申し訳ありません。
PostgreSQL 9.2 を使用する新しいアプリケーションを作成しています。Firebird で使用されているのと同じ「ロジック」を使用しようとしていますが、明らかに PostgreSQL では機能しません。
「Albaran」と呼ばれるマスターテーブルと「AlbaMov」と呼ばれる他の詳細テーブルがあります。詳細テーブルのレコードを変更すると、マスター テーブルが更新する、対応する義務を持ついくつかのトリガーを定義しました。マスター テーブルのレコードを削除する場合を除いて、すべてが完全に機能します。
マスター テーブルのレコードを削除すると、詳細からすべてのレコードが削除され、マスター テーブルの「合計」フィールドが 0 に更新されますが、マスター テーブルのレコードは削除されません。マスターテーブルからレコードを削除すると、詳細テーブルのレコードがスムーズに削除されます。
私はテストを行っており、Master テーブルへの UPDATE が CalculoAlbaranVenta と呼ばれる関数で行われていることに問題があることがわかりました。
この同じシステムは、Firebird でも完全に機能します。
この関数は、PHP 画面を更新するために使用する type% ROWTYPE の変数を返します。
ここでは、トリガーと関数を含むテーブルの定義を残します。
どこに問題がありますか?
よろしくお願いします。
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"();
テストを行ったところ、この関数からマスター テーブル レコードを削除すると、完全に機能するのか、なぜ壊れていないのか、理解できないことがわかりました。
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;
/********************** Deleting master record work ****************/
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;
これは機能しません:
CREATE OR REPLACE FUNCTION public."BorrarAlbaran"
(
IN "cSerie" public."SerieDocumento",
IN "nNumeroDoc" public."NumeroDocumento"
)
RETURNS void AS
$$
begin
DELETE FROM "Albaran"
WHERE ("Serie" = "cSerie") and ("NumeroDoc" = "nNumeroDoc");
end
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;
回避策:
CREATE OR REPLACE FUNCTION public."BorrarAlbaranVenta"
(
IN "cSerie" public."SerieDocumento",
IN "nNumeroDoc" public."NumeroDocumento"
)
RETURNS void AS
$$
begin
DELETE FROM "AlbaMov"
WHERE ("Serie" = "cSerie") and ("NumeroDoc" = "nNumeroDoc");
DELETE FROM "Albaran"
WHERE ("Serie" = "cSerie") and ("NumeroDoc" = "nNumeroDoc");
end
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;