1С-Предприятие 8.0. Практическое пособие разработчика

       

Оптимизация документа ОказаниеУслуги


Первое, что мы сделаем для оптимизации документа "ОказаниеУслуги" – удалим реквизит табличной части "Стоимость", который нам не понадобится в будущем.

Также следует удалить соответствующую колонку из табличного поля, расположенного в форме.

После этого можно полностью удалить содержимое обработчика события "ОбработкаПроведения" в модуле документа и создать в нем заготовку процедуры проведения. Текст запроса, выполняемого в режиме оперативного проведения, будет отличаться от запроса, выполняемого при неоперативном проведении, поэтому формирование текста запроса мы включим в условие Если...Иначе...КонецЕсли:

Процедура ОбработкаПроведения(Отказ, Режим)

   Запрос = Новый Запрос;

   Если Режим = РежимПроведенияДокумента.Оперативный Тогда

       Запрос.Текст =

       ;



   Иначе

       Запрос.Текст =

       ;

   КонецЕсли;

КонецПроцедуры

Вызовем конструктор запроса и раскроем таблицу "ПереченьНоменклатуры" табличной части документа "ОказаниеУслуги" и выберем из нее поля:

·"Номенклатура",
·"Количество",
·"ВидНоменклатуры",
·"Сумма".[238]

Эти поля будут нужны нам для задания значений измерений регистров и их ресурсов. Кроме того, поле "ВидНоменклатуры" понадобится нам для анализа того, чем является номенклатура, указанная в документе: материалом или услугой:

Теперь задумаемся о том, что для указания значений ресурса "Стоимость" регистров "СтоимостьМатериалов" и "Продажи" нам понадобится рассчитать текущую стоимость номенклатуры как частное стоимости остатка этого материала и его оставшегося количества.

Поэтому добавим к списку выбранных таблиц еще две таблицы:

·"РегистрНакопления.СтоимостьМатериалов.Остатки",
·"РегистрНакопления.ОстаткиМатериалов.Остатки":






Для этих виртуальных таблиц нам нужно будет задать параметры. Эти параметры будут одинаковы для обеих таблиц. Они будут включать в себя момент времени, на который должны быть получены остатки этих регистров, и условие получения данных.[239]

Условие получения данных указывает, что остатки должны быть получены только по тем позициям номенклатуры, которые содержатся в проводимом документе (перед выполнением запроса мы передадим в параметр "СписокНоменклатурыДокумента" список всех позиций номенклатуры, содержащихся в проводимом документе):





Узнай больше!

Следует внимательно подходить к использованию виртуальных таблиц запросов. В частности, необходимо уделять особое внимание максимально возможному использованию параметров этих таблиц. Например, в нашем случае можно было бы и не использовать параметр "Условие", а ограничить выбранные поля уже в самом запросе, указав в условии ПО равенство номенклатуры из документа и материала из таблицы остатков. В результате мы получили бы тот же самый результат, однако no производительности этот способ сильно отличался бы от того, который мы используем.

В самом деле, в нашем варианте виртуальная таблица предоставит нам ровно столько записей, сколько различных элементов номенклатуры содержится в проводимом документе. Если же не указывать условие, виртуальная таблица предоставит нам записи no абсолютно всем элементам номенклатуры, информация о которых есть в регистре накопления. И уже в самом нашем запросе мы будем отбирать из этой огромной массы записей лишь несколько, которые нам действительно нужны.

Очевидно, что второй вариант будет работать дольше, и время выполнения такого запроса будет зависеть в основном не от количества данных, содержащихся в документе (т.е. реального количества обрабатываемой информации), а от размера регистра накопления.[240]

Кроме того, что подобный вариант снижает производительность конфигурации, могут возникать ситуации, когда результаты, полученные одним и другим способом, будут различны. Такое, например, вполне возможно при использовании виртуальной таблицы регистра сведений "СрезПоследних". Более подробно можно прочитать об этом на диске ИТС (информационно технологического сопровождения) в статье "Использование отборов в запросах с виртуальными таблицами".



После того, как будут заданы параметры обеих виртуальных таблиц регистров накопления, выберем из них поля "СтоимостьОстаток" и "КоличествоОстаток":



Теперь вспомним о том, что документы "ОказаниеУслуги" могут быть проведены как в оперативном, так и в неоперативном режиме.



Узнай больше!

О концепции оперативного и неоперативного проведения документов можно прочитать в главе "Концепция оперативного и неоперативного проведения документов" на странице 541.

Поскольку в оперативном режиме нам понадобится контролировать остатки списываемой номенклатуры на складе, выберем еще раз виртуальную таблицу регистра накопления "ОстаткиМатериалов" и переименуем ее в "ОстаткиМатериаловОстаткиНаСкладе":



[241]

Для этой виртуальной таблицы мы также укажем "МоментВремени", а в условии напишем, что материал должен находиться в списке номенклатуры и склад должен быть равен складу указанному в документе:



Теперь из этой виртуальной таблицы мы выберем поле "КоличествоОстаток":



На этом формирование списка выбранных полей закончено, и мы можем перейти к заданию условий связи между выбранными таблицами. Каждая из виртуальных таблиц должна быть связана с таблицей документа таким образом, что для всех записей таблицы документа должны подбираться имеющиеся записи виртуальной таблицы, причем номенклатура в таблице документа должна быть равна материалу из виртуальной таблицы:





[242]

Теперь перейдем на закладку "Дополнительно" и установим флаг "Для изменения". Предложение ДЛЯ ИЗМЕНЕНИЯ позволяет заблаговременно заблокировать чтение указанных данных (которые могут читаться транзакцией другого соединения) уже при считывании, чтобы исключить взаимные блокировки при записи. Это предложение дает возможность указать в запросе таблицы, считываемые данные которых предполагается изменять.





Узнай больше!

Более подробно об использовании предложения ДЛЯ ИЗМЕНЕНИЯ можно прочесть на диске ИТС (информационно-технологического сопровождения) в статье "Использование предложения ДЛЯ ИЗМЕНЕНИЯ в языке запросов".

Поскольку мы с вами планируем выполнить запись регистров накопления "ОстаткиМатериалов" и "СтоимостьМатериалов", укажем таблицы этих регистров в качестве таблиц для изменения:



Перейдем на закладку "Условия" и зададим условие отбора из таблицы документа только строк проводимого документа (ссылка на него будет передана в параметр запроса "Ссылка"):



[243]

Перейдем на закладку "Псевдонимы" и зададим следующие псевдонимы полей:

·"НоменклатураВидНоменклатуры" – "ВидНоменклатуры",
·"КличествоОстаток1> – "КоличествоОстатокНаСкладе".
Нажмем "ОК" и посмотрим, какой текст запроса сформировал конструктор:

Процедура ОбработкаПроведения(Отказ, Режим)

   Запрос = Новый Запрос;

   Если Режим = РежимПроведенияДокумента.Оперативный Тогда

       Запрос.Текст =

       "ВЫБРАТЬ

       |    ОказаниеУслугиПереченьНоменклатуры.Номенклатура,

       |    ОказаниеУслугиПереченьНоменклатуры.Количество,

       |    ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры,

       |    ОказаниеУслугиПереченьНоменклатуры.Сумма,

       |    ОстаткиМатериаловОстатки.КоличествоОстаток,

       |    СтоимостьМатериаловОстатки.СтоимостьОстаток,

       |    ОстаткиМатериаловОстаткиНаСкладе.КоличествоОстаток КАК КоличествоОстатокНаСкладе



       |ИЗ

       |    Документ.ОказаниеУслуги. ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры

       |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК СтоимостьМатериаловОстатки

       |        ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = СтоимостьМатериаловОстатки.Материал

       |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК ОстаткиМатериаловОстатки

       |        ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = ОстаткиМатериаловОстатки.Материал

       |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(

       |        &МоментВремени,

       |        Материал В (&СписокНоменклатурыДокумента)

       |            И Склад = &СкладВДокументе) КАК ОстаткиМатериаловОстаткиНаСкладе

       |        ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = ОстаткиМатериаловОстаткиНаСкладе.Материал

       |ГДЕ [244]

       |    ОказаниеУслугиПереченьНоменклатуры.Ссылка = &Ссылка

       |

       |ДЛЯ ИЗМЕНЕНИЯ

       |    РегистрНакопления.СтоимостьМатериалов.Остатки,



       |    РегистрНакопления.ОстаткиМатериалов.Остатки";

   Иначе

       Запрос.Текст =

       ;

   КонецЕсли;

КонецПроцедуры

Как видите, в запросе нет ничего сложного за исключением, быть может, трех левых соединений с таблицей табличной части документа и использования ключевого предложения ДЛЯ ИЗМЕНЕНИЯ, значение которого было объяснено выше.

Текст запроса для случая неоперативного проведения документа будет практически таким же, за исключением того, что в нем будет отсутствовать третье левое соединение и, соответственно, поле "КоличествоОстатокНаСкладе", т.к. проверку остатков в этом случае мы выполнять не будем:

...

   Иначе

       Запрос.Текст =

       "ВЫБРАТЬ

       |    ОказаниеУслугиПереченьНоменклатуры.Номенклатура,

       |    ОказаниеУслугиПереченьНоменклатуры.Количество,

       |    ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры,

       |    ОказаниеУслугиПереченьНоменклатуры.Сумма,

       |    ОстаткиМатериаловОстатки.КоличествоОстаток,

       |    СтоимостьМатериаловОстатки.СтоимостьОстаток

       |ИЗ

       |    Документ.ОказаниеУслуги.ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры

       |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК СтоимостьМатериаловОстатки



       |        ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = СтоимостьМатериаловОстатки.Материал

       |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК ОстаткиМатериаловОстатки

       |        ПО ОказаниеУслугиПереченьНоменклатуры.Номенклатура = [245] ОстаткиМатериаловОстатки.Материал

       |ГДЕ

       |    ОказаниеУслугиПереченьНоменклатуры.Ссылка = &Ссылка

       |

       |ДЛЯ ИЗМЕНЕНИЯ

       |    РегистрНакопления.СтоимостьМатериалов.Остатки,

       |    РегистрНакопления.ОстаткиМатериалов.Остатки";

   КонецЕсли;

КонецПроцедуры

Теперь добавим в текст обработчика задание параметров запроса:

Процедура ОбработкаПроведения(Отказ, Режим)

   Запрос = Новый Запрос;

   Запрос.УстановитьПараметр("СкладВДокументе",Склад);

...

       |    РегистрНакопления.ОстаткиМатериалов.Остатки";

   КонецЕсли;

   Запрос.УстановитьПараметр("МоментВремени", МоментВремени());

   Запрос.УстановитьПараметр("СписокНоменклатурыДокумента", ПереченьНоменклатуры.ВыгрузитьКолонку("Номенклатура"));

   Запрос.УстановитьПараметр("Ссылка",Ссылка);

...

КонецПроцедуры

Обратите внимание, что для формирования списка номенклатуры документа мы используем метод ВыгрузитьКолонку() объекта ДокументТабличнаяЧасть.ОказаниеУслуги.ПереченьНоменклатуры.[246]

После этого добавим получение результата запроса и цикл его обхода:



...

   КонецЕсли;

   Запрос.УстановитьПараметр("МоментВремени", МоментВремени());

   Запрос.УстановитьПараметр("СписокНоменклатурыДокумента", ПереченьНоменклатуры.ВыгрузитьКолонку("Номенклатура"));

   Запрос.УстановитьПараметр("Ссылка",Ссылка);

   ВыборкаРезультатаЗапроса = Запрос.Выполнить().Выбрать();

   Пока ВыборкаРезультатаЗапроса.Следующий() Цикл

   КонецЦикла;

КонецПроцедуры

Теперь, прежде чем начать формирование движений по регистрам, нам нужно проверить наличие на складе достаточного количества номенклатуры:

...

   КонецЕсли;

   Запрос.УстановитьПараметр("МоментВремени", МоментВремени());

   Запрос.УстановитьПараметр("СписокНоменклатурыДокумента", ПереченьНоменклатуры.ВыгрузитьКолонку("Номенклатура"));

   Запрос.УстановитьПараметр("Ссылка",Ссылка);

   ВыборкаРезультатаЗапроса = Запрос.Выполнить().Выбрать();

   Пока ВыборкаРезультатаЗапроса.Следующий() Цикл

       //Проверить остаток при оперативном проведении

       Если Режим = РежимПроведенияДокумента.Оперативный Тогда

           Если ВыборкаРезультатаЗапроса.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Материал Тогда

               Остаток = ?(ВыборкаРезультатаЗапроса.КоличествоОстатокНаСкладе = Null, 0, ВыборкаРезультатаЗапроса.КоличествоОстатокНаСкладе);

               Если Остаток < ВыборкаРезультатаЗапроса.Количество Тогда

                   Сообщить("Материала " + СокрЛП(ВыборкаРезультатаЗапроса.Номенклатура) + " имеется только " + Остаток);



                   Отказ = Истина;

                   Возврат;

               КонецЕсли;

           КонецЕсли; [247]

       КонецЕсли;

   КонецЦикла;

КонецПроцедуры

И в заключение добавим формирование движений по регистрам накопления:

...

   КонецЕсли;

   Запрос.УстановитьПараметр("МоментВремени", МоментВремени());

   Запрос.УстановитьПараметр("СписокНоменклатурыДокумента", ПереченьНоменклатуры.ВыгрузитьКолонку("Номенклатура"));

   Запрос.УстановитьПараметр("Ссылка",Ссылка);

   ВыборкаРезультатаЗапроса = Запрос.Выполнить().Выбрать();

   Пока ВыборкаРезультатаЗапроса.Следующий() Цикл

       //Проверить остаток при оперативном проведении

       Если Режим = РежимПроведенияДокумента.Оперативный Тогда

           Если ВыборкаРезультатаЗапроса.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Материал Тогда

               Остаток = ?(ВыборкаРезультатаЗапроса.КоличествоОстатокНаСкладе = Null, 0, ВыборкаРезультатаЗапроса.КоличествоОстатокНаСкладе);

               Если Остаток < ВыборкаРезультатаЗапроса.Количество Тогда

                   Сообщить("Материала " + СокрЛП(ВыборкаРезультатаЗапроса.Номенклатура) + " имеется только " + Остаток);



                   Отказ = Истина;

                   Возврат;

               КонецЕсли;

           КонецЕсли;

       КонецЕсли;

       //Сформировать движения

       Если ВыборкаРезультатаЗапроса.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.Материал Тогда

           // регистр ОстаткиМатериалов Расход

           Движение = Движения.ОстаткиМатериалов.Добавить();

           Движение.ВидДвижения = ВидДвиженияНакопления.Расход;

           Движение.Период = Дата;

           Движение.Материал = ВыборкаРезультатаЗапроса.Номенклатура;

           Движение.Склад = Склад;

           Движение.Количество = ВыборкаРезультатаЗапроса.Количество;

           // регистр СтоимостьМатериалов Расход

           Движение = Движения.СтоимостьМатериалов.Добавить();

           Движение.ВидДвижения = ВидДвиженияНакопления.Расход; [248]

           Движение.Период = Дата;

           Движение.Материал = ВыборкаРезультатаЗапроса.Номенклатура;

           //расчитать стоимость материала



           СтоимостьМатериала = ?(ВыборкаРезультатаЗапроса.КоличествоОстаток = Null, 0,

               ВыборкаРезультатаЗапроса.СтоимостьОстаток / ВыборкаРезультатаЗапроса.КоличествоОстаток);

           Движение.Стоимость = СтоимостьМатериала * ВыборкаРезультатаЗапроса.Количество;

       КонецЕсли;

       // регистр Продажи

       Движение = Движения.Продажи.Добавить();

       Движение.Период = Дата;

       Движение.Номенклатура = ВыборкаРезультатаЗапроса.Номенклатура;

       Движение.Клиент = Клиент;

       Движение.Мастер = Мастер;

       Движение.Количество = ВыборкаРезультатаЗапроса.Количество;

       Движение.Выручка = ВыборкаРезультатаЗапроса.Сумма;

       Если ВыборкаРезультатаЗапроса.ВидНоменклатуры =    Перечисления.ВидыНоменклатуры.Материал Тогда

           Движение.Стоимость = СтоимостьМатериала * ВыборкаРезультатаЗапроса.Количество;

       Иначе

           Движение.Стоимость = 0;

       КонецЕсли;

   КонецЦикла;

   // записать движения регистров

   Движения.ОстаткиМатериалов.Записать();

   Движения.СтоимостьМатериалов.Записать();

   Движения.Продажи.Записать();

КонецПроцедуры

Запустим 1С:Предприятие в режиме отладки и проверим работу нового обработчика события "ОбработкаПроведения", перепроведя все документы "ОказаниеУслуги".



В заключение следует сделать небольшое отступление, которое касается задания параметров виртуальных таблиц, использовавшихся в наших запросах.[249]

Как в первом, так и во втором запросах мы использовали условие что материал должен находиться в списке значений, задаваемом одним из параметров запроса:

...

|    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьМатериалов.Остатки(&МоментВремени, Материал В (&СписокНоменклатурыДокумента)) КАК СтоимостьМатериаловОстатки

...

Однако, что при большом количестве строк табличной части документа, из которой формируется список номенклатуры документа, возможно более эффективным будет не передача номенклатуры документа в списке значений, а выполнение вложенного запроса:

...

|    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки(

|    &МоментВремени,

|    Материал В

|        (ВЫБРАТЬ РАЗЛИЧНЫЕ

|            ОказаниеУслугиПереченьНоменклатуры.Номенклатура

|        ИЗ

|            Документ.ОказаниеУслуги.ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры

|        ГДЕ

|            ОказаниеУслугиПереченьНоменклатуры.Ссылка = &Ссылка))

...

Выбор одного или другого решения требует сравнительных тестов и зависит от конкретной информационной базы, размеров документа, количества одновременно работающих пользователей, производительности компьютеров и т.д.

Теперь мы можем на некоторое время отвлечься от запросов, с которыми мы достаточно "плотно" работали в этой главе, и обратить свое внимание на не менее интересные возможности, которые предоставляет разработчику платформа 1С:Предприятие 8.0.[250]


Содержание раздела