Глава 9. СОЗДАЕМ ОКОННЫЙ ИНТЕРФЕЙС ДОКУМЕНТА «ПРОДАЖА»
Реализуем расчет средней себестоимости в документе «Продажа».
Мы с вами уже рассчитывали среднюю себестоимость, когда занимались
генерацией продаж. Тогда мы создали в базе данных хранимую
процедуру SALE_ITEM_UPDATE_COST, которая
вычисляла среднюю стоимость каждого проданного товара и записывала ее в
позиции всех документов «Продажа». Вычисление средней стоимости делалось
на основе данных в таблице бухгалтерских проводок ACC_TURN
.
Мы создадим новую хранимую процедуру SALE_ITEM_SET
_COST на основе имеющейся процедуры, ограничив ее действие
позициями одного документа.
Вызовем окно интерактивного SQL и введем такой текст (жирным
шрифтом выделены отличия от процедуры SALE_ITEM_UPDATE
_COST):
create procedure sale_item_set_cost(id integer)
as
/*переменные для хранения полей*/
declare variable acc_id integer;
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;
select stock_acc from sale where id = :id
into :acc_id;
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 and
object_id in (select goods from sale_item where id = :id)
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) and (doc_id = id)) 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
and cost_s <> (quantity * :avg_cost);
end
amount = amount + debit - credit;
quantity = quantity + quantity_debit - quantity_credit;
end
end
Выполним команду в окне ISQL, нажав Ctrl+Enter
.
Обратите внимание, что мы используем вложенный запрос в условии
WHERE основного запроса:
object_id in (select goods from sale_item where id = :id)
Этот второй запрос возвращает нам множество всех товаров, себестоимость
которых следует уточнить. Вычисленная стоимость записывается только в документ
с внутренним номером id, который передается при вызове процедуры
при помощи входного параметра.
Кроме этого мы добавили условие в команду UPDATE:
and cost_s <> (quantity * :avg_cost)
Это защитит от обновления те позиции документа, себестоимость в
которых уже рассчитана и рассчитана правильно. Дело в том
, что мы будем вызывать созданную нами хранимую процедуру при
сохранении документа «Продажа». А так как пользователь может
несколько раз редактировать и сохранять документ, то не хотелось
бы, чтобы это приводило к многократной перезаписи одних и
тех же позиций в базе. Многоверсионный механизм транзакций сервера
InterBase при каждой модификации записи в таблице создает ее копию
и лишние модификации могут приводить к «замусориванию» базы
данных такими копиями и неоправданному увеличению размера файла базы.
Нам осталось добавить вызов хранимой процедуры SALE_ITEM_
SET_COST в проект оконного интерфейса «Продажа».
Закроем окно ISQL и войдем в режим «Дизайнер».
Откроем проект sale_project.ipr.
Добавим на форму SaleForm компонент IBQuery с палитры InterBase.
Придадим в Инспекторе объектов его свойствам значения:
Свойство |
Значение |
Transaction |
TraCurrent |
DataSource |
DsrMaster |
SQL |
execute procedure sale_item_set_cost(:
id) |
Name |
QrySetItemCost |
В обработчик команды actSave добавим текст, выделенный жирным шрифтом
:
procedure TSaleForm.actSaveExecute(Sender: TObject);
begin
qryTotals.Open;
qryMaster.Edit;
qryMaster.FieldByName('TOTAL_L').AsCurrency := qryTotals.Fields[0].AsCurrency;
qryMaster.FieldByName('TOTAL_R').AsCurrency := qryTotals.Fields[1].AsCurrency;
qryMaster.FieldByName('TOTAL_S').AsCurrency := qryTotals.Fields[2].AsCurrency;
qryMaster.Post;
qryTotals.Close;
MakeEntries('SALE', qryMaster.FieldByName('ID').AsInteger, traCurrent);
if qryMaster.FieldByName('HAS_ENTRY').AsInteger = 1 then
begin
{Вызываем перерасчет себестоимости}
qrySetItemCost.ExecSQL;
{повторно проводим документ}
MakeEntries('SALE', qryMaster.FieldByName('ID').AsInteger, traCurrent);
end;
traCurrent.CommitRetaining; //подтвердить транзакцию
Modified := False;
actSave.Enabled := Modified;
RefreshExplorer; //пересветить "Проводник по документам"
RefreshBalance; //пересветить "Баланс"
end;
|