Проанализировав имеющиеся статьи и разработки на Инфостарте, обнаружил, что ничего подобного нет, а то, что есть, фактически не позволяет выполнить поставленных задач.
Итак, вкратце опишу, что нужно было сделать:
- В момент, когда происходит заказ или реализация товара, нужно было добавить в документ дополнительно номенклатуру, которая должна быть обязательно в этом документе. То есть, например, вы продаете водку «Особенная», вы добавляете данную позицию в документ, после в документ автоматически добавляется позиция пиво «Светлое», а, как известно, водка без пива деньги на ветер.
- Для меня также было важно рассчитать количество единиц товара, которые мне нужно было добавить в документ. То есть если в расчете на 1 бутылку водки идет 3 бутылки пива, то мне при добавлении в документ 4 бутылок водки надо было добавить 12 бутылок пива.
- Следует учесть еще такой момент: допустим, у вас вся завязка на добавление связанной номенклатуры может быть не только номенклатура — номенклатура, а также группа номенклатуры — номенклатура. То есть, если брать наш пример, это будет группа товара водка, а номенклатура пиво «Светлое»
- Если в пределах одного документа нужно будет добавить одну и ту же позицию, надо будет проверить, чтобы данная позиция не была задвоена в документе и вычислить общее ее количество в заказе, возможно, ваше количество придется округлить.
- Последнее, что может потребоваться, — выполнить какие-то дополнительные проверки. Допустим, запускать наш процесс добавления номенклатуры только один раз при создании нового документа, проверять документ на соответствие определенным критериям (ответственный, проект).
Теперь давайте приступим к реализации поставленной задачи, я по пунктам распишу, что нужно для этого сделать:
1. Поскольку сам механизм добавления связанной номенклатуры в документе может быть не всегда нужен, или он нужен только определенным пользователям, поэтому для этого лучше предусмотреть возможность его включения и выключения.
Ну тут как бы есть 2 возможных решения:
- Добавить соответствующую константу типа булево и вынести ее где-то на форму для удобного редактирования
- Добавить в предопределенные элементы планов видов характеристик "Настройки пользователей" настройки пользования соответствующую настройку
2. Наверное вам нужно будет где-нибудь хранить информацию о соответствии связанной номенклатуры. Лучше всего для этого использовать регистр сведений. Также вам нужно будет определиться с периодичностью регистра, в моем случае я делал регистр непериодический, мне не нужно было мониторить во времени, когда которую номенклатуру добавлять в документ. Если вам нужно будет добавить какие-то дополнительные условия, структура регистра у вас может немного отличаться, но глобально структура будет примерно такая, как на скриншоте ниже.
Для удобной работы с самим регистром я немного модифицировал форму списка регистра, разделив ее на две части
- в первой части определяется группа номенклатуры или номенклатура, для которой будет добавляться связанная номенклатура, плюс дополнительные настройки, если они нужны, в моем случае это отбор по проекту в документе
- вторая часть это таблица со связанной номенклатурой, плюс дополнительные настройки, такие как количество, запрет удаления номенклатуры из документа
Ниже пример кода формы списка:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| МС_СвязиНоменклатуры.Номенклатура КАК Наименование,
| МС_СвязиНоменклатуры.Проект
|ИЗ
| РегистрСведений.МС_СвязиНоменклатуры КАК МС_СвязиНоменклатуры";
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
ТЗОбъект = РеквизитФормыВЗначение("Номенклатура");
ТЗОбъект = РезультатЗапроса.Выгрузить();
ЗначениеВРеквизитФормы(ТЗОбъект, "Номенклатура");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура НоменклатураПриАктивизацииСтроки(Элемент)
Если Элемент.ТекущиеДанные <> Неопределено Тогда
ПолучитьСписокСвязанойНоменклатурыСервер(Элемент.ТекущиеДанные.Наименование, Элемент.ТекущиеДанные.Проект);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура НоменклатураПередУдалением(Элемент, Отказ)
Если Элемент.ТекущиеДанные <> Неопределено Тогда
УдалитьНаборСервер(Элемент.ТекущиеДанные.Наименование);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура СписокНоменклатурыВходящейВСоставПослеУдаления(Элемент)
ПолучитьСписокСвязанойНоменклатурыСервер(Элементы.Номенклатура.ТекущиеДанные.Наименование, Элементы.Номенклатура.ТекущиеДанные.Проект);
КонецПроцедуры
&НаКлиенте
Процедура СписокНоменклатурыВходящейВСоставПередУдалением(Элемент, Отказ)
Если Элемент.ТекущиеДанные <> Неопределено Тогда
ЗаписатьВРегистр(Элементы.Номенклатура.ТекущиеДанные.Наименование,
Элемент.ТекущиеДанные.Номенклатура,
Элементы.Номенклатура.ТекущиеДанные.Проект,
Элемент.ТекущиеДанные.Количество,
Элемент.ТекущиеДанные.ЗапретУдаления,
Ложь);
КонецЕсли
КонецПроцедуры
&НаКлиенте
Процедура СписокНоменклатурыВходящейВСоставПередОкончаниемРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования, Отказ)
Если Элемент.ТекущиеДанные <> Неопределено Тогда
ЗаписатьВРегистр(Элементы.Номенклатура.ТекущиеДанные.Наименование,
Элемент.ТекущиеДанные.Номенклатура,
Элементы.Номенклатура.ТекущиеДанные.Проект,
Элемент.ТекущиеДанные.Количество,
Элемент.ТекущиеДанные.ЗапретУдаления,
Истина);
ПолучитьСписокСвязанойНоменклатурыСервер(Элементы.Номенклатура.ТекущиеДанные.Наименование, Элементы.Номенклатура.ТекущиеДанные.Проект);
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Процедура УдалитьНаборСервер(Номенклатура)
НаборЗаписей = РегистрыСведений.МС_СвязиНоменклатуры.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Номенклатура.Установить(Номенклатура);
НаборЗаписей.Очистить();
НаборЗаписей.Записать();
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ЗаписатьВРегистр(Номенклатура, ВходящаяВСостав, Проект, Количество, ЗапретУдаления, ЗаписьИлиУдаление)
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.МС_СвязиНоменклатуры");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементБлокировки.УстановитьЗначение("Номенклатура", Номенклатура);
ЭлементБлокировки.УстановитьЗначение("Проект" , Проект);
Блокировка.Заблокировать();
Запись = РегистрыСведений.МС_СвязиНоменклатуры.СоздатьМенеджерЗаписи();
Запись.Номенклатура = Номенклатура;
Запись.НоменклатураВходящаяВСостав = ВходящаяВСостав;
Запись.Проект = Проект;
Запись.Количество = Количество;
Запись.ЗапретУдаления = ЗапретУдаления;
Если ЗаписьИлиУдаление Тогда
Запись.Записать();
Иначе
Запись.Удалить();
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПолучитьСписокСвязанойНоменклатурыСервер(Номенклатура, Проект)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МС_СвязиНоменклатуры.НоменклатураВходящаяВСостав КАК Номенклатура,
| МС_СвязиНоменклатуры.Проект КАК Проект,
| МС_СвязиНоменклатуры.Количество КАК Количество,
| МС_СвязиНоменклатуры.ЗапретУдаления КАК ЗапретУдаления
|ИЗ
| РегистрСведений.МС_СвязиНоменклатуры КАК МС_СвязиНоменклатуры
|ГДЕ
| МС_СвязиНоменклатуры.Номенклатура = &Номенклатура
| И МС_СвязиНоменклатуры.Проект = &Проект";
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Запрос.УстановитьПараметр("Проект" , Проект);
РезультатЗапроса = Запрос.Выполнить();
СписокНоменклатурыВходящейВСостав.Очистить();
Если НЕ РезультатЗапроса.Пустой() Тогда
ТЗОбъект = РеквизитФормыВЗначение("СписокНоменклатурыВходящейВСостав");
ТЗОбъект = РезультатЗапроса.Выгрузить();
ЗначениеВРеквизитФормы(ТЗОбъект, "СписокНоменклатурыВходящейВСостав");
КонецЕсли;
КонецПроцедуры
3. Сама процедура добавления связанной номенклатуры в документ была вынесена в общий модуль, где в запрос передаются данные документа и выполняются при этом манипуляции, связанные с добавлением номенклатуры и установлением дополнительных условий.
// Процедура выполняет добавление связанной номенклатури в документы
//
Процедура ДобавитьСвязануюНоменклатуру(Объект) Экспорт
// Процедура добавления связаной номенклатуры
// код присутствует
// в общем модуле: "МС_ОбщийМодуль"
// в обработке : "Рабочее место менеджера по продажам"
// в документе : "Заказ покупателя"
// в документе : "Коммерческое предложение"
// в документе : "Счет на оплату покупателю"
Если Константы.МС_ВключатьВЗаказПокупателяСвязаннуюНоменклатуру.Получить() И Объект.ЭтоНовый() Тогда
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТабЧастьНоменклатура.Номенклатура,
| ТабЧастьНоменклатура.Родитель1 КАК Родитель1,
| ТабЧастьНоменклатура.Родитель2 КАК Родитель2,
| ТабЧастьНоменклатура.РОдитель3 КАК Родитель3,
| ТабЧастьНоменклатура.Родитель4 КАК Родитель4,
| ТабЧастьНоменклатура.Родитель5 КАК Родитель5,
| ТабЧастьНоменклатура.Родитель6 КАК Родитель6,
| ТабЧастьНоменклатура.Родитель7 КАК Родитель7,
| ТабЧастьНоменклатура.Родитель8 КАК Родитель8,
| ТабЧастьНоменклатура.Родитель9 КАК Родитель9,
| ТабЧастьНоменклатура.Родитель10 КАК Родитель10,
| ТабЧастьНоменклатура.Количество
|ПОМЕСТИТЬ СписокНоменклатура
|ИЗ
| &Товары КАК ТабЧастьНоменклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СписокНоменклатура.Номенклатура КАК Номенклатура_Табличная,
| СписокНоменклатура.Количество КАК Количество_Табличное,
| МС_СвязиНоменклатуры.НоменклатураВходящаяВСостав КАК Номенклатура,
| МС_СвязиНоменклатуры.Количество,
| МС_СвязиНоменклатуры.ЗапретУдаления,
| МС_СвязиНоменклатуры.НоменклатураВходящаяВСостав.ЕдиницаХраненияОстатков КАК ЕдиницаХраненияОстатков,
| МС_СвязиНоменклатуры.НоменклатураВходящаяВСостав.ЕдиницаХраненияОстатков.Коэффициент КАК Коэффициент
|ИЗ
| СписокНоменклатура КАК СписокНоменклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.МС_СвязиНоменклатуры КАК МС_СвязиНоменклатуры
| ПО (ВЫБОР
| КОГДА СписокНоменклатура.Номенклатура = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Номенклатура = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель1 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель1 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель2 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель2 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель3 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель3 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель4 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель4 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель5 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель5 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель6 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель6 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель7 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель7 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель8 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель8 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель9 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель9 = МС_СвязиНоменклатуры.Номенклатура
| КОГДА СписокНоменклатура.Родитель10 = МС_СвязиНоменклатуры.Номенклатура
| ТОГДА СписокНоменклатура.Родитель10 = МС_СвязиНоменклатуры.Номенклатура
| КОНЕЦ)
|ГДЕ
| (МС_СвязиНоменклатуры.Проект = &Проект
| ИЛИ МС_СвязиНоменклатуры.Проект = ЗНАЧЕНИЕ(Справочник.Проекты.ПустаяСсылка))";
ТабЧастьНоменклатура = Объект.Товары.Выгрузить();
Копия_Товары = НС_ОбщийМодуль.ДобавитьДоИсходнойТаблицыКолонкиСродителями(ТабЧастьНоменклатура);
Запрос.УстановитьПараметр("Товары", Копия_Товары);
Запрос.УстановитьПараметр("Проект", ?(Объект.Метаданные().Реквизиты.Найти("МС_Проект") = Неопределено, Справочники.Проекты.ПустаяСсылка(), Объект.МС_Проект));
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Если РезультатЗапроса.Количество() <> 0 Тогда
Для каждого стр Из РезультатЗапроса Цикл
Строка_Есть = Объект.Товары.Найти(стр.Номенклатура);
Если Строка_Есть = Неопределено Тогда
Строка = Объект.Товары.Добавить();
Строка.Номенклатура = стр.Номенклатура;
Строка.ЕдиницаИзмерения = стр.ЕдиницаХраненияОстатков;
Строка.Коэффициент = стр.Коэффициент;
Строка.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС;
// Установим нужное количество добавляемой номенклатуры
Если стр.Количество <> 0 Тогда
Строка.Количество = окр(стр.Количество_Табличное / стр.Количество, 0)
КонецЕсли;
// Установим запрет на удаление строки, если такая колонка есть в таблице
Попытка
ЕстьВТаблице = Строка["МС_ЗапретУдаления"];
Строка.МС_ЗапретУдаления = стр.ЗапретУдаления;
Исключение
//Сообщить("Нет колонки запрет на удаление");
КонецПопытки;
// Заполняем реквизиты табличной части.
Если ТипЗнч(Объект.Ссылка) = Тип("ДокументСсылка.СчетНаОплатуПокупателю") Тогда
ОбработкаТабличныхЧастей.ЗаполнитьСтавкуНДСТабЧасти(Строка, Объект, "Реализация");
ОбработкаТабличныхЧастей.ЗаполнитьЕдиницуЦенуПродажиТабЧасти(Строка, Объект, Объект.ВалютаДокумента);
Иначе
Объект.ПриИзмененииНоменклатурыТоваров(Строка);
КонецЕсли;
Иначе
Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", стр.Номенклатура);
Копия = РезультатЗапроса.Скопировать(Отбор);
ОбщееКоличество = РезультатЗапроса.Итог("Количество_Табличное");
// Установим нужное количество добавляемой номенклатуры, если такая уже есть в таблице товаров
Если стр.Количество <> 0 Тогда
Строка_Есть.Количество = окр(ОбщееКоличество / стр.Количество, 0);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
4. В общем, все, что остается сделать, это при окончании редактирования табличной части выполнить данную процедуру
// Процедура - обработчик события "ПриОкончанииРедактирования" табличной части "Товары".
//
Процедура ТоварыПриОкончанииРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования)
МС_ОбщийМодуль.ДобавитьСвязануюНоменклатуру(ЭтотОбъект);
КонецПроцедуры // ТоварыПриОкончанииРедактирования()
Дополнительные сведения
- Все изменения проводились в конфигурации "Управление торговым предприятием для Украины", редакция 1.2. (1.2.48.1), данный механизм можно реализовать и других конфигурациях, в которых присутствуют справочник номенклатура, документы реализация товаров и услуг, заказ покупателя и т.д.
- Сам запрос получения связанной номенклатуры можно немного проапгрейдить, также в запросе учитывается вложенность справочника номенклатуры до 10 уровней
- Также в коде упоминается процедура "ДобавитьДоИсходнойТаблицыКолонкиСродителями", эта процедура нужна для того, чтобы дополнить табличную часть товары из документа колонками родителей номенклатуры.
А чем вас не устроили «наборы» ?
Спасибо уважаемый. Нужная вещь. чуть было не пришлось самостоятельно разрабатывать.
(1) Если ви имели ввиду наборы номенклатуры — комплекты, то это не совсем подходит под поставленные задачи, плюс там нет возможности включения дополнительних условий отборов и т.д.