Глава 10. БУХГАЛТЕРСКИЕ ПРОВОДКИ
Автоматическое проведение документов, системная таблица ACC_TURN
Так как хранимые процедуры шаблонов в любой момент предоставляют нам
необходимый «кусок» журнала проводок, остается лишь сохранить
этот набор в таблице проводок. Все бухгалтерские проводки хранятся
в таблице ACC_TURN. Если мы хотим «
перепровести» уже существующий документ (например, после внесения
в него изменений), то нужно сначала удалить старые проводки
по данному документу, а затем вставить новые проводки,
поставляемые хранимой процедурой шаблона. Программа делает все это автоматически
путем вызова хранимой процедуры <имя_главной_таблицы
_документа>_CHANGED. Эта хранимая процедура создается программой
автоматически, и ее текст зависит от того, какие
шаблоны имеет конкретный тип документа.
Пример хранимой процедуры, осуществляющей перепроведение документов типа «Ручная
операция»:
CREATE PROCEDURE GAAP_CHANGED(ID INTEGER)
AS
DECLARE VARIABLE OPER_DATE DATE;
DECLARE VARIABLE PERIOD_START DATE;
DECLARE VARIABLE LAYER_ID INTEGER;
DECLARE VARIABLE DEBIT DECIMAL(18,2);
DECLARE VARIABLE CREDIT DECIMAL(18,2);
DECLARE VARIABLE MIN_DATE DATE;
BEGIN
/*Текст данной процедуры создан системой Allegro автоматически.*/
/*Не меняйте этот текст. Ваши изменения могут быть утеряны*/
/*Выбираем дату начала периода*/
SELECT START_DATE FROM PERIOD_PROPERTIES INTO :PERIOD_START;
/*Удаление старых проводок*/
DELETE FROM ACC_TURN WHERE DOC_ID = :ID;
/*Проверка на равенство дебета кредиту в каждом слое на каждую дату*/
FOR SELECT OPER_DATE, LAYER_ID, SUM(DEBIT) DEBIT, SUM(CREDIT) CREDIT
FROM GAAP_TEMPLATE(NULL,NULL,:ID) T, ACC
WHERE OPER_DATE>=:PERIOD_START AND T.ACC_ID = ACC.ACC_ID AND
ACC.NO_BALANCE = 0
GROUP BY OPER_DATE, LAYER_ID INTO :OPER_DATE, :LAYER_ID, :DEBIT, :CREDIT
DO
BEGIN
IF (DEBIT <> CREDIT) THEN EXCEPTION NO_BALANCE;
/*Запоминание саммой ранней даты в составе операции*/
IF ((MIN_DATE IS NULL) OR (MIN_DATE > OPER_DATE)) THEN
MIN_DATE = OPER_DATE;
END
/*Добавление новых проводок*/
INSERT INTO ACC_TURN (OP_DATE, ACC_ID, LAYER_ID, OBJECT_ID, TEMPLATE_ID,
DOC_ID, IS_CREDIT, DEBIT, CREDIT,
QUANTITY_DEBIT, QUANTITY_CREDIT)
SELECT OPER_DATE, ACC_ID, LAYER_ID, OBJECT_ID, TEMPLATE_ID,
DOC_ID, IS_CREDIT, SUM(DEBIT), SUM(CREDIT),
SUM(QDEBIT), SUM(QCREDIT)
FROM GAAP_TEMPLATE(NULL,NULL,:ID)
WHERE OPER_DATE >= :PERIOD_START
GROUP BY OPER_DATE, ACC_ID, LAYER_ID, OBJECT_ID, TEMPLATE_ID,
DOC_ID, IS_CREDIT;
/*Сдвиг даты расчетов*/
IF (NOT (MIN_DATE IS NULL)) THEN
EXECUTE PROCEDURE SET_CALCULATIONS(:MIN_DATE);
END
Обратим внимание, что при проведении документа однотипные записи по
счетам «сжимаются» с помощью агрегатных функций SUM().
Это сделано для уменьшения количества строк в таблице проводок ACC
_TURN и для придания операциям более компактного и читабельного
вида. Например, два десятка парных записей при проведении
накладной «Продажи» сожмется до 13 результирующих записей в
таблице проводок. Одна запись будет содержать общую сумму,
начисленную покупателю, одна запись - сумму начисленных доходов,
одна – сумму начисленных товарных расходов, а 10 остальных
– суммы, списанные со счета «Товары», раздельно
для каждого товара.
Итак, для перепроведения конкретного документа какого-то типа
, нам достаточно вызвать хранимую процедуру <имя_главной
_таблицы_документа>_CHANGED, передав в нее
входным параметром ID документа. Например, так можно перепровести
«Продажи товара», документ с ID = 11031:
execute procedure sale_changed(11031);
Разработчик для вызова этой команды может воспользоваться также процедурой встроенного
языка:
procedure MakeEntries(const MainTableName: string; doc_id: integer;
ATransaction: TIBTransaction);
Эта процедура просто вызывает ту же SQL команду в контексте
определенной транзакции. В качестве параметров нужно передать название главной
таблицы документа, внутренний номер ID документа, который следует
перепровести, и указатель на компонент транзакции.
Так как все действия по перепроведению документов производятся на сервере
, это происходит без ощутимых временных затрат (практически мгновенно
). Все проводки хранятся в таблице ACC_TURN -
сердце бухгалтерской системы. Таблица организована таким образом, чтобы
максимально быстро извлекать из нее все интересующие нас сведения,
например, любой Т-счет. Для этого она
снабжена специальными индексами:
PRIMARY KEY (OP_DATE,ACC_ID,LAYER_ID,OBJECT_ID,TEMPLATE_ID,DOC_ID,IS_CREDIT)
CREATE INDEX X_ACC_TURN_DOC_ID ON ACC_TURN(DOC_ID);
CREATE INDEX X_ACC_T_ASC ON ACC_TURN(ACC_ID,OP_DATE,DOC_ID,TEMPLATE_ID,LAYER_ID);
CREATE DESCENDING INDEX X_ACC_T_DESC ON ACC_TURN(ACC_ID,OP_DATE,DOC_ID,
TEMPLATE_ID,LAYER_ID);
Системная таблица ACC_TURN
Название поля |
Тип данных |
Назначение |
OP_DATE |
DATE |
Дата проводки |
ACC_ID |
INTEGER |
Внутренний ACC_ID счета |
LAYER_ID |
INTEGER |
Внутренний LAYER_ID слоя |
OBJECT_ID |
INTEGER |
Внутренний ID объекта аналитики |
TEMPLATE_ID |
INTEGER |
Внутренний TEMPLATE_ID шаблона операции |
DOC_ID |
INTEGER |
Внутренний ID конкретного проведенного документа |
IS_CREDIT |
SMALLINT |
Тип записи
0 – запись в дебет
1
– запись в кредит |
DEBIT |
DECIMAL(18,2) |
Сумма дебет |
CREDIT |
DECIMAL(18,2) |
Сумма кредит |
QUANTITY_DEBIT |
DECIMAL(18,3) |
Количество дебет |
QUANTITY_CREDIT |
DECIMAL(18,3) |
Количество кредит |
Обратим внимание на то, что бухгалтерские записи хранятся «
вертикально» в англо-американском стиле «смешанных»
проводок: по одной записи на каждый счет. Такое
хранение проводок значительно ускоряет получение Т-счетов, всевозможный
анализ и интерпретацию данных. Например, мы с помощью
одного SQL-запроса быстро (за доли секунды при
десятках тысяч проводок) можем получить текущую себестоимость всех имеющихся
товаров на складах по каждому товару:
select g.id, o.short_name,
(sum(a.debit)-sum(a.credit))/(sum(a.quantity_debit)-sum(a.quantity_credit))
from acc_turn a, goods g, object_names o
where a.object_id = o.object_id and g.id = o.object_id
group by g.id, o.short_name,
having sum(a.quantity_debit)-sum(a.quantity_credit) <> 0
|