Глава 7. ГЕНЕРАЦИЯ ДОКУМЕНТОВ
Генерация 100 документов поступлений и 300 документов продаж
Для отладки конфигурации нам понадобятся данные, которых пока нет
. Вводить вручную сотни документов мы не будем, вместо
этого мы создадим специальный проект для генерации документов. Цены
и количества мы будем создавать с помощью генератора случайных чисел
, а товары и фирмы выбирать случайным образом из справочников
.
Вызовем проводник по документам и создадим в нем две папки
. Одну назовем «Поступления», а другую – «
Продажи». Вызовем окно интерактивного SQL и сделаем запрос:
select * from doc_dir
Запомним или запишем на бумаге ID имеющихся у нас папок
. Допустим, эти ID равны 1001 и 1002.
Теперь займемся созданием генератора документов.
Включим режим «Дизайнер» и создадим новое приложение.
Сохраним модуль формы в файле monte_carlo.pas
, а проект в файле monte_carlo_project
.ipr. Введем заголовок формы:
Caption = Розыгрыш поступлений и продаж
Для того чтобы форма была дочерним окном главной формы,
установим ее свойство:
FormStyle = fsMDIChild
Для того чтобы форма имела фиксированный размер, установим свойство
:
BorderStyle = bsDialog
Добавим на форму с палитры InterBase компоненты, назначая им
имена (свойство Name):
Компонент |
Name |
IBTransaction |
traCurrent |
IBQuery |
qryGoods |
IBQuery |
qryContragent |
IBQuery |
qry |
IBDataSet |
qryStock_In |
IBDataSet |
qryStock_In_Item |
IBDataSet |
qrySale |
IBDataSet |
qrySale_Item |
Установим у компонента traCurrent свойство:
DefaultDatabase = MainConnection.MainDatabase
Дважды щелкнем на компоненте traCurrent и установим изоляцию транзакций ReadCommitted
.
Выделим все остальные компоненты и назначим им свойство:
Transaction = traCurrent
Дважды щелкая на свойстве SQL компонентов в редакторе SQL-
команды запишем:
Компонент |
Текст запроса |
qryGoods |
select ID from GOODS where ID <> 0 |
qryContragent |
select ID from CONTRAGENT where ID <> 0 |
Теперь создадим у формы обработчик OnCreate:
procedure TForm1.FormCreate(Sender: TObject);
begin
traCurrent.StartTransaction;
qryContragent.Open;
qryContragent.FetchAll;
qryGoods.Open;
qryGoods.FetchAll;
end;
Добавим компонента ProgressBar с палитры Win32.
Добавим 2 компонента Button с палитры Standard и присвоим им
имена и заголовки:
Компонент |
Name |
Caption |
Button1 |
btnDelete |
Удалить документы |
Button2 |
btnGenerate |
Создать документы |
Расположим все компоненты так, как показано на рисунке:
Мы должны создать SQL-запросы в компонентах, которые
будут работать с документами. Запишем в свойствах SelectSQL следующие
запросы:
Компонент |
Текст запроса |
qryStock_In |
select * from STOCK_IN |
QrySale |
select * from SALE
order by ENTRY_DATE |
QryStock_In_Item |
select * from STOCK_IN_ITEM
where
ID = -1 |
QrySale_Item |
select * from SALE_ITEM
where ID =
-1 |
В запросе документов продаж мы упорядочили набор по датам отгрузки
. А в запросы позиций мы ввели условие ID =
-1 для того, чтобы получать пустые наборы при
SELECT.
Вызывая через контекстное меню компонент DataSet Editor, создадим для
всех этих компонентов тексты запросов InsertSQL, ModifySQL, DeleteSQL
, RefreshSQL. Для этого в редакторе последовательно нажимаем кнопки
: Get Table Fields, Select Primary Keys, Generate
SQL, OK.
Растянем главное Allegro окно с помощью кнопки Растянуть Главное Окно
, вызовем окно ISQL с помощью меню Инструменты/Интерактивный
SQL и создадим в базе данных хранимую процедуру, которая
будет обновлять поля сумм TOTAL_L, TOTAL_
S, TOTAL_R каждого документа «Поступление на
склад» в таблице STOCK_IN на основе сумм
его позиций AMOUNT_L, AMOUNT_S,
AMOUNT_R в таблице STOCK_IN_ITEM
:
create procedure update_stock_in_totals
as
declare variable id integer;
declare variable t1 decimal(18,2);
declare variable t2 decimal(18,2);
declare variable t3 decimal(18,2);
begin
for select id, sum(amount_l),sum(amount_s),sum(amount_r)
from stock_in_item
group by id
into id, :t1, :t2, :t3
do update stock_in
set total_l = :t1,
total_s = :t2,
total_r = :t3
where id = :id;
update template set is_changed = 1 where template_id = 102;
end
Создадим еще такую же хранимую процедуру для документов типа «
Продажа»:
create procedure update_sale_totals
as
declare variable id integer;
declare variable t1 decimal(18,2);
declare variable t2 decimal(18,2);
declare variable t3 decimal(18,2);
begin
for select id, sum(amount_l),sum(amount_s),sum(amount_r)
from sale_item
group by id
into id, :t1, :t2, :t3
do update sale
set total_l = :t1,
total_s = :t2,
total_r = :t3
where id = :id;
update template set is_changed = 1 where template_id = 103;
end
Мы будем использовать следующий алгоритм генерации документов:
- Добавляем 100 записей в компонент qryStock_In. Каждая
запись соответствует одному документу поступления. В процессе добавления документы
распределяем равномерно по датам на 300 дней, с интервалом
3 дня, начиная с 1 января 2003 г.
Затем добавляем 350 записей в компонент qrySale. Каждая запись
соответствует одному документу продаж. В процессе добавления распределяем их
по одному документу на каждую дату, начиная с 1
января 2003г. В каждом документе выбираем случайного контрагента,
присваивая случайное целое число свойству RecNo (номер записи)
компонента qryContragent и копируя значение поля ID из этого компонента
в документ. В результате мы получим 450 документов (
пока без позиций) в таблицах базы.
- В цикле создаем 1000 позиций «Поступлений на склад»,
используя компонент qryStock_In_Item. Документ для
добавления позиции выбираем каждый раз, присваивая случайное целое число
свойству RecNo компонента qryStock_In. После посылки компонентом
qryStock_In_Item каждой новой записи на сервер
, переоткрываем его запрос SelectSQL. Благодаря тому, что
в этом запросе имеется условие ID = -1,
после переоткрытия запрос возвращает пустой набор. Это будет защищать
нас от замедления процесса генерации позиций, вызванного накоплением позиций
в компоненте qryStock_In_Item. После создания
позиции в документе поступления, распределяем эту партию случайным образом
по нескольким документам продаж, датированным позднее данного поступления.
Это делаем во внутреннем цикле путем вставки записей в компонент
qrySale_Item. После посылки компонентом qrySale_Item
каждой новой записи на сервер, его запрос SelectSQL переоткрываем
.
- Вызываем хранимые процедуры update_stock_in_totals
и update_sale_totals для приведения в соответствие
сумм в шапках документов суммам в их позициях.
- Подтверждаем транзакцию, перепроводим все документы вызывая процедуру UpadateTurmovers.
- Вызываем хранимую процедуру (она описана далее) для расчета
себестоимости проданных товаров и обновления этой себестоимости в позициях документов
продаж.
- Подтверждаем транзакцию, перепроводим все документы вызывая процедуру UpadateTurmovers.
Создадим в базе данных с помощью окна ISQL хранимую процедуру
, которая занимается вычислением средней стоимости проданных позиций на основе
проводок в таблице ACC_TURN и записывает полученные значения
в позиции документов продаж в поле cost_s.
Эта процедура обходит таблицу проводок в определенном порядке: id
товара, дата проводки, id шаблона операции. Встречая
при сканировании операцию «продажа» (template_id
= 103), рассчитанная средняя стоимость записывается в позицию соответствующего
документа doc_id. Обнуление остаточной стоимости amount и
количества quantity происходит в момент прохождения «границы» между
разными товарами. Далее записи в дебет увеличивают эти показатели
, а записи в кредит – уменьшают их. Средневзвешенная
цена вычисляется как отношение остаточной стоимости к количеству amount/
quantity на момент отгрузки.
Текст процедуры:
create procedure sale_item_update_cost(acc_id integer)
as
/*переменные для хранения полей*/
declare variable op_date date;
declare variable layer_id integer;
declare variable object_id integer;
declare variable template_id integer;
declare variable doc_id integer;
declare variable debit decimal(18,2);
declare variable credit decimal(18,2);
declare variable quantity_debit decimal(18,3);
declare variable quantity_credit decimal(18,3);
/*переменные для хранения текущей суммы и количества в партии товара*/
declare variable amount decimal(18,2);
declare variable quantity decimal(18,3);
declare variable avg_cost double precision;
/*переменная для храниения текущего id товара*/
declare variable goods integer;
begin
goods = -1;
for select
op_date, layer_id, object_id, template_id, doc_id,
debit, credit, quantity_debit, quantity_credit
from
acc_turn
where
acc_id = :acc_id and layer_id = 2
order by
object_id, op_date, template_id
into
:op_date, :layer_id, :object_id, :template_id, :doc_id,
:debit, :credit, :quantity_debit, :quantity_credit
do
begin
if (goods <> object_id) then /*начинаем очередной товар*/
begin
amount = 0;
quantity = 0;
goods = object_id;
end
if (template_id = 103) then /*103 - Продажа*/
begin
if (quantity <> 0) then
avg_cost = amount / quantity;
else
avg_cost = 0;
credit = quantity_credit * avg_cost;
/*изменяем стоимость в позициях документа "Продажи"*/
update sale_item set cost_s = quantity * :avg_cost
where id = :doc_id and goods = :object_id;
end
amount = amount + debit - credit;
quantity = quantity + quantity_debit - quantity_credit;
end
update template set is_changed = 1 where template_id = 103;
end;
Осталось создать обработчики для кнопок «Удалить документы» и
«Создать документы». Удаление документов может нам понадобиться,
если нас не удовлетворят результаты генерации, или если наш
проект будет работать неполностью или неправильно, из-за
каких-то программных ошибок. При нажатии кнопки «
Удалить документы» программа должна удалить все документы и все
проводки, связанные с ними.
Создадим обработчики OnClick для кнопок «Удалить документы» и
«Создать документы». Их обработчики содержат достаточно много программного
текста. Поэтому приводим полный листинг модуля:
unit monte_carlo;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
IBQuery, IBDatabase, ComCtrls, StdCtrls,IBCustomDataSet;
type
TForm1 = class(TForm)
qryContragent: TIBQuery;
traCurrent: TIBTransaction;
qryGoods: TIBQuery;
ProgressBar1: TProgressBar;
qryStock_In: TIBDataSet;
qrySale: TIBDataSet;
btnGenerate: TButton;
btnDelete: TButton;
qry: TIBQuery;
qryStock_In_Item: TIBDataSet;
qrySale_Item: TIBDataSet;
ProgressBar2: TProgressBar;
procedure FormCreate(Sender: TObject);
procedure btnDeleteClick(Sender: TObject);
procedure btnGenerateClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
traCurrent.StartTransaction;
qryContragent.Open;
qryContragent.FetchAll;
qryGoods.Open;
qryGoods.FetchAll;
end;
procedure TForm1.btnDeleteClick(Sender: TObject);
begin
Screen.Cursor := crHourGlass;
try
with qry do
begin
SQL.Text := 'DELETE FROM STOCK_IN';
ExecSQL;
SQL.Text := 'DELETE FROM SALE';
ExecSQL;
SQL.Text := 'update template set is_changed = 1'#13+
'where template_id in (102, 103)';
ExecSQL;
end;
qryStock_in.Close;
qrySale.Close;
traCurrent.CommitRetaining; //подтверждение транзакции
UpdateTurnovers(True); //перепроведение документов
btnGenerate.Enabled := True;
finally
Screen.Cursor := crDefault;
end;
ShowMessage('Удаление документов завершено');
end;
procedure TForm1.btnGenerateClick(Sender: TObject);
const
stock_in_count = 100;
stock_in_item_count = 1000;
sale_count = 350;
var
i, contragent,
goods, quantity, price_USD,
sales_from, sale_quantity: integer;
begin
btnGenerate.Enabled := False;
qryStock_in.Open;
qrySale.Open;
ProgressBar1.Max := stock_in_count + sale_count;
{Генерация поступлений на склад (только шапки)}
for i := 1 to stock_in_count do
begin
{выбираем случайного контрагента}
with qryContragent do
begin
RecNo := random(RecordCount) + 1;
contragent := FieldByName('ID').AsInteger;
end;
{создаем "Поступление на склад"}
with qryStock_In do
begin
Insert; //вставка записи в набор
FieldByName('DIR_ID').AsInteger := 1001; //Папка "Поступления"
FieldByName('STOCK_ACC').AsInteger := 1007; //Счет "Главный склад"
{Поступления происходят раз в 3 дня, начиная с января 2003 г}
FieldByName('ENTRY_DATE').AsDateTime := EncodeDate(2003,1,1) + 3*i;
FieldByName('DOC_KIND').AsInteger := 0; //Поступление от поставщика
FieldByName('ACC_ID').AsInteger := 1012; //Счет "Поставщики"
FieldByName('CONTRAGENT').AsInteger := contragent;
{1 + остаток от деления ID контрагента на 3 в качестве валюты документа}
FieldByName('LAYER_ID').AsInteger := 1 + contragent mod 3;
FieldByName('CALC_MODE').AsInteger := 1; //Расчет от цены c НДС
FieldByName('HAS_ENTRY').AsInteger := 1; //Товар поступил на склад
FieldByName('DOC_DATE').AsDateTime := FieldByName('ENTRY_DATE').AsDateTime;
FieldByName('VAT_RATE').AsInteger := 20; //Ставка НДС
FieldByName('EXCH_RATE_S').AsCurrency := 30+random(100)/100; //курс доллара
{курс валюты документа зависит от валюты документа}
case FieldByName('LAYER_ID').AsInteger of
1: FieldByName('EXCH_RATE_L').AsCurrency := 1; //курс рубля
2: FieldByName('EXCH_RATE_L').AsCurrency :=
FieldByName('EXCH_RATE_S').AsCurrency; //курс доллара
3: FieldByName('EXCH_RATE_L').AsCurrency := 32+random(100)/100; //курс ЕВРО
end;
{Всем суммам "всего" присваиваем 0}
FieldByName('TOTAL_L').AsCurrency := 0;
FieldByName('TOTAL_S').AsCurrency := 0;
FieldByName('TOTAL_R').AsCurrency := 0;
{Документы просто нумеруем подряд}
FieldByName('DOC_NO').AsString := IntToStr(i);
Post; //посылка на сервер
end;
ProgressBar1.Position := i; //отображаем прогресс
end;
{Генерация продаж (только шапки)}
for i := 1 to sale_count do
begin
{выбираем случайного контрагента}
with qryContragent do
begin
RecNo := random(RecordCount) + 1;
contragent := FieldByName('ID').AsInteger;
end;
{создаем "Продажу"}
with qrySale do
begin
Insert; //вставка записи в набор
FieldByName('DIR_ID').AsInteger := 1002; //Папка "Продажи"
FieldByName('STOCK_ACC').AsInteger := 1007; //Счет "Главный склад"
{Продажи происходят каждый день, начиная с 1 января 2003 г}
FieldByName('ENTRY_DATE').AsDateTime := EncodeDate(2003,1,1) + i;
FieldByName('CONTRAGENT').AsInteger := contragent;
{1 + остаток от деления ID контрагента на 3 в качестве валюты документа}
FieldByName('LAYER_ID').AsInteger := 1 + contragent mod 3;
FieldByName('CALC_MODE').AsInteger := 1; //Расчет от цены c НДС
FieldByName('HAS_ENTRY').AsInteger := 1; //Товар отгружен со склада
FieldByName('DOC_DATE').AsDateTime := FieldByName('ENTRY_DATE').AsDateTime;
FieldByName('VAT_RATE').AsInteger := 20; //Ставка НДС
FieldByName('EXCH_RATE_S').AsCurrency := 30+random(100)/100; //курс доллара
{курс валюты документа зависит от валюты документа}
case FieldByName('LAYER_ID').AsInteger of
1: FieldByName('EXCH_RATE_L').AsCurrency := 1; //курс рубля
2: FieldByName('EXCH_RATE_L').AsCurrency :=
FieldByName('EXCH_RATE_S').AsCurrency; //курс доллара
3: FieldByName('EXCH_RATE_L').AsCurrency := 32+random(100)/100; //курс ЕВРО
end;
{Всем суммам "всего" присваиваем 0}
FieldByName('TOTAL_L').AsCurrency := 0;
FieldByName('TOTAL_S').AsCurrency := 0;
FieldByName('TOTAL_R').AsCurrency := 0;
{Документы просто нумеруем подряд}
FieldByName('DOC_NO').AsString := IntToStr(i);
FieldByName('PRICE_TYPE').AsInteger := 0;
FieldByName('PRICE_DISCOUNT').AsCurrency := 0;
Post; //посылка на сервер
end;
ProgressBar1.Position := stock_in_count + i; //отображаем прогресс
end;
// ShowMessage('Генерация шапок документов завершена');
{Генерация позиций в обоих типах документов}
ProgressBar2.Max := stock_in_item_count;
for i := 1 to stock_in_item_count do
begin
{выбираем случайный товар}
with qryGoods do
begin
RecNo := random(RecordCount) + 1;
goods := FieldByName('ID').AsInteger;
end;
{выбираем случайное поступление на склад}
with qryStock_In do
RecNo := random(RecordCount) + 1;
{разыгрываем величины}
quantity := random(5) + 1; //поступившее количество (партия)
price_USD := 100 + random(200); //закупочная цена USD
{создаем строку в поступлении на склад}
with qryStock_In_Item do
begin
Open;
Insert;
FieldByName('ID').AsInteger := qryStock_In.FieldByName('ID').AsInteger;
FieldByName('GOODS').AsInteger := goods;
FieldByName('QUANTITY').AsInteger := quantity;
{пересчитываем цену в валюту документа
по кросс-курсу и округляем до центов}
FieldByName('PRICE_L').AsCurrency := round(price_USD *
qryStock_In.FieldByName('EXCH_RATE_S').AsCurrency /
qryStock_In.FieldByName('EXCH_RATE_L').AsCurrency * 100)/100;
{Вычисляем цену без НДС в валюте документа}
FieldByName('PRICE_L_WO_VAT').AsCurrency :=
FieldByName('PRICE_L').AsCurrency/
(1 + qryStock_In.FieldByName('VAT_RATE').AsCurrency/100);
{пересчитываем цену в рубли и округляем до копеек}
FieldByName('PRICE_R').AsCurrency := round(price_USD *
qryStock_In.FieldByName('EXCH_RATE_S').AsCurrency * 100)/100;
{Вычисляем цену без НДС в рублях}
FieldByName('PRICE_R_WO_VAT').AsCurrency :=
FieldByName('PRICE_R').AsCurrency/
(1 + qryStock_In.FieldByName('VAT_RATE').AsCurrency/100);
{Вычисляем суммы}
FieldByName('AMOUNT_L').AsCurrency :=
FieldByName('PRICE_L').AsCurrency * quantity;
FieldByName('AMOUNT_S').AsCurrency := price_USD * quantity;
FieldByName('AMOUNT_R').AsCurrency :=
FieldByName('PRICE_R').AsCurrency * quantity;
{находим документ продажи, совпадающий по дате с этим поступлением}
qrySale.Locate('ENTRY_DATE',
qryStock_In.FieldByName('ENTRY_DATE').AsDateTime,
MkSet());
sales_from := qrySale.RecNo; //запоминаем № строки набора
Post;
Close;
end;
{делаем наценку 10%}
price_USD := price_USD * 1.1;
{повторяем идущий далее цикл, пока не распродадим всю эту партию}
repeat
sale_quantity := random(quantity) + 1; //продаваемое количество
{выбираем случайную продажу}
with qrySale do
RecNo := sales_from + random(RecordCount - sales_from) + 1;
{создаем позицию в продажах}
with qrySale_Item do
begin
Open;
Insert;
FieldByName('ID').AsInteger := qrySale.FieldByName('ID').AsInteger;
FieldByName('GOODS').AsInteger := goods;
FieldByName('QUANTITY').AsInteger := sale_quantity;
{пересчитываем цену в валюту документа
по кросс-курсу и округляем до центов}
FieldByName('PRICE_L').AsCurrency := round(price_USD *
qrySale.FieldByName('EXCH_RATE_S').AsCurrency /
qrySale.FieldByName('EXCH_RATE_L').AsCurrency * 100)/100;
{Вычисляем цену без НДС в валюте документа}
FieldByName('PRICE_L_WO_VAT').AsCurrency :=
FieldByName('PRICE_L').AsCurrency/
(1 + qrySale.FieldByName('VAT_RATE').AsCurrency/100);
{пересчитываем цену в рубли и округляем до копеек}
FieldByName('PRICE_R').AsCurrency := round(price_USD *
qrySale.FieldByName('EXCH_RATE_S').AsCurrency * 100)/100;
{Вычисляем цену без НДС в рублях}
FieldByName('PRICE_R_WO_VAT').AsCurrency :=
FieldByName('PRICE_R').AsCurrency/
(1 + qrySale.FieldByName('VAT_RATE').AsCurrency/100);
{Вычисляем суммы}
FieldByName('AMOUNT_L').AsCurrency :=
FieldByName('PRICE_L').AsCurrency * sale_quantity;
FieldByName('AMOUNT_S').AsCurrency := price_USD * sale_quantity;
FieldByName('AMOUNT_R').AsCurrency :=
FieldByName('PRICE_R').AsCurrency * sale_quantity;
{списываем пока стоимость 0}
FieldByName('COST_S').AsCurrency := 0;
Post;
Close;
end;
quantity := quantity - sale_quantity; //уменьшаем количество в партии
until quantity <= 0;
ProgressBar2.Position := i; //отображаем прогресс
end;
{Расчет сумм в шапках документов}
with qry do
begin
SQL.Text := 'EXECUTE PROCEDURE update_stock_in_totals';
ExecSQL;
SQL.Text := 'EXECUTE PROCEDURE update_sale_totals';
ExecSQL;
{Удаляем документы продаж, которые не получили позиций вообще}
SQL.Text := 'DELETE FROM SALE WHERE TOTAL_S = 0';
ExecSQL;
end;
traCurrent.CommitRetaining; //подтверждение транзакции
UpdateTurnovers(True); //перепроведение документов
// ShowMessage('Генерация позиций документов завершена');
{Расчет средней себестоимости проданных товаров}
with qry do
begin
SQL.Text := 'EXECUTE PROCEDURE sale_item_update_cost(1007)';
ExecSQL;
end;
traCurrent.CommitRetaining; //подтверждение транзакции
UpdateTurnovers(True); //перепроведение документов
ProgressBar1.Position := 0;
ProgressBar2.Position := 0;
ShowMessage('Создание документов завершено');
end;
end.
Мы не будем встраивать этот проект в конфигурацию, так
как он понадобится нам всего один раз. Мы просто
запустим его из режима дизайнера и создадим все документы.
После запуска проекта:
- Удаляем документы
- Создаем документы
Выйдем из режима дизайнера. Откроем «Проводник про документам
». Теперь у нас имеется 100 документов поступлений на склад
и около 300 документов продаж:
Вызовем окно «Баланс» и откроем в нем регистр
«Товары», слой «USD». Выберем счет «
Главный склад» и вызовем Т-счет (F3
):
В Т-счете мы видим движение товаров через главный
склад. Записи в дебет соответствуют поступлениям на склад,
записи в кредит – продажам. Для того чтобы проверить
, правильно ли мы рассчитали среднюю стоимость товаров, нужно
взглянуть на баланс счета «Главный склад» на дату
более позднюю, чем дата самого последнего документа. Алгоритм
генерации документов устроен был так, что все, что
приходило на склад должно было отгрузиться. Поэтому, скажем
, на 1 января 2004 г. на «Главном
складе» товаров быть не должно и сумма этого счета
должна быть нулевой. Выберем «запредельную» дату в
селекторе дат окна «Баланс» и взглянем на баланс
всей компании. Для этого слева откроем «Средства»,
а справа – «Обязательства и Капитал». Включим режим
«консолидированно» и выберем валюту USD:
Мы видим, что стоимость товарного остатка на Главном складе
близка к нулю. Значит мы все сделали правильно.
Остаточная цифра -1 цент связана с ошибками округления при
определении средней стоимости в документах, содержащих несколько раз один
и тот же товар (у заказчика такие документы не
предвидятся). Сумма средств компании около $655 000.
Валовая годовая прибыль около $60 000.Все средства
$655 000 находятся в счетах дебиторов у «Покупателей
». Все обязательства $595 000 сосредоточены на счетах кредиторов
у «Поставщиков». Прибыль неравномерно росла в течение года
, что можно увидеть на диаграмме. Для получения диаграммы
воспользуемся пунктом меню Бухгалтерия/Временные диаграммы и в появившемся
окне выберем отчет «Кредит-оборот минус Дебет-
оборот», слой «Доллары США» и нажмем кнопку
«Запрос»:
Создадим архивную копию базы данных.
На этом мы закончили розыгрыш поступлений и продаж.
Мы можем увидеть общее число документов и бухгалтерских проводок в
базе данных. Для этого нужно использовать пункт меню Инструменты
/Сведения о периоде. Появится окно:
Итого мы создали около 400 документов. И получили около
4500 проводок.
Для выяснения количества сгенерированных позиций можно сделать в окне ISQL
запрос такого рода:
select count(*) from sale_item
union
select count(*) from stock_in_item
Мы получим что-то порядка 1000 одних и 1730
других.
Если поделить число проводок на общее число проведенных позиций,
то мы получим:
4500 / 2730 = 1.65
Таким образом, наши шаблоны проводок создают в среднем менее
2 проводок на каждую позицию документа. Это при том
, что шаблон «поступления на склад» содержит записи
по 4 счетам, а шаблон отгрузки содержит записи по
6-и счетам. Казалось бы мы должны были
получить 4x1000 + 6x1730 = 14380 проводок, а мы
имеем всего 4530. Экономия связана с группировкой записей перед
вставкой их в таблицу проводок. Экономия важна для времени
запроса баланса. Заметим, что при имеющихся 400 документах
мы получили баланс за 0.1 сек. Это
одно из преимуществ смешанных проводок – значительная экономия места и
выигрыш в скорости.
При создании любой конфигурации имеет смысл осуществлять генерацию документов наподобие
той, что мы с вами произвели. Это позволит
экспериментально оценить время, которое затрачивается на SQL-запросы
и убедиться в том, что запросы работают быстро и
правильно. Мы рекомендуем тестировать конфигурацию до того, как
пользователи начнут вносить данные. А отлаживать и тестировать ее
на пустых таблицах или на ничтожном количестве данных весьма неудобно
и рискованно для разработчика.
|