В практике разработки на платформе 1С программисты часто сталкиваются с требованиями пользователей перенести логику сложных таблиц Excel непосредственно в интерфейс информационной системы. Типичный сценарий: отчет выводит множество колонок, рассчитанных по сложным запросам и алгоритмам, но одну или несколько колонок пользователь должен иметь возможность заполнять или корректировать вручную. При этом система должна мгновенно пересчитывать итоговые показатели строки "на лету", а при повторном формировании отчета введенные пользователем данные не должны бесследно исчезать.
В этой статье мы подробно разберем, как решить эту задачу на платформе 1С для обычных форм. Мы выясним причину распространенных ошибок при попытке редактирования ячеек табличного документа, научимся работать с событиями интерактивного изменения данных и рассмотрим два надежных архитектурных подхода к сохранению пользовательского ввода.
При попытке обработать изменение ячеек в процедуре ТабДокПриИзмененииСодержимогоОбласти разработчики часто сталкиваются с тем, что отладчик выдает ошибку, а свойства ячеек оказываются недоступными. Проанализируем ситуацию. По умолчанию ячейки табличного документа предназначены исключительно для вывода текстовой информации. Свойство СодержитЗначение для них установлено в значение Ложь. В таком режиме любая попытка прочитать или записать свойство Значение ячейки вызывает системное исключение.
Чтобы превратить обычную ячейку табличного документа в интерактивное поле ввода, необходимо правильно настроить свойства ячейки в макете отчета или сделать это программно при выводе строки. Выполним следующие настройки:
СодержитЗначение в Истина. Это ключевой шаг, который сообщает платформе, что ячейка теперь является типизированным полем ввода.ТипЗначения. Нам необходимо явно указать тип данных (например, число с нужной точностью и неотрицательным знаком), чтобы пользователь не мог ввести некорректные символы.Защита для редактируемой колонки (в нашем примере это «Товар в пути») должно быть установлено в Ложь.Защита = Истина. Это убережет отчет от случайного повреждения формул и запросных данных пользователем.ТабличныйДокумент, отображаемый на форме, по умолчанию имеет свойство ТолькоПросмотр = Истина. Для реализации нашей задачи мы обязаны программно или в свойствах элемента формы установить ТолькоПросмотр = Ложь. Не беспокойтесь о сохранности остальных ячеек: благодаря точечной настройке свойства Защита в самом макете, пользователь сможет кликнуть и изменить данные только в разрешенной колонке.Рассмотрим, как выполнить эти настройки программно в цикле вывода строк отчета, чтобы не перегружать макет жесткими ограничениями:
// Пример программной настройки ячейки при выводе строки
ОбластьТоварВПути = ДанныеСтроки.Область("R" + НомерСтроки + "C10"); // Предположим, 10-я колонка
ОбластьТоварВПути.СодержитЗначение = Истина;
ОбластьТоварВПути.ТипЗначения = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3, ДопустимыйЗнак.Неотрицательный));
ОбластьТоварВПути.Защита = Ложь;
Для отслеживания действий пользователя мы задействуем стандартное событие табличного документа формы ПриИзмененииСодержимогоОбласти. Разберем по шагам алгоритм работы этого обработчика. Нам нужно определить координаты измененной ячейки, проверить, относится ли она к нашей редактируемой колонке «Товар в пути», считать введенное число, получить остальные слагаемые из этой же строки и перезаписать итоговое значение «Остаток на конец».
Посмотрим на пример практической реализации данного обработчика:
Процедура ТабДокПриИзмененииСодержимогоОбласти(Элемент, Область)
// Определяем координаты измененной ячейки
СтрокаИзменения = Область.Верх;
КолонкаИзменения = Область.Лево;
// Предположим, что колонка "Товар в пути" имеет индекс 10
НомерКолонкиТоварВПути = 10;
Если КолонкаИзменения = НомерКолонкиТоварВПути Тогда
// Получаем введенное пользователем значение
ВведенноеЗначение = Область.Значение;
Если ВведенноеЗначение = Неопределено Тогда
ВведенноеЗначение = 0;
КонецЕсли;
// Определяем номера колонок для расчета
// Формула: ОстатокНаКонец = (ОстатокНаНачало + Приход + ТоварВПути) - Продажа
КолонкаОстатокНаНачало = 5;
КолонкаПриход = 6;
КолонкаПродажа = 8;
КолонкаОстатокНаКонец = 12;
// Считываем значения из других ячеек этой же строки
ОстатокНаНачало = Число(Элемент.Область(СтрокаИзменения, КолонкаОстатокНаНачало).Текст);
Приход = Число(Элемент.Область(СтрокаИзменения, КолонкаПриход).Текст);
Продажа = Число(Элемент.Область(СтрокаИзменения, КолонкаПродажа).Текст);
// Выполняем расчет по формуле
НовыйОстатокНаКонец = (ОстатокНаНачало + Приход + ВведенноеЗначение) - Продажа;
// Записываем новое расчетное значение в ячейку "Остаток на конец"
ЯчейкаИтога = Элемент.Область(СтрокаИзменения, КолонкаОстатокНаКонец);
ЯчейкаИтога.Текст = Формат(НовыйОстатокНаКонец, "ЧДЦ=3");
// Сохраняем введенное значение во временном буфере формы,
// чтобы оно не потерялось при переформировании
СохранитьВведенноеЗначениеВБуфер(СтрокаИзменения, ВведенноеЗначение, Элемент);
КонецЕсли;
КонецПроцедуры
Основная сложность, о которой предупреждали участники обсуждения на форуме, заключается в том, что при повторном нажатии пользователем кнопки «Сформировать» табличный документ полностью очищается, запрос выполняется заново, и все введенные вручную данные будут утеряны. Нам необходимо спроектировать механизм сохранения этих корректировок. Рассмотрим два эффективных архитектурных варианта решения этой проблемы.
Этот вариант идеально подходит, если пользователю необходимо оперативно скорректировать цифры «здесь и сейчас» для быстрой выгрузки или печати отчета руководству, и эти изменения не требуется постоянно хранить в информационной базе.
Разберем по шагам логику этого подхода:
ВведенныеКорректировки с типом ТаблицаЗначений. Добавим в эту таблицу две колонки: КлючСтроки (тип может быть ссылкой на справочник Номенклатура или любым другим уникальным идентификатором строки) и Количество (тип Число).Расшифровка. Это позволит нам в любой момент однозначно сопоставить ячейку таблицы с конкретным товаром.ВведенныеКорректировки.ВведенныеКорректировки. Если запись найдена, мы подставляем сохраненное число в колонку «Товар в пути» и сразу же программно рассчитываем итоговый остаток строки.Рассмотрим пример кода для реализации этого варианта:
// Процедура записи измененных данных в таблицу на форме
Процедура СохранитьВведенноеЗначениеВБуфер(НомерСтроки, Значение, ТабДок)
// Получаем ключ строки из расшифровки измененной ячейки
КлючСтроки = ТабДок.Область(НомерСтроки, 10).Расшифровка;
Если ЗначениеЗаполнено(КлючСтроки) Тогда
// Ищем строку в нашей таблице хранения
ПараметрыПоиска = Новый Структура("КлючСтроки", КлючСтроки);
НайденныеСтроки = ВведенныеКорректировки.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
СтрокаБуфера = НайденныеСтроки[0];
Иначе
СтрокаБуфера = ВведенныеКорректировки.Добавить();
СтрокаБуфера.КлючСтроки = КлючСтроки;
КонецЕсли;
СтрокаБуфера.Количество = Значение;
КонецЕсли;
КонецПроцедуры
Теперь проанализируем, как эти данные будут подставляться обратно в отчет при его переформировании кнопкой «Сформировать»:
// Фрагмент кода заполнения отчета в цикле обхода выборки запроса
Пока ВыборкаДетали.Следующий() Цикл
// Выводим стандартные данные строки
ТабДок.Вывести(ОбластьСтрока);
НомерТекущейСтроки = ТабДок.ВысотаТаблицы;
// Заполняем расшифровку для последующей идентификации ячейки
ЯчейкаВвода = ТабДок.Область(НомерТекущейСтроки, 10);
ЯчейкаВвода.Расшифровка = ВыборкаДетали.Номенклатура;
ЯчейкаВвода.СодержитЗначение = Истина;
ЯчейкаВвода.ТипЗначения = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3));
ЯчейкаВвода.Защита = Ложь;
// Проверяем, вносил ли пользователь изменения ранее
СтрокаПоиска = ВведенныеКорректировки.Найти(ВыборкаДетали.Номенклатура, "КлючСтроки");
Если СтрокаПоиска <> Неопределено Тогда
// Подставляем сохраненное пользователем значение
ЯчейкаВвода.Значение = СтрокаПоиска.Количество;
ТоварВПути = СтрокаПоиска.Количество;
Иначе
// Значение по умолчанию
ЯчейкаВвода.Значение = 0;
ТоварВПути = 0;
EndIf;
// Рассчитываем итоговую колонку с учетом ручных корректировок
ОстатокНаНачало = ВыборкаДетали.ОстатокНаНачало;
Приход = ВыборкаДетали.Приход;
Продажа = ВыборкаДетали.Продажа;
ОстатокНаКонец = (ОстатокНаНачало + Приход + ТоварВПути) - Продажа;
// Записываем итог
ТабДок.Область(НомерТекущейСтроки, 12).Текст = Формат(ОстатокНаКонец, "ЧДЦ=3");
КонецЦикла;
Если введенные пользователем данные по товарам в пути должны сохраняться в системе перманентно (например, чтобы другие менеджеры или директор при открытии отчета на следующий день видели эти же цифры), использование временных таблиц формы не поможет. В этом случае нам необходимо организовать хранение корректировок в базе данных.
Наилучшим техническим решением будет создание простого независимого регистра сведений с именем КорректировкиТоваровВПути. В качестве измерений определим Номенклатура (и, если требуется аналитика, Склад или Организация), а ресурсом сделаем Количество с типом Число.
При таком подходе логика работы программы изменится в лучшую сторону:
ПриИзмененииСодержимогоОбласти мы сразу записываем введенное пользователем число в регистр сведений, используя менеджер записи:
// Запись изменений напрямую в базу данных
Процедура СохранитьВведенноеЗначениеВРегистр(НоменклатураСсылка, Значение)
МенеджерЗаписи = РегистрыСведений.КорректировкиТоваровВПути.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Номенклатура = НоменклатураСсылка;
Если Значение = 0 Тогда
// Если пользователь очистил ячейку, удаляем запись из регистра
МенеджерЗаписи.Удалить();
Иначе
// Записываем новое значение
МенеджерЗаписи.Количество = Значение;
МенеджерЗаписи.Записать(Истина);
КонецЕсли;
КонецПроцедуры
ЛЕВОЕ СОЕДИНЕНИЕ нашей таблицы данных с виртуальной таблицей среза последних или просто физической таблицей регистра сведений КорректировкиТоваровВПути по полю Номенклатура.Чтобы пользователи могли комфортно работать с интерактивным отчетом, рекомендуем применить несколько полезных интерфейсных приемов:
Область.Имя = "ТоварВПути_" + СокрЛП(Номенклатура.УникальныйИдентификатор());. Тогда в событии изменения вы легко сможете определить, какая именно строка и номенклатура подверглись корректировке, не привязываясь к номерам колонок.Мы подробно разобрали, как превратить статичный табличный документ 1С в удобный интерактивный инструмент с возможностью ручного ввода данных. Комбинируя правильную настройку свойств ячеек, обработку событий изменения содержимого и надежные механизмы сохранения данных через временные буферы или регистры сведений, вы сможете решать любые нестандартные задачи по автоматизации рабочих мест ваших пользователей.