Связанная номенклатура







Механизм добавления связанной номенклатуры.

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

Итак, вкратце опишу, что нужно было сделать:

  • В момент, когда происходит заказ или реализация товара, нужно было добавить в документ дополнительно номенклатуру, которая должна быть обязательно в этом документе. То есть, например, вы продаете водку «Особенная», вы добавляете данную позицию в документ, после в документ автоматически добавляется позиция пиво «Светлое», а, как известно, водка без пива деньги на ветер.
  • Для меня также было важно рассчитать количество единиц товара, которые мне нужно было добавить в документ. То есть если в расчете на 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. Все изменения проводились в конфигурации "Управление торговым предприятием для Украины", редакция 1.2. (1.2.48.1), данный механизм можно реализовать и других конфигурациях, в которых присутствуют справочник номенклатура, документы реализация товаров и услуг, заказ покупателя и т.д.
  2. Сам запрос получения связанной номенклатуры можно немного проапгрейдить, также в запросе учитывается вложенность справочника номенклатуры до 10 уровней
  3. Также в коде упоминается процедура "ДобавитьДоИсходнойТаблицыКолонкиСродителями", эта процедура нужна для того, чтобы дополнить табличную часть товары из документа колонками родителей номенклатуры.

3 Comments

  1. simuljakr

    А чем вас не устроили «наборы» ?

    Reply
  2. almas

    Спасибо уважаемый. Нужная вещь. чуть было не пришлось самостоятельно разрабатывать.

    Reply
  3. Mellow

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

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *