Полное закрытие периода (постановка)
Имеем базу данных, с которой требуется удалить старые документы и движение с формированием складских и бухгалтерских остатков по удаленным документам. Данные исходной базы должны остаться без изменений.
Решение
Необходимо получить «чистую» базу, то есть базу без складских документов и проводок созданных ранее даты закрытия периода. Есть три пути:
- копирование исходной базы А в файл базы Б, и последующее удаление данных старше указанной даты закрытия периода с базы Б, затем перенос складских и бухгалтерских остатков на дату закрытия с базы А на Б
- формирование базы Б – копии базы данных А, но без данных, и последующий перенос на базу Б складских и бухгалтерских остатков на дату закрытия, а также документов и проводок созданных после даты закрытия периода с базы А (+ все остальные необходимые справочники и нескладские документы)
- копирование исходной базы А в файл базы Б (в смысле делаем backup-restore в базу Б), последующее формирование бухгалтерских и складских остатков на дату закрытия периода прямо на базе Б, удаление складских документов и проводок с базы Б созданных до даты закрытия периода.
Предполагается давать возможность выбора:
- типов закрываемых документов, то есть документов, по которым будет формироваться складской и бухгалтерский остаток, и из которых затем будут удаляться устаревшие записи.
- бухгалтерских аналитик и складских признаков карточки по которым будет происходить группировка остатков. Например, при формировании складского остатка нам не нужны признаки карточки привязывающие движение к конкретной позиции документа (USR$INV_ADDLINEKEY – ссылка на документ прихода). При формировании бухгалтерского остатка будем использовать все признаки бухгалтерской проводки кроме привязки к конкретному документу.
Под датой {CLOSEDATE} закрытия периода понимается дата, начиная с которой на новой базе будут идти уже не сгруппированные документы и проводки. Расчет бухгалтерского и складского остатков будет производиться на дату {CLOSEDATE - 1}
Предполагается возможность неоднократного закрытия периода, то есть возможность повторения процесса закрытия периода на основе текущей базы, полученной ранее в процессе полного закрытия периода. Данная информация будет необходима при построении отчетов за период, охватывающий несколько баз данных. Историю закрытий предполагается накапливать в таблице:
CREATE TABLE DB_CLOSEHISTORY ??? ( id dintkey, databasepath varchar(2048), closedate date )
Разовьем 3-ий способ закрытия периода.
- Заходим в базу Б и запускаем процедуру закрытия периода
- Формируется бухгалтерский остаток на дату {CLOSEDATE - 1}
- Удаляем проводки, по которым сформировался остаток
- Сохраняем бухгалтерский остаток как проводки на дату {CLOSEDATE - 1}
- Формируем складской остаток на дату {CLOSEDATE - 1}
- Перепривязываем складские карточки
- Удаляем документы, по которым сформировался складской остаток
- Сохраняем складской остаток в документе прихода INV_DOCUMENT с датой равной {CLOSEDATE - 1}
- Перестроить оперативную информацию по проводкам в таблице AC_ENTRY_BALANCE (при удалении и вставке проводок появится много лишних записей)
- Установим генератор {generator name} в значение {CLOSEDATE} (предполагается в дальнейшем использовать эту дату при построении комплексных отчетов по данным находящимся в новой и старой базе)
Складские остатки формируются путем группировки всех складских движений предшествующих дате закрытия периода. Для хранения сформированных складских остатков предполагается создать складской документ INV_DOCUMENT, который обладает всеми признаками новой карточки, и приходует товар на подразделение нашей организации с Псевдоклиента. По документу INV_DOCUMENT не должно быть настроено никаких автоматических операций.
Бухгалтерские остатки формируются путем группировки всех проводок за период предшествующий дате закрытия периода. Сформированные бухгалтерские остатки предполагается вставлять в таблицу AC_ENTRY (+ AC_RECORD) как проводки на дату закрытия периода. Удаление устаревших проводок проходит просто, необходимо только правильно ограничить запрос на удаление (дата, типы документов). При удалении устаревших документов удаляются и складские карточки созданные позициями этих документов, но более новые карточки из движения могут ссылаться на эти, удаляемые, карточки (по полю PARENT). Значит необходима перепривязка новых (с датой создания >= {CLOSEDATE}) складских карточек к карточкам созданным документом INV_DOCUMENT, то есть к карточкам по сформированному складскому остатку.
SELECT m_less.movementdate, c_less.ID, c_less.PARENT, c_less.DOCUMENTKEY, c_less.FIRSTDOCUMENTKEY, c_less.FIRSTDATE, m_more.movementdate, c_more.ID, c_more.PARENT, c_more.DOCUMENTKEY, c_more.FIRSTDOCUMENTKEY, c_more.FIRSTDATE FROM inv_card c_less JOIN inv_card c_more ON c_more.parent = c_less.id JOIN inv_movement m_less ON m_less.cardkey = c_less.id JOIN inv_movement m_more ON m_more.cardkey = c_more.id WHERE m_less.movementdate < :closedate AND m_more.movementdate >= :closedate
Во всех карточках c_more полученных из этого запроса надо переставить поле PARENT на карточки по документу INV_DOCUMENT. Соответствие карточек определяется на основе выбранных пользователем актуальных признаков складских карточек. Те же самые изменения произвести с записями m_more, но изменять надо поле CARDKEY.
Перепривязка карточек выполняется после формирования складского остатка, и до удаления устаревших документов. Список документов подлежащих удалению получим по ссылкам DOCUMENTKEY таблицы INV_MOVEMENT, на основе которой формируется складской остаток. Выполняем запрос с такими же ограничениями что и при формировании складского остатка (дата, типы документов), но без группировки.
EXECUTE BLOCK
(
closedate DATE = :closedate
)
AS
DECLARE VARIABLE mid INTEGER;
DECLARE VARIABLE cardkey INTEGER;
DECLARE VARIABLE dockey INTEGER;
DECLARE VARIABLE headdockey INTEGER;
DECLARE VARIABLE rel_name_head VARCHAR(31);
DECLARE VARIABLE rel_name_line VARCHAR(31);
DECLARE VARIABLE Sql VARCHAR(1024);
BEGIN
FOR
SELECT
m.id, m.cardkey, m.documentkey
FROM
inv_movement m
/*WHERE
m.movementdate < :closedate */
ORDER BY
m.movementdate DESC, m.id DESC
INTO
:mid, :cardkey, :dockey
DO
BEGIN
SELECT
line.relationname, head.relationname
FROM
gd_document doc
JOIN gd_documenttype t ON t.id = doc.documenttypekey
JOIN at_relations line ON line.id = t.linerelkey
JOIN at_relations head ON head.id = t.headerrelkey
WHERE
doc.id = :dockey
INTO
:rel_name_line, rel_name_head;
SELECT
parent
FROM
gd_document
WHERE
id = :dockey
INTO
:headdockey;
-- удалим позицию документа
Sql = 'DELETE FROM ' || :rel_name_line || ' WHERE documentkey = ' || CAST(:dockey AS VARCHAR(20));
EXECUTE STATEMENT Sql;
DELETE FROM gd_document WHERE id = :dockey;
-- если шапка стала пустой удалим и ее
Sql = 'SELECT h.documentkey FROM ' || :rel_name_head || ' h WHERE h.documentkey = ';
Sql = Sql || CAST(:headdockey AS VARCHAR(20)) || ' AND NOT EXISTS (SELECT l.documentkey FROM ';
Sql = Sql || :rel_name_line ||' l WHERE l.masterkey = h.documentkey )';
FOR
EXECUTE STATEMENT Sql
INTO :dockey
DO
BEGIN
Sql = 'DELETE FROM ' || :rel_name_head || ' WHERE documentkey = ' || CAST(:dockey AS VARCHAR(20));
EXECUTE STATEMENT Sql;
DELETE FROM ac_entry WHERE (documentkey = :dockey) OR (masterdockey = :dockey) OR (usr$gs_document = :dockey);
DELETE FROM gd_document WHERE id = :dockey;
END
END
END
Глобальные отчеты и получение «закрытых» данных
После полного закрытия периода может возникнуть необходимость в построении отчета, или получении данных одновременно с текущей рабочей базы, и уже закрытой архивной базы (баз). Предполагается создать новый бизнес-объект {TgdcDatabaseCloseHistory} представляющий информацию из таблицы {DB_CLOSEHISTORY}. Через функцию объекта:
function GetDatabaseList(ActualDateBegin, ActualDateEnd: TDateTime): TStringList
, указав значения дат по которым необходимо построить отчет или получить данные, можно будет получить список баз данных (с путями) которые необходимо задействовать в данном отчете. Список баз будет отсортирован по дате закрытия, начиная с самой старой базы. Получив список закрытых баз программист может получить данные с внешней базы либо при помощи создания транзакции к внешней базе, либо при помощи конструкции EXECUTE STATEMENT, которая, начиная с FB 2.5 может обращаться к внешней базе.
[FOR] EXECUTE STATEMENT <query_text> [(<input_parameters>)]
[ON EXTERNAL [DATA SOURCE] <connection_string>]
[WITH {AUTONOMOUS | COMMON} TRANSACTION]
[AS USER <user_name>]
[PASSWORD <password>]
[WITH CALLER PRIVILEGES]
[INTO <variables>]