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