DEPO Firebird: проверка на реальной базе Fancy — генераторы, диалект 1, дата без времени

Обезличенная база Fancy (Firebird 2.5.9, диалект 1, кодировка NONE) с UDF ib_Fansy и штатными
триггерами: установка, HANDBOOK, поручения IN/OUT, события до BJ_EXECUTED, операции и проводки, ошибки.
- генераторы OD_DOCS_ID_GEN / OD_DOLS_ID_GEN (как в Fancy);
- COMMENT без кавычек: в диалекте 1 "..." — строка;
- FTV_M2M_DAY: дата без времени (в диалекте 1 CAST AS DATE время не отрезает);
- README: установка в WIN1251, что подтвердилось, что проверить у клиента (UDF, кодировщик M2M, оператор).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
fontvielle
2026-10-27 17:31:29 +03:00
parent 56efa9266c
commit ee857b089a
6 changed files with 110 additions and 69 deletions
@@ -1,6 +1,6 @@
/* Bridge&Join — процедуры обработки M2M для Fancy DEPO на Firebird 2.5.
Перенос процедур MSSQL-версии (Фонтвьель, тексты от 22.10.2026, OPERATION — от 24.10.2026) на PSQL. Логика та же; отличия:
- raiserror → FTV_M2M_FAIL: текст причины кладётся в контекст транзакции и поднимается исключение FTV_M2M_ERROR;
- raiserror — FTV_M2M_FAIL: текст причины кладётся в контекст транзакции и поднимается исключение FTV_M2M_ERROR;
FTV_M2M_JOB пишет этот текст в COMMENT (в Firebird 2.5 текст исключения в WHEN не доступен; контекст
возвращает строку без кодировки — приводится к WIN1251, кодировке базы Fancy);
- ошибка записи откатывает только её изменения (WHEN ANY = точка сохранения), в MSSQL-версии изменения
@@ -11,7 +11,10 @@
- SELECT … INTO, который в MSSQL молча брал последнюю из нескольких строк, — SELECT FIRST 1;
- в цикле по строкам поручения бумага/счета сбрасываются перед поиском (в MSSQL ненайденное значение
оставалось от предыдущей строки).
Порядок создания: вспомогательные → WIRING → OPERATION → UPDATE_HANDBOOK → EVENTS → CREATE → JOB. */
Тексты работают в обоих диалектах: база Fancy — диалект 1 (DATE хранит и время, "…" — строка, а не имя),
поэтому COMMENT без кавычек, а дата без времени — через FTV_M2M_DAY. Проверено 27.10.2026 на обезличенной
базе Fancy (Firebird 2.5, диалект 1, кодировка NONE) с UDF ib_Fansy и штатными триггерами.
Порядок создания: вспомогательные, WIRING, OPERATION, UPDATE_HANDBOOK, EVENTS, CREATE, JOB. */
SET TERM ^ ;
@@ -42,6 +45,14 @@ BEGIN
LPAD(CAST(FLOOR(EXTRACT(SECOND FROM T)) AS INTEGER), 2, '0');
END^
/* Дата без времени (CONVERT(DATE, …) MSSQL-версии). В диалекте 1 CAST(… AS DATE) время не отрезает. */
CREATE OR ALTER PROCEDURE FTV_M2M_DAY (T TIMESTAMP) RETURNS (D TIMESTAMP) AS
BEGIN
D = NULL;
IF (T IS NOT NULL) THEN
D = CAST(EXTRACT(YEAR FROM T) || '-' || EXTRACT(MONTH FROM T) || '-' || EXTRACT(DAY FROM T) AS TIMESTAMP);
END^
/* Директива «Обработать» для документа «Зачисление» или «Списание». Значения берутся только из самого
документа; M2M-поручение и трансферные таблицы не используются. */
@@ -151,7 +162,7 @@ DECLARE VARIABLE CLISER_ID INTEGER;
DECLARE VARIABLE CLISER_NUM VARCHAR(64);
DECLARE VARIABLE CLISER_DATE TIMESTAMP;
DECLARE VARIABLE DIRECTION VARCHAR(64);
DECLARE VARIABLE DD_DATE DATE;
DECLARE VARIABLE DD_DATE TIMESTAMP;
DECLARE VARIABLE DD_NUM VARCHAR(64);
DECLARE VARIABLE SHARE INTEGER;
DECLARE VARIABLE AMOUNT NUMERIC(18,7);
@@ -167,7 +178,7 @@ DECLARE VARIABLE FILER_DATE TIMESTAMP;
DECLARE VARIABLE STEP_ID INTEGER;
DECLARE VARIABLE P VARCHAR(4000);
DECLARE VARIABLE REP_NUM VARCHAR(255);
DECLARE VARIABLE REP_DATE DATE;
DECLARE VARIABLE REP_DATE TIMESTAMP;
DECLARE VARIABLE CMT VARCHAR(4000);
BEGIN
EXECUTE PROCEDURE FTV_M2M_GET('WHOS') RETURNING_VALUES :WHOS;
@@ -181,7 +192,7 @@ BEGIN
SELECT FIRST 1 H.DOC, D.NUM, D.D_DATE,
CASE WHEN DC.SYS_NAME = 'I_CLISER_M2M_ZACH' THEN 'IN' WHEN DC.SYS_NAME = 'I_CLISER_M2M_SPIS' THEN 'OUT' END,
CAST(D.D_DATE AS DATE), D.NUM, F.SHARE, F.AMOUNT, F.CLIENT, H.DEPONENT, F.PLACE
D.D_DATE, D.NUM, F.SHARE, F.AMOUNT, F.CLIENT, H.DEPONENT, F.PLACE
FROM D_CLISER_M2M_DOL F
LEFT JOIN OD_DOLS L ON L.ID = F.DOL
LEFT JOIN OD_DOCS D ON D.ID = L.DOC
@@ -192,6 +203,7 @@ BEGIN
IF (CLISER_ID IS NULL) THEN
EXECUTE PROCEDURE FTV_M2M_FAIL('Не найдено M2M-поручение по GUID + REFERENCE');
EXECUTE PROCEDURE FTV_M2M_DAY(:DD_DATE) RETURNING_VALUES :DD_DATE;
/* Направление */
SELECT FIRST 1 DC.ID FROM OD_DOC_CATS DC
@@ -199,7 +211,7 @@ BEGIN
INTO :D_CAT;
/* ID и номер операции */
OPER_ID = GEN_ID(OD_DOC_ID_GEN, 1);
OPER_ID = GEN_ID(OD_DOCS_ID_GEN, 1);
EXECUTE PROCEDURE FU_NEXT_DOC_NUM (:D_CAT, :OPER_DATE, NULL, :OPER_ID) RETURNING_VALUES :NUM, :NEW_DATE;
NUM = NUM || '_' || REFERENCE;
@@ -212,14 +224,14 @@ BEGIN
(MSSQL-версия, правка программиста 24.10.2026). */
P = TRIM(PARAMETERS);
REP_NUM = LEFT(P, POSITION(' ' IN P || ' ') - 1);
REP_DATE = CAST(RIGHT(P, POSITION(' ' IN REVERSE(P)) - 1) AS DATE);
REP_DATE = CAST(RIGHT(P, POSITION(' ' IN REVERSE(P)) - 1) AS TIMESTAMP);
IF (DIRECTION = 'IN') THEN
BEGIN
CMT = 'Перевод M2M (без поручения), GUID ' || GUID || ', референс ' || REFERENCE;
INSERT INTO U_OP_INV_ZACH
(DOC, DD_TYPE, DD_DATE, DD_NUM, SHARE, AMOUNT, BLANKS, PLACE, D_NUM, DEPO, K_NUM, MARKET,
CONDITION, IS_REG, TAX_R, B_TYPE, B_DATE, B_NUM, WIR_DATE, NOTIFY_DATE, REG_DATE, IS_BLOCK, FROM_FACE, REJECT_REASON, "COMMENT")
CONDITION, IS_REG, TAX_R, B_TYPE, B_DATE, B_NUM, WIR_DATE, NOTIFY_DATE, REG_DATE, IS_BLOCK, FROM_FACE, REJECT_REASON, COMMENT)
VALUES
(:OPER_ID, 1 /* клиентское */, :DD_DATE, :DD_NUM, :SHARE, :AMOUNT, NULL, :PLACE, :ACC_D, :CLIENT, :ACC_K, :MARKET,
NULL, NULL, NULL, :B_TYPE, :REP_DATE, :REP_NUM, :OPER_DATE, :OPER_DATE, :OPER_DATE, 0, :FACE, NULL, :CMT);
@@ -229,20 +241,20 @@ BEGIN
CMT = 'Перевод M2M, GUID ' || GUID || ', референс ' || REFERENCE;
INSERT INTO U_OP_INV_SPIS
(DOC, DD_TYPE, DD_DATE, DD_NUM, SHARE, AMOUNT, BLANKS, PLACE, D_NUM, DEPO, K_NUM, MARKET,
OLD_CONDITIONS, IS_REG, TAX_R, B_TYPE, B_DATE, B_NUM, WIR_DATE, NOTIFY_DATE, REG_DATE, IS_BLOCK, FROM_FACE, REJECT_REASON, "COMMENT")
OLD_CONDITIONS, IS_REG, TAX_R, B_TYPE, B_DATE, B_NUM, WIR_DATE, NOTIFY_DATE, REG_DATE, IS_BLOCK, FROM_FACE, REJECT_REASON, COMMENT)
VALUES
(:OPER_ID, 1 /* клиентское */, :DD_DATE, :DD_NUM, :SHARE, :AMOUNT, NULL, :PLACE, :ACC_D, :CLIENT, :ACC_K, :MARKET,
NULL, NULL, NULL, :B_TYPE, :REP_DATE, :REP_NUM, :OPER_DATE, :OPER_DATE, :OPER_DATE, 0, :FACE, NULL, :CMT);
END
/* Входящая запись (отчёт НРД) */
FILER_ID = GEN_ID(OD_DOC_ID_GEN, 1);
FILER_ID = GEN_ID(OD_DOCS_ID_GEN, 1);
FILER_DATE = CURRENT_TIMESTAMP;
EXECUTE PROCEDURE FU_NEXT_DOC_NUM ('D_FILER', :FILER_DATE, 1, :FILER_ID) RETURNING_VALUES :FILER_NUM, :NEW_DATE;
INSERT INTO OD_DOCS (ID, NUM, D_CAT, D_DATE, STATE, DIRECTION, WHOS)
VALUES (:FILER_ID, :FILER_NUM, :FILER_CAT, :FILER_DATE, 2, 1, :WHOS);
INSERT INTO D_FILER (DOC, PASS_KIND, RUBRICA, "COMMENT", DOC_NUM, DOC_DATE, CORR_FACE, FUND, ACCOUNT, PLACE, ORI_DATE, CHILD_CAT, MESS)
INSERT INTO D_FILER (DOC, PASS_KIND, RUBRICA, COMMENT, DOC_NUM, DOC_DATE, CORR_FACE, FUND, ACCOUNT, PLACE, ORI_DATE, CHILD_CAT, MESS)
VALUES (:FILER_ID, 8, :RUBRICA, 'Отчет (перевод через MOEX-МОСТ)', :REP_NUM, :REP_DATE, :CORR_FACE,
:FACE, :CLIENT, :PLACE, :FILER_DATE, NULL, 'MS010 / ' || :GUID || ' / ' || :REFERENCE);
@@ -362,7 +374,7 @@ DECLARE VARIABLE CLIENT INTEGER;
DECLARE VARIABLE FACE INTEGER;
DECLARE VARIABLE CLISER_ID INTEGER;
DECLARE VARIABLE DOL_ID INTEGER;
DECLARE VARIABLE DD_DATE DATE;
DECLARE VARIABLE DD_DATE TIMESTAMP;
DECLARE VARIABLE DD_NUM VARCHAR(64);
DECLARE VARIABLE OPER_ID INTEGER;
DECLARE VARIABLE ST INTEGER;
@@ -370,7 +382,7 @@ DECLARE VARIABLE EV_S VARCHAR(19);
DECLARE VARIABLE NOW_S VARCHAR(19);
BEGIN
SELECT FIRST 1 ME.GUID, ME.REFERENCE, ME.EVENT_TYPE, ME.EVENT_DATE, ME.M2M_REJECT_CODE, ME.PARAMETERS, M.DIRECTION,
H.DOC, F.DOL, CAST(D.D_DATE AS DATE), D.NUM, F.SHARE, F.AMOUNT, F.PLACE, F.CLIENT, H.DEPONENT
H.DOC, F.DOL, D.D_DATE, D.NUM, F.SHARE, F.AMOUNT, F.PLACE, F.CLIENT, H.DEPONENT
FROM FTV_M2M_EVENTS ME
LEFT JOIN FTV_M2M M ON M.GUID = ME.GUID
LEFT JOIN D_CLISER_M2M_DOL F ON F.DOL_REF = ME.REFERENCE
@@ -384,7 +396,7 @@ BEGIN
IF (EVENT_TYPE = 'HANDBOOK') THEN
BEGIN
EXECUTE PROCEDURE FTV_M2M_UPDATE_HANDBOOK RETURNING_VALUES :CMT;
UPDATE FTV_M2M_EVENTS SET "COMMENT" = :CMT WHERE ID = :ID;
UPDATE FTV_M2M_EVENTS SET COMMENT = :CMT WHERE ID = :ID;
EXIT;
END
@@ -399,7 +411,7 @@ BEGIN
/* 3. Признак документа */
EXECUTE PROCEDURE FTV_M2M_DT(:EVENT_DATE) RETURNING_VALUES :EV_S;
EXECUTE PROCEDURE FTV_M2M_DT(CURRENT_TIMESTAMP) RETURNING_VALUES :NOW_S;
INSERT INTO OD_INVALIDS (DOC, STATUS, "COMMENT", DO_ING)
INSERT INTO OD_INVALIDS (DOC, STATUS, COMMENT, DO_ING)
VALUES (:CLISER_ID, 0,
:EV_S || ' - ' || :EVENT_TYPE || COALESCE(' (референс - ' || :REFERENCE || ')', '') || COALESCE(' (параметры - ' || :PARAMETERS || ')', ''),
'Время начала обработки: ' || :NOW_S);
@@ -474,6 +486,7 @@ DECLARE VARIABLE FILER_ID INTEGER;
DECLARE VARIABLE FILER_NUM VARCHAR(255);
DECLARE VARIABLE FILER_DATE TIMESTAMP;
DECLARE VARIABLE DOL_ID INTEGER;
DECLARE VARIABLE D_DAY TIMESTAMP;
BEGIN
EXECUTE PROCEDURE FTV_M2M_GET('WHOS') RETURNING_VALUES :WHOS;
@@ -508,7 +521,7 @@ BEGIN
INTO :DEPONENT;
/* 1.5. Шапка документа */
CLISER_ID = GEN_ID(OD_DOC_ID_GEN, 1);
CLISER_ID = GEN_ID(OD_DOCS_ID_GEN, 1);
D_DATE = CURRENT_TIMESTAMP;
EXECUTE PROCEDURE FU_NEXT_DOC_NUM (:D_CAT, :D_DATE, NULL, :CLISER_ID) RETURNING_VALUES :NUM, :NEW_DATE;
@@ -549,7 +562,7 @@ BEGIN
/* Счёт клиента */
SELECT FIRST 1 C.ID FROM OD_CLIENTS C WHERE C.NUMBER = :DEPO_NUMBER INTO :DEPO;
DOL_ID = GEN_ID(OD_DOL_ID_GEN, 1);
DOL_ID = GEN_ID(OD_DOLS_ID_GEN, 1);
INSERT INTO OD_DOLS (ID, DOC) VALUES (:DOL_ID, :CLISER_ID);
INSERT INTO D_CLISER_M2M_DOL (DOL, SHARE, AMOUNT, DOL_REF, PLACE, CLIENT) VALUES (:DOL_ID, :SHARE, :AMOUNT, :REFERENCE, :PLACE, :DEPO);
@@ -561,13 +574,14 @@ BEGIN
IF (DIRECTION = 'OUT') THEN
BEGIN
EXECUTE PROCEDURE FTV_M2M_GET('FILER_CAT') RETURNING_VALUES :FILER_CAT;
FILER_ID = GEN_ID(OD_DOC_ID_GEN, 1);
FILER_ID = GEN_ID(OD_DOCS_ID_GEN, 1);
FILER_DATE = CURRENT_TIMESTAMP;
EXECUTE PROCEDURE FU_NEXT_DOC_NUM ('D_FILER', :FILER_DATE, 1, :FILER_ID) RETURNING_VALUES :FILER_NUM, :NEW_DATE;
EXECUTE PROCEDURE FTV_M2M_DAY(:D_DATE) RETURNING_VALUES :D_DAY;
INSERT INTO OD_DOCS (ID, NUM, D_CAT, D_DATE, STATE, DIRECTION, WHOS)
VALUES (:FILER_ID, :FILER_NUM, :FILER_CAT, :FILER_DATE, 2, 1, :WHOS);
INSERT INTO D_FILER (DOC, PASS_KIND, RUBRICA, "COMMENT", DOC_NUM, DOC_DATE, CORR_FACE, FUND, ACCOUNT, PLACE, ORI_DATE, CHILD_CAT, MESS)
VALUES (:FILER_ID, 7, 8, 'M2M поручение на списание через MOEX-МОСТ', :NUM, CAST(:D_DATE AS DATE), :DEPONENT, :DEPONENT,
INSERT INTO D_FILER (DOC, PASS_KIND, RUBRICA, COMMENT, DOC_NUM, DOC_DATE, CORR_FACE, FUND, ACCOUNT, PLACE, ORI_DATE, CHILD_CAT, MESS)
VALUES (:FILER_ID, 7, 8, 'M2M поручение на списание через MOEX-МОСТ', :NUM, :D_DAY, :DEPONENT, :DEPONENT,
:DEPO, :PLACE, :FILER_DATE, :D_CAT, :GUID);
/* Связываем с M2M-поручением */
UPDATE OD_DOCS SET ID = 0, KVIT = :FILER_ID WHERE ID = :CLISER_ID;
@@ -597,7 +611,7 @@ BEGIN
CMT = COALESCE(CAST(RDB$GET_CONTEXT('USER_TRANSACTION', 'FTV_M2M_ERR') AS VARCHAR(255) CHARACTER SET WIN1251), 'ошибка Fancy') || ' // FTV_M2M_PROCESS_CREATE // SQLCODE ' || SQLCODE || ', GDSCODE ' || GDSCODE;
END
END
UPDATE FTV_M2M SET STATE = :ST, TIMEEND = CURRENT_TIMESTAMP, "COMMENT" = :CMT WHERE ID = :ID;
UPDATE FTV_M2M SET STATE = :ST, TIMEEND = CURRENT_TIMESTAMP, COMMENT = :CMT WHERE ID = :ID;
END
/* НОВЫЕ СОБЫТИЯ */
@@ -616,7 +630,7 @@ BEGIN
END
/* без ошибки COMMENT не трогаем: в нём отчёт справочника (HANDBOOK) */
IF (CMT IS NOT NULL) THEN
UPDATE FTV_M2M_EVENTS SET STATE = :ST, TIMEEND = CURRENT_TIMESTAMP, "COMMENT" = :CMT WHERE ID = :ID;
UPDATE FTV_M2M_EVENTS SET STATE = :ST, TIMEEND = CURRENT_TIMESTAMP, COMMENT = :CMT WHERE ID = :ID;
ELSE
UPDATE FTV_M2M_EVENTS SET STATE = :ST, TIMEEND = CURRENT_TIMESTAMP WHERE ID = :ID;
END