Оптимизация документа ОказаниеУслуги
Первое, что мы сделаем для оптимизации документа "ОказаниеУслуги" – удалим реквизит табличной части "Стоимость", который нам не понадобится в будущем.
Также следует удалить соответствующую колонку из табличного поля, расположенного в форме.
После этого можно полностью удалить содержимое обработчика события "ОбработкаПроведения" в модуле документа и создать в нем заготовку процедуры проведения. Текст запроса, выполняемого в режиме оперативного проведения, будет отличаться от запроса, выполняемого при неоперативном проведении, поэтому формирование текста запроса мы включим в условие Если...Иначе...КонецЕсли:
Процедура ОбработкаПроведения(Отказ, Режим)
Запрос = Новый Запрос;
Если Режим = РежимПроведенияДокумента.Оперативный Тогда
Запрос.Текст =
;
Иначе
Запрос.Текст =
;
КонецЕсли;
КонецПроцедуры
Вызовем конструктор запроса и раскроем таблицу "ПереченьНоменклатуры" табличной части документа "ОказаниеУслуги" и выберем из нее поля:
· | "Номенклатура", |
· | "Количество", |
· | "ВидНоменклатуры", |
· | "Сумма".[238] |
Эти поля будут нужны нам для задания значений измерений регистров и их ресурсов. Кроме того, поле "ВидНоменклатуры" понадобится нам для анализа того, чем является номенклатура, указанная в документе: материалом или услугой:
Теперь задумаемся о том, что для указания значений ресурса "Стоимость" регистров "СтоимостьМатериалов" и "Продажи" нам понадобится рассчитать текущую стоимость номенклатуры как частное стоимости остатка этого материала и его оставшегося количества.
Поэтому добавим к списку выбранных таблиц еще две таблицы:
· | "РегистрНакопления.СтоимостьМатериалов.Остатки", |
· | "РегистрНакопления.ОстаткиМатериалов.Остатки": |
Для этих виртуальных таблиц нам нужно будет задать параметры. Эти параметры будут одинаковы для обеих таблиц. Они будут включать в себя момент времени, на который должны быть получены остатки этих регистров, и условие получения данных.[239]
Условие получения данных указывает, что остатки должны быть получены только по тем позициям номенклатуры, которые содержатся в проводимом документе (перед выполнением запроса мы передадим в параметр "СписокНоменклатурыДокумента" список всех позиций номенклатуры, содержащихся в проводимом документе):
Следует внимательно подходить к использованию виртуальных таблиц запросов. В частности, необходимо уделять особое внимание максимально возможному использованию параметров этих таблиц. Например, в нашем случае можно было бы и не использовать параметр "Условие", а ограничить выбранные поля уже в самом запросе, указав в условии ПО равенство номенклатуры из документа и материала из таблицы остатков. В результате мы получили бы тот же самый результат, однако no производительности этот способ сильно отличался бы от того, который мы используем.
В самом деле, в нашем варианте виртуальная таблица предоставит нам ровно столько записей, сколько различных элементов номенклатуры содержится в проводимом документе. Если же не указывать условие, виртуальная таблица предоставит нам записи no абсолютно всем элементам номенклатуры, информация о которых есть в регистре накопления. И уже в самом нашем запросе мы будем отбирать из этой огромной массы записей лишь несколько, которые нам действительно нужны.
Очевидно, что второй вариант будет работать дольше, и время выполнения такого запроса будет зависеть в основном не от количества данных, содержащихся в документе (т.е. реального количества обрабатываемой информации), а от размера регистра накопления.[240]
Кроме того, что подобный вариант снижает производительность конфигурации, могут возникать ситуации, когда результаты, полученные одним и другим способом, будут различны. Такое, например, вполне возможно при использовании виртуальной таблицы регистра сведений "СрезПоследних". Более подробно можно прочитать об этом на диске ИТС (информационно технологического сопровождения) в статье "Использование отборов в запросах с виртуальными таблицами".
Теперь вспомним о том, что документы "ОказаниеУслуги" могут быть проведены как в оперативном, так и в неоперативном режиме.
О концепции оперативного и неоперативного проведения документов можно прочитать в главе "Концепция оперативного и неоперативного проведения документов" на странице 541.
[241]
Для этой виртуальной таблицы мы также укажем "МоментВремени", а в условии напишем, что материал должен находиться в списке номенклатуры и склад должен быть равен складу указанному в документе:
Теперь из этой виртуальной таблицы мы выберем поле "КоличествоОстаток":
На этом формирование списка выбранных полей закончено, и мы можем перейти к заданию условий связи между выбранными таблицами. Каждая из виртуальных таблиц должна быть связана с таблицей документа таким образом, что для всех записей таблицы документа должны подбираться имеющиеся записи виртуальной таблицы, причем номенклатура в таблице документа должна быть равна материалу из виртуальной таблицы:
[242]
Теперь перейдем на закладку "Дополнительно" и установим флаг "Для изменения". Предложение ДЛЯ ИЗМЕНЕНИЯ позволяет заблаговременно заблокировать чтение указанных данных (которые могут читаться транзакцией другого соединения) уже при считывании, чтобы исключить взаимные блокировки при записи. Это предложение дает возможность указать в запросе таблицы, считываемые данные которых предполагается изменять.
Более подробно об использовании предложения ДЛЯ ИЗМЕНЕНИЯ можно прочесть на диске ИТС (информационно-технологического сопровождения) в статье "Использование предложения ДЛЯ ИЗМЕНЕНИЯ в языке запросов".
Перейдем на закладку "Условия" и зададим условие отбора из таблицы документа только строк проводимого документа (ссылка на него будет передана в параметр запроса "Ссылка"):
[243]
Перейдем на закладку "Псевдонимы" и зададим следующие псевдонимы полей:
· | "НоменклатураВидНоменклатуры" – "ВидНоменклатуры", |
· | "КличествоОстаток1> – "КоличествоОстатокНаСкладе". |
Запрос = Новый Запрос;
Если Режим = РежимПроведенияДокумента.Оперативный Тогда
Запрос.Текст =
"ВЫБРАТЬ
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура,
| ОказаниеУслугиПереченьНоменклатуры.Количество,
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры,
| ОказаниеУслугиПереченьНоменклатуры.Сумма,
| ОстаткиМатериаловОстатки.КоличествоОстаток,
| СтоимостьМатериаловОстатки.СтоимостьОстаток,
| ОстаткиМатериаловОстаткиНаСкладе.КоличествоОстаток КАК КоличествоОстатокНаСкладе
|ИЗ
| Документ.ОказаниеУслуги. ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК СтоимостьМатериаловОстатки
| ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = СтоимостьМатериаловОстатки.Материал
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК ОстаткиМатериаловОстатки
| ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = ОстаткиМатериаловОстатки.Материал
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(
| &МоментВремени,
| Материал В (&СписокНоменклатурыДокумента)
| И Склад = &СкладВДокументе) КАК ОстаткиМатериаловОстаткиНаСкладе
| ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = ОстаткиМатериаловОстаткиНаСкладе.Материал
|ГДЕ [244]
| ОказаниеУслугиПереченьНоменклатуры.Ссылка = &Ссылка
|
|ДЛЯ ИЗМЕНЕНИЯ
| РегистрНакопления.СтоимостьМатериалов.Остатки,
| РегистрНакопления.ОстаткиМатериалов.Остатки";
Иначе
Запрос.Текст =
;
КонецЕсли;
КонецПроцедуры
Текст запроса для случая неоперативного проведения документа будет практически таким же, за исключением того, что в нем будет отсутствовать третье левое соединение и, соответственно, поле "КоличествоОстатокНаСкладе", т.к. проверку остатков в этом случае мы выполнять не будем:
Иначе
Запрос.Текст =
"ВЫБРАТЬ
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура,
| ОказаниеУслугиПереченьНоменклатуры.Количество,
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры,
| ОказаниеУслугиПереченьНоменклатуры.Сумма,
| ОстаткиМатериаловОстатки.КоличествоОстаток,
| СтоимостьМатериаловОстатки.СтоимостьОстаток
|ИЗ
| Документ.ОказаниеУслуги.ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК СтоимостьМатериаловОстатки
| ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = СтоимостьМатериаловОстатки.Материал
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК ОстаткиМатериаловОстатки
| ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = [245] ОстаткиМатериаловОстатки.Материал
|ГДЕ
| ОказаниеУслугиПереченьНоменклатуры.Ссылка = &Ссылка
|
|ДЛЯ ИЗМЕНЕНИЯ
| РегистрНакопления.СтоимостьМатериалов.Остатки,
| РегистрНакопления.ОстаткиМатериалов.Остатки";
КонецЕсли;
КонецПроцедуры
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("СкладВДокументе",Склад);
...
| РегистрНакопления.ОстаткиМатериалов.Остатки";
КонецЕсли;
Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
Запрос.УстановитьПараметр("СписокНоменклатурыДокумента", ПереченьНоменклатуры.ВыгрузитьКолонку("Номенклатура"));
Запрос.УстановитьПараметр("Ссылка",Ссылка);
...
КонецПроцедуры
После этого добавим получение результата запроса и цикл его обхода:
КонецЕсли;
Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
Запрос.УстановитьПараметр("СписокНоменклатурыДокумента", ПереченьНоменклатуры.ВыгрузитьКолонку("Номенклатура"));
Запрос.УстановитьПараметр("Ссылка",Ссылка);
ВыборкаРезультатаЗапроса = Запрос.Выполнить().Выбрать();
Пока ВыборкаРезультатаЗапроса.Следующий() Цикл
КонецЦикла;
КонецПроцедуры
КонецЕсли;
Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
Запрос.УстановитьПараметр("СписокНоменклатурыДокумента", ПереченьНоменклатуры.ВыгрузитьКолонку("Номенклатура"));
Запрос.УстановитьПараметр("Ссылка",Ссылка);
ВыборкаРезультатаЗапроса = Запрос.Выполнить().Выбрать();
Пока ВыборкаРезультатаЗапроса.Следующий() Цикл
//Проверить остаток при оперативном проведении
Если Режим = РежимПроведенияДокумента.Оперативный Тогда
Если ВыборкаРезультатаЗапроса.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Материал Тогда
Остаток = ?(ВыборкаРезультатаЗапроса.КоличествоОстатокНаСкладе = Null, 0, ВыборкаРезультатаЗапроса.КоличествоОстатокНаСкладе);
Если Остаток < ВыборкаРезультатаЗапроса.Количество Тогда
Сообщить("Материала " + СокрЛП(ВыборкаРезультатаЗапроса.Номенклатура) + " имеется только " + Остаток);
Отказ = Истина;
Возврат;
КонецЕсли;
КонецЕсли; [247]
КонецЕсли;
КонецЦикла;
КонецПроцедуры
КонецЕсли;
Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
Запрос.УстановитьПараметр("СписокНоменклатурыДокумента", ПереченьНоменклатуры.ВыгрузитьКолонку("Номенклатура"));
Запрос.УстановитьПараметр("Ссылка",Ссылка);
ВыборкаРезультатаЗапроса = Запрос.Выполнить().Выбрать();
Пока ВыборкаРезультатаЗапроса.Следующий() Цикл
//Проверить остаток при оперативном проведении
Если Режим = РежимПроведенияДокумента.Оперативный Тогда
Если ВыборкаРезультатаЗапроса.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Материал Тогда
Остаток = ?(ВыборкаРезультатаЗапроса.КоличествоОстатокНаСкладе = Null, 0, ВыборкаРезультатаЗапроса.КоличествоОстатокНаСкладе);
Если Остаток < ВыборкаРезультатаЗапроса.Количество Тогда
Сообщить("Материала " + СокрЛП(ВыборкаРезультатаЗапроса.Номенклатура) + " имеется только " + Остаток);
Отказ = Истина;
Возврат;
КонецЕсли;
КонецЕсли;
КонецЕсли;
//Сформировать движения
Если ВыборкаРезультатаЗапроса.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Материал Тогда
// регистр ОстаткиМатериалов Расход
Движение = Движения.ОстаткиМатериалов.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Материал = ВыборкаРезультатаЗапроса.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = ВыборкаРезультатаЗапроса.Количество;
// регистр СтоимостьМатериалов Расход
Движение = Движения.СтоимостьМатериалов.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход; [248]
Движение.Период = Дата;
Движение.Материал = ВыборкаРезультатаЗапроса.Номенклатура;
//расчитать стоимость материала
СтоимостьМатериала = ?(ВыборкаРезультатаЗапроса.КоличествоОстаток = Null, 0,
ВыборкаРезультатаЗапроса.СтоимостьОстаток / ВыборкаРезультатаЗапроса.КоличествоОстаток);
Движение.Стоимость = СтоимостьМатериала * ВыборкаРезультатаЗапроса.Количество;
КонецЕсли;
// регистр Продажи
Движение = Движения.Продажи.Добавить();
Движение.Период = Дата;
Движение.Номенклатура = ВыборкаРезультатаЗапроса.Номенклатура;
Движение.Клиент = Клиент;
Движение.Мастер = Мастер;
Движение.Количество = ВыборкаРезультатаЗапроса.Количество;
Движение.Выручка = ВыборкаРезультатаЗапроса.Сумма;
Если ВыборкаРезультатаЗапроса.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Материал Тогда
Движение.Стоимость = СтоимостьМатериала * ВыборкаРезультатаЗапроса.Количество;
Иначе
Движение.Стоимость = 0;
КонецЕсли;
КонецЦикла;
// записать движения регистров
Движения.ОстаткиМатериалов.Записать();
Движения.СтоимостьМатериалов.Записать();
Движения.Продажи.Записать();
КонецПроцедуры
В заключение следует сделать небольшое отступление, которое касается задания параметров виртуальных таблиц, использовавшихся в наших запросах.[249]
Как в первом, так и во втором запросах мы использовали условие что материал должен находиться в списке значений, задаваемом одним из параметров запроса:
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК СтоимостьМатериаловОстатки
...
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(
| &МоментВремени,
| Материал В
| (ВЫБРАТЬ РАЗЛИЧНЫЕ
| ОказаниеУслугиПереченьНоменклатуры.Номенклатура
| ИЗ
| Документ.ОказаниеУслуги.ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры
| ГДЕ
| ОказаниеУслугиПереченьНоменклатуры.Ссылка = &Ссылка))
...
Теперь мы можем на некоторое время отвлечься от запросов, с которыми мы достаточно "плотно" работали в этой главе, и обратить свое внимание на не менее интересные возможности, которые предоставляет разработчику платформа 1С:Предприятие 8.0.[250]