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