На главную   На главную   Форумы Новости Документация Скачать Купить  
Регистрация  
Система Allegro
Oб Allegro Характеристики Пример конфигурации Документация База ошибок Развитие
Версия для печати К списку книг Вернуться к оглавлению Предыдущий параграф Следующий параграф
Поиск по книге

Глава 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

Мы будем использовать следующий алгоритм генерации документов:

  1. Добавляем 100 записей в компонент qryStock_In. Каждая запись соответствует одному документу поступления. В процессе добавления документы распределяем равномерно по датам на 300 дней, с интервалом 3 дня, начиная с 1 января 2003 г. Затем добавляем 350 записей в компонент qrySale. Каждая запись соответствует одному документу продаж. В процессе добавления распределяем их по одному документу на каждую дату, начиная с 1 января 2003г. В каждом документе выбираем случайного контрагента, присваивая случайное целое число свойству RecNo (номер записи) компонента qryContragent и копируя значение поля ID из этого компонента в документ. В результате мы получим 450 документов ( пока без позиций) в таблицах базы.
  2. В цикле создаем 1000 позиций «Поступлений на склад», используя компонент qryStock_In_Item. Документ для добавления позиции выбираем каждый раз, присваивая случайное целое число свойству RecNo компонента qryStock_In. После посылки компонентом qryStock_In_Item каждой новой записи на сервер , переоткрываем его запрос SelectSQL. Благодаря тому, что в этом запросе имеется условие ID = -1, после переоткрытия запрос возвращает пустой набор. Это будет защищать нас от замедления процесса генерации позиций, вызванного накоплением позиций в компоненте qryStock_In_Item. После создания позиции в документе поступления, распределяем эту партию случайным образом по нескольким документам продаж, датированным позднее данного поступления. Это делаем во внутреннем цикле путем вставки записей в компонент qrySale_Item. После посылки компонентом qrySale_Item каждой новой записи на сервер, его запрос SelectSQL переоткрываем .
  3. Вызываем хранимые процедуры update_stock_in_totals и update_sale_totals для приведения в соответствие сумм в шапках документов суммам в их позициях.
  4. Подтверждаем транзакцию, перепроводим все документы вызывая процедуру UpadateTurmovers.
  5. Вызываем хранимую процедуру (она описана далее) для расчета себестоимости проданных товаров и обновления этой себестоимости в позициях документов продаж.
  6. Подтверждаем транзакцию, перепроводим все документы вызывая процедуру 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-запросы и убедиться в том, что запросы работают быстро и правильно. Мы рекомендуем тестировать конфигурацию до того, как пользователи начнут вносить данные. А отлаживать и тестировать ее на пустых таблицах или на ничтожном количестве данных весьма неудобно и рискованно для разработчика.



Пример создания склада в Allegro Наверх