При работе с системой 1С:Предприятие 8.3 мы часто сталкиваемся с задачами, требующими автоматического обновления данных в документах, особенно после использования внешних обработок. Одной из таких распространенных ситуаций является необходимость пересчета колонок табличной части и обновления формы документа, чтобы пользователь сразу увидел внесенные изменения. Давайте разберем эту проблему подробно и найдем оптимальные решения, учитывая особенности клиент-серверной архитектуры 1С.
Суть проблемы: мы программно изменяем данные в табличной части документа (например, через внешнюю обработку), но форма документа на клиенте не "знает" об этих изменениях и не отображает их автоматически. Пользователю приходится вручную обновлять форму или даже перезаходить в документ, что крайне неудобно. Наша цель — добиться автоматического пересчета и визуального обновления.
Прежде чем перейти к решениям, давайте вспомним, что архитектура 1С:Предприятие 8.3 разделяет выполнение кода на клиентскую и серверную части. Клиент в основном отвечает за отображение интерфейса и взаимодействие с пользователем, тогда как сервер выполняет основную логику, работает с данными информационной базы. Взаимодействие между ними происходит через серверные вызовы. Это разделение важно учитывать при разработке, особенно когда речь идет о работе с данными документов.
Любые изменения, касающиеся данных объекта (документа, справочника), должны выполняться на сервере. Если мы меняем данные в табличной части на клиенте, а затем хотим выполнить серверные операции (например, пересчитать суммы или обратиться к базе данных), нам потребуется передать эти данные на сервер. Для оптимизации взаимодействия рекомендуется объединять несколько вызовов сервера в один, чтобы сократить сетевой трафик и ускорить работу.
В типовых конфигурациях 1С, таких как ERP, часто предусмотрены специальные служебные модули для работы с табличными частями. Они позволяют выполнять различные действия, включая пересчет сумм, НДС, заполнение ставок и другие операции. Мы рассмотрим, как использовать эти механизмы для автоматического пересчета.
Обратим внимание на общие модули ПакетнаяОбработкаТабличнойЧастиКлиентСервер и ПакетнаяОбработкаТабличнойЧастиСервер. Они предоставляют методы для обработки как всей табличной части, так и отдельных строк.
Шаг 1: Подготовка параметров пересчета.
Сначала нам нужно определить, какие параметры пересчета мы хотим применить. Для этого мы можем использовать функцию ПараметрыПересчетаСуммыНДСВТЧ из модуля ПакетнаяОбработкаТабличнойЧастиКлиентСервер. Она вернет структуру с необходимыми настройками.
Шаг 2: Формирование структуры действий.
Затем мы создадим структуру, которая будет описывать, какие именно действия должны быть выполнены над табличной частью. Например, мы можем указать пересчет суммы НДС и суммы с НДС.
Шаг 3: Вызов серверной процедуры обработки.
Для выполнения пакетной обработки всей табличной части мы используем метод ОбработатьТЧ из модуля ПакетнаяОбработкаТабличнойЧастиСервер. Этот метод принимает объект табличной части и структуру действий.
Посмотрим на пример кода, демонстрирующий этот подход:
// Предполагаем, что "об" - это объект документа, например, ЗаказКлиента
// и мы находимся в серверном контексте (&НаСервере)
// === Часть 1: Заполнение ставки НДС (если требуется) ===
// Получаем объект документа
// об = ссылкаРТУ.ПолучитьОбъект(); // Если мы работаем со ссылкой
СтруктураДействийЗаполнения = Новый Структура;
СтруктураДействийЗаполнения.Вставить("ЗаполнитьСтавкуНДС", ПакетнаяОбработкаТабличнойЧастиКлиентСервер.ПараметрыЗаполненияСтавкиНДС(об));
ПакетнаяОбработкаТабличнойЧастиСервер.ОбработатьТЧ(об.Товары, СтруктураДействийЗаполнения, Неопределено);
// === Часть 2: Пересчет сумм НДС и сумм с НДС ===
СтруктураПересчетаСуммы = ПакетнаяОбработкаТабличнойЧастиКлиентСервер.ПараметрыПересчетаСуммыНДСВТЧ(об);
СтруктураДействийПересчета = Новый Структура;
СтруктураДействийПересчета.Вставить("ПересчитатьСуммуНДС", СтруктураПересчетаСуммы);
СтруктураДействийПересчета.Вставить("ПересчитатьСуммуСНДС", СтруктураПересчетаСуммы);
ПакетнаяОбработкаТабличнойЧастиСервер.ОбработатьТЧ(об.Товары, СтруктураДействийПересчета, Неопределено);
В этом примере мы сначала можем заполнить ставки НДС, а затем пересчитать суммы. Обратите внимание, что все эти действия выполняются на сервере. Для поиска аналогичных примеров в типовых конфигурациях, вы можете открыть форму документа, например, ЗаказКлиента.ФормаДокумента или ЗаказПоставщику.ФормаДокумента, и выполнить поиск по строке ПакетнаяОбработкаТабличнойЧастиСервер.ОбработатьТЧ или ПакетнаяОбработкаТабличнойЧастиСервер.ОбработатьСтрокиТЧ.
Бывают ситуации, когда стандартных механизмов недостаточно. Например, если нам нужно выполнить сложный расчет на основе дополнительных реквизитов номенклатуры или других специфических данных, а затем обновить определенные колонки табличной части. В таких случаях мы можем написать собственный код для построчного пересчета.
Давайте рассмотрим пример, где нам нужно рассчитать площадь на основе длины и ширины, хранящихся в дополнительных реквизитах номенклатуры, и затем обновить количество в табличной части, а также пересчитать суммы.
Шаг 1: Создание серверной процедуры.
Внешняя обработка должна вызывать серверную процедуру, которая будет выполнять всю основную логику. Эта процедура должна принимать данные формы (или объект документа) в качестве параметра.
Процедура РасчитатьМ2(ДанныеФормы) Экспорт
ТекстПодробно = "";
// ... дальнейший код ...
КонецПроцедуры;
Шаг 2: Подготовка параметров для построчного пересчета.
Аналогично первому решению, мы можем использовать ПакетнаяОбработкаТабличнойЧастиКлиентСервер.ПараметрыПересчетаСуммыНДСВСтрокеТЧ для получения параметров пересчета конкретной строки.
СтруктураПересчетаСуммы = ПакетнаяОбработкаТабличнойЧастиКлиентСервер.ПараметрыПересчетаСуммыНДСВСтрокеТЧ(ДанныеФормы);
СтруктураДействий = Новый Структура;
СтруктураДействий.Вставить("ПересчитатьСуммуНДС", СтруктураПересчетаСуммы);
СтруктураДействий.Вставить("ПересчитатьСуммуСНДС", СтруктураПересчетаСуммы);
СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомРучнойСкидки", Новый Структура("Очищать", Ложь));
СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомАвтоматическойСкидки", Новый Структура("Очищать", Истина));
Здесь мы также добавляем действия по пересчету сумм с учетом ручных и автоматических скидок. Обратите внимание на параметры Очищать, которые могут быть полезны при различных сценариях пересчета.
Шаг 3: Оптимизация получения дополнительных реквизитов.
Важным моментом является оптимизация доступа к данным. Если мы получаем ссылки на метаданные (например, на виды дополнительных реквизитов) в цикле, это может замедлить работу. Мы настоятельно рекомендуем вынести такие операции за пределы цикла перебора строк табличной части.
// Получаем ссылки на дополнительные реквизиты один раз до начала цикла
ДопСвойствоШирина = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Ширина");
ДопСвойствоДлина = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Длина");
КэшированныеЗначения = Неопределено; // Используется для оптимизации в типовых механизмах
Шаг 4: Перебор строк табличной части и выполнение расчетов.
Теперь мы можем перебрать каждую строку табличной части, выполнить для нее наши специфические расчеты и обновить данные.
Для Каждого Товар Из ДанныеФормы.Товары Цикл
// Получаем значения дополнительных реквизитов для текущей номенклатуры и характеристики
// Важно: Проверяйте существование реквизитов перед обращением к ним
Ширина = 0;
СвойствоШирина = Товар.Номенклатура.ДополнительныеРеквизиты.Найти(ДопСвойствоШирина);
Если СвойствоШирина <> Неопределено Тогда
Ширина = СвойствоШирина.Значение;
КонецЕсли;
Длина = 0;
СвойствоДлина = Товар.Характеристика.ДополнительныеРеквизиты.Найти(ДопСвойствоДлина);
Если СвойствоДлина <> Неопределено Тогда
Длина = СвойствоДлина.Значение;
КонецЕсли;
КоличествоШтук = Товар.Д4Зак_КоличествоСправочно; // Пример пользовательского поля
// Выполняем наш расчет
Площадь = (Ширина / 1000) * (Длина / 1000) * КоличествоШтук;
// Обновляем количество в зависимости от наличия полей
Если Товар.Свойство("КоличествоУпаковок") Тогда
Товар.КоличествоУпаковок = Площадь;
СтруктураДействий.Вставить("ПересчитатьСумму", "КоличествоУпаковок");
СтруктураДействий.ПересчитатьСуммуСУчетомРучнойСкидки.Вставить("ИмяКоличества", "КоличествоУпаковок");
ИначеЕсли Товар.Свойство("Количество") Тогда
Товар.Количество = Площадь;
СтруктураДействий.Вставить("ПересчитатьСумму", "Количество");
СтруктураДействий.ПересчитатьСуммуСУчетомРучнойСкидки.Вставить("ИмяКоличества", "Количество");
КонецЕсли;
// Вызываем построчную обработку типовым механизмом
ПакетнаяОбработкаТабличнойЧастиСервер.ОбработатьСтрокуТЧ(Товар, СтруктураДействий, КэшированныеЗначения, ДанныеФормы.Товары);
// Формируем дополнительную информацию для вывода
Наименование = "";
Если Товар.Свойство("НоменклатураПартнера") Тогда
Если Товар.НоменклатураПартнера <> Справочники.НоменклатураКонтрагентов.ПустаяСсылка() Тогда
Наименование = Товар.НоменклатураПартнера.НаименованиеПолное;
КонецЕсли;
Иначе
Наименование = Товар.Номенклатура.НаименованиеПолное;
КонецЕсли;
ТекстПодробно = ТекстПодробно + Наименование + " длина " + Формат(Длина, "ЧГ=") + " " + КоличествоШтук + " шт" + Символы.ПС;
КонецЦикла;
Шаг 5: Обновление дополнительных полей формы.
Если в форме предусмотрены поля для вывода дополнительной информации, мы можем обновить их в конце процедуры.
Если ДанныеФормы.Свойство("ДополнительнаяИнформация") Тогда
ДанныеФормы.ДополнительнаяИнформация = ТекстПодробно;
ИначеЕсли ДанныеФормы.Свойство("ПрочаяДополнительнаяИнформацияТекст") Тогда
ДанныеФормы.ПрочаяДополнительнаяИнформацияТекст = ТекстПодробно;
КонецЕсли;
КонецПроцедуры;
Важные замечания по коду:
Для Каждого Товар Из ДанныеФормы.Товары Цикл, мы рекомендуем использовать более описательные имена, например, Для Каждого СтрокаТовар Из ДанныеФормы.Товары Цикл или Для Каждого Строка Из ДанныеФормы.Товары Цикл.ЗаказПоставщику.Товары) обычно содержат оба реквизита КоличествоУпаковок и Количество, и они могут быть заполнены. Проверяйте наличие и тип данных перед использованием.НаименованиеПолное: Использование Товар.Номенклатура.НаименованиеПолное напрямую может быть не всегда оптимальным. В некоторых случаях, особенно при работе с объектами, не записанными в базу, или при сложных сценариях, может потребоваться более аккуратный подход к получению наименования.После того как мы программно изменили данные в табличной части (на сервере), эти изменения необходимо отобразить на форме, которая находится на клиенте. Это критически важный шаг, без которого пользователь не увидит результат наших действий.
Мы рассмотрим несколько методов, которые помогут нам обновить форму:
Прочитать(): Этот метод объекта формы (или объекта документа, если мы работаем непосредственно с ним) заставляет форму перечитать свои данные из объекта в базе данных. Если наша внешняя обработка уже записала изменения в базу, Прочитать() — это наиболее надежный способ обновить всю форму.
// Пример вызова после выполнения серверной процедуры и записи объекта
ЭтаФорма.Прочитать();
ОбновитьОтображениеДанных(): Этот метод также позволяет обновить данные на форме. Он может быть полезен, когда изменения вносятся в данные формы, но еще не записаны в базу данных, или когда нужно обновить только часть данных.
// Пример вызова
ЭтаФорма.ОбновитьОтображениеДанных();
ЗначениеВРеквизитФормы(): Если изменения внесены в объект, но еще не записаны в базу данных, и нам нужно перенести эти изменения на реквизиты формы, мы можем использовать ЗначениеВРеквизитФормы(). Этот метод позволяет передать данные из объекта в реквизит формы.
// Пример: Обновить реквизит формы "Объект" из переменной "ОбъектДокумента"
ЗначениеВРеквизитФормы(ОбъектДокумента, "Объект");
Чаще всего, после серверного вызова, который изменил объект документа и, возможно, записал его, достаточно вызвать ЭтаФорма.Прочитать() на клиенте, чтобы форма полностью обновилась, включая табличные части и другие реквизиты.
Чтобы ваша работа с табличными частями была эффективной и поддерживаемой, мы предлагаем придерживаться следующих рекомендаций:
&НаСервере, &НаКлиенте) в зависимости от контекста выполнения.ПриСозданииНаСервере формы.Мы проанализировали различные подходы к автоматическому пересчету колонок табличных частей и обновлению формы документа после внешней обработки. Используя типовые механизмы пакетной обработки или разрабатывая собственные алгоритмы с учетом клиент-серверной архитектуры и лучших практик, вы сможете создавать эффективные и удобные решения в 1С:Предприятие 8.3.
← К списку