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