В практике автоматизации на платформе 1С часто возникает задача, когда при выборе определенного товара в документе необходимо автоматически добавить сопутствующую услугу или материал. Типичный пример: при продаже товара "А" в количестве N единиц, должна автоматически добавиться услуга "Б", количество которой рассчитывается как N * 1.3. Рассмотрим, как реализовать такую логику в конфигурации 1С:Бухгалтерия предприятия 3.0, сохранив при этом возможность легкого обновления программы.
Для того чтобы система знала, какую номенклатуру добавлять и какой коэффициент использовать, нам нужно где-то хранить эти данные. Поскольку наша конфигурация находится на поддержке, мы не будем изменять структуру метаданных напрямую. Вместо этого воспользуемся типовым механизмом Дополнительных сведений.
Разберем, как это настроить:
СправочникСсылка.Номенклатура).Число, точность 3 знака).Теперь в карточке номенклатуры "А" на вкладке "Дополнительные сведения" мы сможем указать, что для нее связана номенклатура "Б" и задать коэффициент (например, 1.3). Это позволит гибко настраивать зависимости без изменения программного кода при добавлении новых пар товаров.
Чтобы код работал в типовой конфигурации, создадим Расширение. Это наиболее правильный метод, так как он позволяет перехватывать события формы, не снимая базу с поддержки. Нам необходимо найти форму документа (например, Реализация товаров услуг) и создать обработчик события для табличной части Товары.
Проанализируем, какое событие нам подходит. Оптимальной точкой входа является НоменклатураПриИзменении. Именно в этот момент пользователь закончил выбор основного товара, и мы можем программно добавить строку со связанной услугой.
Разберем по шагам пример кода, который выполняет поиск дополнительных сведений и добавляет новую строку в табличную часть. Нам потребуется написать запрос к регистру сведений ДополнительныеСведения.
&НаКлиенте
Процедура Расш1_ТоварыНоменклатураПриИзмененииПосле(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
// Получаем связанные данные с сервера
ДанныеСвязи = ПолучитьДанныеСвязаннойНоменклатуры(ТекущаяСтрока.Номенклатура);
Если ДанныеСвязи.Найдено Тогда
// Проверяем, нет ли уже этой номенклатуры в табличной части, чтобы избежать дублей
ПараметрыПоиска = Новый Структура("Номенклатура", ДанныеСвязи.НоменклатураБ);
НайденныеСтроки = Объект.Товары.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() = 0 Тогда
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Номенклатура = ДанныеСвязи.НоменклатураБ;
НоваяСтрока.Количество = ТекущаяСтрока.Количество * ДанныеСвязи.Коэффициент;
// Вызываем типовые механизмы пересчета цен и сумм
ОбработатьИзменениеНоменклатурыНаСервере(НоваяСтрока.НомерСтроки);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
На стороне сервера нам нужно эффективно вытянуть значения дополнительных сведений. Рассмотрим пример функции:
&НаСервереБезКонтекста
Функция ПолучитьДанныеСвязаннойНоменклатуры(НоменклатураА)
Результат = Новый Структура("Найдено, НоменклатураБ, Коэффициент", Ложь, Неопределено, 0);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДополнительныеСведения.Значение КАК Значение,
| ДополнительныеСведения.Свойство КАК Свойство
|ИЗ
| РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения
|ГДЕ
| ДополнительныеСведения.Объект = &НоменклатураА
| И (ДополнительныеСведения.Свойство.Наименование = ""СвязаннаяНоменклатура""
| ИЛИ ДополнительныеСведения.Свойство.Наименование = ""КоэффициентДляРасчета"")";
Запрос.УстановитьПараметр("НоменклатураА", НоменклатураА);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.Свойство.Наименование = "СвязаннаяНоменклатура" Тогда
Результат.НоменклатураБ = Выборка.Значение;
Результат.Найдено = Истина;
ИначеЕсли Выборка.Свойство.Наименование = "КоэффициентДляРасчета" Тогда
Результат.Коэффициент = Выборка.Значение;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Важный момент, который мы должны проанализировать — это зацикливание. Если для номенклатуры "А" задана связь с "Б", а для "Б" случайно установлена обратная связь с "А", система может начать бесконечно добавлять строки, пока документ не "взорвется" или программа не зависнет.
Для предотвращения этой ситуации мы применили проверку Объект.Товары.НайтиСтроки(). Если номенклатура "Б" уже присутствует в табличной части, повторное добавление не производится. Также стоит рассмотреть ситуацию, когда пользователь меняет количество у основного товара "А" спустя время. В этом случае количество в строке "Б" само не изменится. Чтобы решить эту проблему, необходимо добавить код в событие КоличествоПриИзменении, который будет находить связанную строку и обновлять ее значение.
Выясним причину возможных расхождений в учете. При использовании коэффициентов (например, 1.3) количество может стать дробным. В типовой 1С:Бухгалтерии 3.0 точность поля Количество составляет 3 знака после запятой. Если расчет дает результат с большим количеством знаков, платформа округлит его автоматически, но лучше управлять этим процессом явно, используя функцию Окр(), чтобы избежать проблем при проведении документа и расчете себестоимости.
Подытожим: использование дополнительных сведений в сочетании с расширением — это наиболее гибкий и безопасный способ реализации данной задачи. Он позволяет сохранить конфигурацию на поддержке и дает администратору возможность менять связи товаров без привлечения программиста.