644 документа
← К списку

В режиме "Конфигуратор"

страница

В режиме «Конфигуратор»

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

Для этого откроем в конфигураторе окно редактирования объекта конфигурации Документ ОказаниеУслуги, перейдем на закладку Данные, раскроем список реквизитов табличной части документа, выделим реквизит Стоимость и нажмем кнопку Удалить в командной панели (рис. 14.13).

Рис. 14.13. Удаление реквизита табличной части

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

Для этого откроем форму ФормаДокумента документа ОказаниеУслуги и в окне структуры элементов формы выделим поле таблицы ПереченьНоменклатурыСтоимость и нажмем кнопку Удалить в командной панели (рис. 14.14).

Рис. 14.14. Удаление поля табличной части

Теперь займемся запросом.

Временную таблицу мы сформируем с помощью того запроса, который у нас уже написан.

Откроем модуль документа ОказаниеУслуги.

В процедуре ОбработкаПроведения() перед созданием запроса создадим менеджер временных таблиц и укажем, что этот запрос будет использовать созданный менеджер временных таблиц (листинг 14.17).

Листинг 14.17. Использование менеджера временных таблиц

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

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

Движения.Продажи.Записывать = Истина;

 

// Создать менеджер временных таблиц

МенеджерВТ = Новый МенеджерВременныхТаблиц;

 

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

 

// Укажем, какой менеджер временных таблиц использует этот запрос

Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

 

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

"ВЫБРАТЬ

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

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

Чтобы конструктор запроса смог открыть наш запрос, удалим из него строку (поля Стоимость у нас больше нет), листинг 14.18.

Листинг 14.18. Изменение запроса

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

Также удалим запятую в конце предыдущей строки (листинг 14.19).

Листинг 14.19. Изменение запроса

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

Теперь установим курсор внутрь текста запроса, например на слове ВЫБРАТЬ, и выполним команду контекстного меню Конструктор запроса. Существующий текст запроса будет показан в форме конструктора запросов (рис. 14.15).

Рис. 14.15. Конструктор запроса

Чтобы результат запроса поместить во временную таблицу, перейдем на закладку Дополнительно и отметим пункт Создание временной таблицы.

Зададим имя временной таблицы — НоменклатураДокумента (рис. 14.16).

Рис. 14.16. Создание временной таблицы

Нажмем ОК и посмотрим, какой текст сформировал конструктор запроса (листинг 14.20).

Листинг 14.20. Текст запроса

"ВЫБРАТЬ

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

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

| СУММА(ОказаниеУслугиПереченьНоменклатуры.Количество) КАК КоличествоВДокументе,

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

|ПОМЕСТИТЬ НоменклатураДокумента

|ИЗ

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

|ГДЕ

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

|СГРУППИРОВАТЬ ПО

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

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

Новым здесь является только строка (листинг 14.21).

Листинг 14.21. Создание временной таблицы

|ПОМЕСТИТЬ НоменклатураДокумента

Это означает, что результат запроса будет сохранен во временной таблице НоменклатураДокумента.

Теперь если мы для другого запроса укажем этот же самый менеджер временных таблиц МенеджерВТ, то в этом другом запросе мы сможем обратиться к данным этой временной таблицы.

Таким образом, мы выполнили первую часть нашего плана — создали запрос, помещающий данные табличной части документа во временную таблицу (рис. 14.17).

Рис. 14.17. Создание первого запроса

Теперь займемся конструированием второго запроса.

Установим курсор на следующую строку после оператора РезультатЗапроса = Запрос.Выполнить(); (именно здесь выполняется создание временной таблицы) и напишем заготовку будущего запроса (листинг 14.22).

Листинг 14.22. Создание второго запроса

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

Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;

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

Мы создали новый объект Запрос и назначили ему тот же самый менеджер временных таблиц, чтобы иметь возможность обращаться к созданной нами ранее временной таблице.

Теперь установим курсор внутрь кавычек и выполним команду контекстного меню Конструктор запроса. Согласимся на создание нового запроса.

Поскольку мы собираемся выбирать данные из нашей временной таблицы, создадим в запросе описание этой временной таблицы. Для этого над списком Таблицы нажмем кнопку Создать описание временной таблицы (рис. 14.18).

Рис. 14.18. Создание описания временной таблицы

В открывшемся окне введем имя нашей временной таблицы НоменклатураДокумента и добавим описание полей:

  • Номенклатура, тип СправочникСсылка.Номенклатура;
  • ВидНоменклатуры, тип ПеречислениеСсылка.ВидыНоменклатуры;
  • КоличествоВДокументе, тип Число, 15, 3;
  • СуммаВДокументе, тип Число, 15, 2.

Нажмем ОК.

В результате у нас получится следующее описание временной таблицы (рис. 14.19).

Рис. 14.19. Создание описания временной таблицы

Нажмем ОК. Выберем из этой таблицы все поля (рис. 14.20) и нажмем кнопку Запрос в левом нижнем углу окна конструктора запроса.

Рис. 14.20. Выбранные поля временной таблицы

Текст запроса будет иметь вид (листинг 14.23).

Листинг 14.23. Текст второго запроса

ВЫБРАТЬ

НоменклатураДокумента.Номенклатура КАК Номенклатура,

НоменклатураДокумента.ВидНоменклатуры КАК ВидНоменклатуры,

НоменклатураДокумента.КоличествоВДокументе КАК КоличествоВДокументе,

НоменклатураДокумента.СуммаВДокументе КАК СуммаВДокументе

ИЗ

НоменклатураДокумента КАК НоменклатураДокумента

Итак, мы создали первую часть второго запроса — выбрали информацию из временной таблицы (рис. 14.21).

Рис. 14.21. Создание второго запроса

Теперь будем соединять эту конструкцию левыми соединениями с таблицами остатков.

Начнем со стоимости материалов.

Добавим в список таблиц запроса виртуальную таблицу РегистрНакопления.СтоимостьМатериалов.Остатки. Из нее выберем поле СтоимостьОстаток. Перейдем на закладку Связи и зададим связь между таблицами.

Из временной таблицы будем выбирать все записи, и поле Номенклатура временной таблицы должно быть равно полю Материал таблицы остатков (рис. 14.22).

Рис. 14.22. Связи между таблицами

Нужно не забыть ограничить виртуальную таблицу только той номенклатурой, которая есть в нашей временной таблице.

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

Зададим параметр Условие следующим образом (листинг 14.24).

Листинг 14.24. Условие виртуальной таблицы

Материал В (ВЫБРАТЬ НоменклатураДокумента.Номенклатура ИЗ НоменклатураДокумента)

То есть материал должен быть среди номенклатуры, выбранной из временной таблицы.

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

Следует внимательно подходить к использованию виртуальных таблиц запросов. В частности, необходимо уделять особое внимание максимально возможному использованию параметров этих таблиц.

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

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

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

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

Нажмем кнопку Запрос и посмотрим, какой текст запроса сформировал конструктор (листинг 14.25).

Листинг 14.25. Текст запроса

ВЫБРАТЬ

НоменклатураДокумента.Номенклатура КАК Номенклатура,

НоменклатураДокумента.ВидНоменклатуры КАК ВидНоменклатуры,

НоменклатураДокумента.КоличествоВДокументе КАК КоличествоВДокументе,

НоменклатураДокумента.СуммаВДокументе КАК СуммаВДокументе,

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

ИЗ

НоменклатураДокумента КАК НоменклатураДокумента

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

(ВЫБРАТЬ

НоменклатураДокумента.Номенклатура

ИЗ

НоменклатураДокумента)) КАК СтоимостьМатериаловОстатки

ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.Материал

Тем самым мы добавили к выбранным ранее полям стоимость номенклатуры (рис. 14.23).

Рис. 14.23. Создание второго запроса

Теперь добавим виртуальную таблицу остатков регистра ОстаткиМатериалов.Остатки, из которой выберем поле КоличествоОстаток.

Перейдем на закладку Связи и зададим связь между таблицами.

Из временной таблицы будем выбирать все записи, и поле Номенклатура временной таблицы должно быть равно полю Материал таблицы остатков (рис. 14.24).

Рис. 14.24. Связи между таблицами

Также зададим параметры виртуальной таблицы ОстаткиМатериаловОстатки.

В параметр Условие внесем следующий текст (листинг 14.26).

Листинг 14.26. Условие виртуальной таблицы

Материал В (ВЫБРАТЬ НоменклатураДокумента.Номенклатура ИЗ НоменклатураДокумента)

В результате мы получим следующий текст запроса (листинг 14.27).

Листинг 14.27. Текст запроса

ВЫБРАТЬ

НоменклатураДокумента.Номенклатура КАК Номенклатура,

НоменклатураДокумента.ВидНоменклатуры КАК ВидНоменклатуры,

НоменклатураДокумента.КоличествоВДокументе КАК КоличествоВДокументе,

НоменклатураДокумента.СуммаВДокументе КАК СуммаВДокументе,

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

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

ИЗ

НоменклатураДокумента КАК НоменклатураДокумента

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

ВЫБРАТЬ

НоменклатураДокумента.Номенклатура

ИЗ

НоменклатураДокумента)) КАК СтоимостьМатериаловОстатки

ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.Материал

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

ВЫБРАТЬ

НоменклатураДокумента.Номенклатура

ИЗ

НоменклатураДокумента)) КАК ОстаткиМатериаловОстатки

ПО НоменклатураДокумента.Номенклатура = ОстаткиМатериаловОстатки.Материал

Тем самым мы добавили к выбранным ранее полям остатки номенклатуры на всех складах (рис. 14.25).

Рис. 14.25. Создание второго запроса

В заключение перейдем на закладку Объединения/Псевдонимы и зададим следующие псевдонимы полей (рис. 14.26):

  • СтоимостьОстаток — Стоимость;
  • КоличествоОстаток — Количество.

Рис. 14.26. Псевдонимы полей

В результате мы получим следующий текст запроса (листинг 14.28).

Листинг 14.28. Текст запроса

ВЫБРАТЬ

НоменклатураДокумента.Номенклатура КАК Номенклатура,

НоменклатураДокумента.ВидНоменклатуры КАК ВидНоменклатуры,

НоменклатураДокумента.КоличествоВДокументе КАК КоличествоВДокументе,

НоменклатураДокумента.СуммаВДокументе КАК СуммаВДокументе,

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

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

ИЗ

НоменклатураДокумента КАК НоменклатураДокумента

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

ВЫБРАТЬ

НоменклатураДокумента.Номенклатура

ИЗ

НоменклатураДокумента)) КАК СтоимостьМатериаловОстатки

ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.Материал

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

ВЫБРАТЬ

НоменклатураДокумента.Номенклатура

ИЗ

НоменклатураДокумента)) КАК ОстаткиМатериаловОстатки

ПО НоменклатураДокумента.Номенклатура = ОстаткиМатериаловОстатки.Материал

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

В такой ситуации левые соединения с виртуальными таблицами не вернут ничего. На языке запросов это значит, что в полях Стоимость и Количество будут значения NULL.

Чтобы в дальнейшем нам было удобно работать с результатом нашего запроса, сразу же в самом запросе избавимся от этих значений.

Для этого мы применим функцию ЕСТЬNULL() к полям Стоимость и Количество. Если значение этого поля будет NULL, функция вернет 0. В остальных случаях функция вернет само значение этого поля.

Перейдем на закладку Таблицы и поля, выделим поле СтоимостьМатериаловОстатки.СтоимостьОстаток и нажмем кнопку Изменить текущий элемент (рис. 14.27).

Рис. 14.27. Изменение значения поля в запросе

В открывшемся окне отредактируем значение поля следующим образом (листинг 14.29), рис. 14.28.

Листинг 14.29. Выражение для расчета поля в запросе

ЕСТЬNULL(СтоимостьМатериаловОстатки.СтоимостьОстаток, 0)

Рис. 14.28. Изменение значения поля в запросе

Аналогично поступим и с другим полем: ОстаткиМатериаловОстатки.КоличествоОстаток.

Нажмем ОК — текст запроса будет вставлен в модуль (листинг 14.30).

Листинг 14.30. Текст запроса

Запрос2.Текст = "ВЫБРАТЬ

| НоменклатураДокумента.Номенклатура КАК Номенклатура,

| НоменклатураДокумента.ВидНоменклатуры КАК ВидНоменклатуры,

| НоменклатураДокумента.КоличествоВДокументе КАК КоличествоВДокументе,

| НоменклатураДокумента.СуммаВДокументе КАК СуммаВДокументе,

| ЕСТЬNULL(СтоимостьМатериаловОстатки.СтоимостьОстаток, 0) КАК Стоимость,

| ЕСТЬNULL(ОстаткиМатериаловОстатки.КоличествоОстаток, 0) КАК Количество

|ИЗ

| НоменклатураДокумента КАК НоменклатураДокумента

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

| ВЫБРАТЬ

| НоменклатураДокумента.Номенклатура

| ИЗ

| НоменклатураДокумента)) КАК СтоимостьМатериаловОстатки

| ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.Материал

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

| ВЫБРАТЬ

| НоменклатураДокумента.Номенклатура

| ИЗ

| НоменклатураДокумента)) КАК ОстаткиМатериаловОстатки

| ПО НоменклатураДокумента.Номенклатура = ОстаткиМатериаловОстатки.Материал";

Нам останется всего лишь дописать после него оператор выполнения запроса (листинг 14.31).

Листинг 14.31. Фрагмент процедуры «ОбработкаПроведения()»

…

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

Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;

Запрос2.Текст = "ВЫБРАТЬ

| НоменклатураДокумента.Номенклатура,

…

| ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.Материал";

 

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

 

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

…

Теперь разберемся с записью движений.

Все операторы, которые были написаны нами ранее, будут работать без изменений.

Единственное, что потребуется изменить, — это способ получения стоимости. Раньше мы просто брали ее из документа, теперь же нам нужно ее рассчитать на основании тех данных, которые мы получили запросом.

Стоимость материала равна частному от деления всей стоимости, полученной запросом (Стоимость), на общее количество материала на всех складах (Количество).

Но, как мы уже сказали, возможна ситуация, когда поле Количество у нас будет равно 0, а на 0 делить нельзя. Поэтому сразу после начала цикла обхода результата запроса рассчитаем стоимость для текущей номенклатуры (листинг 14.32).

Листинг 14.32. Фрагмент процедуры «ОбработкаПроведения()»

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

Если ВыборкаДетальныеЗаписи.Количество = 0 Тогда

СтоимостьМатериала = 0;

Иначе

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

КонецЕсли;

 

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

…

Теперь заменим расчет стоимости в движениях регистров СтоимостьМатериалов и Продажи (листинг 14.33).

Листинг 14.33. Фрагмент процедуры «ОбработкаПроведения()»

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

Если ВыборкаДетальныеЗаписи.Количество = 0 Тогда

СтоимостьМатериала = 0;

 

Иначе

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

 

КонецЕсли;

 

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

 

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

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

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

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

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

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

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

 

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

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

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

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

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

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

КонецЕсли;

 

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

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

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

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

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

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

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

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

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

 

КонецЦикла;

Теперь все вроде бы хорошо, но остался один важный момент.

Если проводить этот документ в первый раз, то результат получится правильный.

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

Дело в том, что когда мы находимся в обработчике проведения документа и этот документ был уже проведен ранее, то в базе данных существуют движения этого документа.

Таким образом, читая из базы данных стоимость и остатки материалов, мы прочитаем их с учетом тех движений, которые документ выполнил ранее. А это неправильно.

Чтобы в обработчике проведения документа прочитать данные базы данных без учета предыдущих движений, которые мог выполнять документ, нужно перед чтением записать пустые наборы записей в те регистры, из которых мы собираемся читать.

В нашем случае такими регистрами являются регистры накопления СтоимостьМатериалов и ОстаткиМатериалов.

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

Листинг 14.33а. Фрагмент процедуры «ОбработкаПроведения()»

…

| ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.Материал";

// Запишем пустые наборы записей, чтобы читать остатки без учета данных в документе

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

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

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