Разработка и Конфигурация · 11 мин
Как изменить данные в табличной части документа 1С программно
Как программно изменить табличную часть документа 1С: получение объекта, перебор строк, изменение и добавление строк, запись с проведением, типовые ошибки.
Табличная часть документа в 1С — это коллекция строк, хранящаяся внутри объекта документа: товары в реализации, начисления в расчете зарплаты, услуги в акте. Чтобы изменить её программно, недостаточно обратиться к ссылке на документ — нужно получить объект документа методом ПолучитьОбъект(), внести изменения в коллекцию строк и записать объект обратно в базу.
Типовая задача выглядит так: найти документ, изменить цену или количество в определенных строках, добавить новую строку или удалить лишние, а затем записать документ с проведением. Все эти операции выполняются средствами встроенного языка и не требуют прямых запросов к базе данных — наоборот, прямое изменение таблиц SQL в обход платформы категорически не рекомендуется.
Ниже разобраны основные сценарии: получение объекта документа, перебор и изменение строк, добавление и удаление строк, запись с проведением и без, а также типичные ошибки, из-за которых изменения теряются или документ перестает проводиться.
Получение объекта документа для редактирования
Ключевой принцип: ссылка на документ доступна только для чтения. Через ссылку можно прочитать реквизиты и строки табличной части, но любая попытка присвоить значение вызовет ошибку. Для редактирования нужен объект документа, который загружается в память целиком — со всеми табличными частями.
ДокументОбъект = ДокументСсылка.ПолучитьОбъект();
// Теперь табличные части доступны для изменения
Для Каждого СтрокаТовара Из ДокументОбъект.Товары Цикл
СтрокаТовара.Цена = СтрокаТовара.Цена * 1.1;
КонецЦикла;
ДокументОбъект.Записать();
Если документ нужно найти по номеру или другому условию, сначала получают ссылку через менеджер документа: Документы.РеализацияТоваровУслуг.НайтиПоНомеру(Номер, Дата) или запросом. Перед вызовом ПолучитьОбъект() полезно проверить, что ссылка не пустая и документ не помечен на удаление, если это критично для логики обработки.
Изменение существующих строк табличной части
Самый частый сценарий — перебор строк коллекции с изменением отдельных реквизитов. Коллекцию табличной части можно обходить циклом Для Каждого или по индексу. Изменения применяются к объекту в памяти и попадут в базу только после записи.
// Увеличить количество в строках с конкретной номенклатурой
Для Каждого СтрокаТовара Из ДокументОбъект.Товары Цикл
Если СтрокаТовара.Номенклатура = ИскомаяНоменклатура Тогда
СтрокаТовара.Количество = СтрокаТовара.Количество + 1;
КонецЕсли;
КонецЦикла;
Если нужно найти конкретную строку без полного перебора, используйте метод Найти() или НайтиСтроки() коллекции. Первый возвращает одну строку или Неопределено, второй — массив всех подходящих строк. Это удобно, когда табличная часть большая, а целевая строка одна.
СтрокаТовара = ДокументОбъект.Товары.Найти(ИскомаяНоменклатура, "Номенклатура");
Если СтрокаТовара <> Неопределено Тогда
СтрокаТовара.Сумма = СтрокаТовара.Количество * СтрокаТовара.Цена;
КонецЕсли;
Пересчет зависимых реквизитов
При программном изменении количества или цены платформа не пересчитает сумму автоматически — в формах документов это делают обработчики событий, которые при программной записи объекта не вызываются. Поэтому после изменения одного реквизита пересчитывайте связанные вручную: сумму, сумму НДС, итоги. Иначе документ запишется с расхождениями, которые всплывут в отчетах и при проведении.
Добавление и удаление строк
Новая строка создается методом Добавить() коллекции табличной части. Метод возвращает созданную строку, реквизиты которой нужно заполнить. Незаполненные реквизиты останутся пустыми — платформа не подставит значения по умолчанию из формы.
НоваяСтрока = ДокументОбъект.Товары.Добавить();
НоваяСтрока.Номенклатура = НоменклатураСсылка;
НоваяСтрока.Количество = 2;
НоваяСтрока.Цена = 1500;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
Удаление выполняется методом Удалить() с передачей строки или индекса, а полная очистка — методом Очистить(). При удалении в цикле есть тонкость: нельзя удалять элементы коллекции, которую перебираете через Для Каждого, — это приведет к пропуску строк или ошибке. Правильные подходы: обход по индексу с конца к началу или сначала собрать строки в массив, а потом удалять.
// Удаление с конца коллекции — безопасный вариант
Для Индекс = 1 - ДокументОбъект.Товары.Количество() По 0 Цикл
СтрокаТовара = ДокументОбъект.Товары[-Индекс];
Если СтрокаТовара.Количество = 0 Тогда
ДокументОбъект.Товары.Удалить(СтрокаТовара);
КонецЕсли;
КонецЦикла;
Запись документа: с проведением и без
Метод Записать() сохраняет объект в базу. Режим записи определяет, что произойдет с движениями документа: РежимЗаписиДокумента.Запись — просто сохранение, РежимЗаписиДокумента.Проведение — сохранение с формированием движений по регистрам, РежимЗаписиДокумента.ОтменаПроведения — распроведение.
// Запись с проведением
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
// Запись без проведения (документ остается непроведенным)
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Если документ был проведен, а вы записали его режимом «Запись», движения сохранятся, но в типовых конфигурациях это может привести к рассогласованию данных документа и регистров. Безопасное правило: изменили проведенный документ — записывайте с проведением. Если проведение невозможно из-за закрытого периода или отсутствия остатков, платформа выдаст ошибку, и запись не выполнится.
Запись без контроля и обмен данными
В массовых обработках иногда используют ДокументОбъект.ОбменДанными.Загрузка = Истина перед записью — это отключает бизнес-логику проведения и проверки. Применять такой прием стоит только при загрузке данных, когда движения формируются отдельно или не нужны. Для обычного изменения табличной части проведенного документа этот режим опасен: документ сохранится, но регистры останутся в старом состоянии.
Массовое изменение и транзакции
Когда табличные части нужно изменить в десятках или тысячах документов, обработку стоит упаковать в транзакцию или хотя бы обернуть запись каждого документа в попытку-исключение. Транзакция гарантирует, что при сбое база не останется в состоянии, где половина документов изменена, а половина нет.
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект();
Для Каждого СтрокаТовара Из ДокументОбъект.Товары Цикл
СтрокаТовара.Цена = СтрокаТовара.Цена * 1.05;
СтрокаТовара.Сумма = СтрокаТовара.Количество * СтрокаТовара.Цена;
КонецЦикла;
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение ОписаниеОшибки();
КонецПопытки;
Учтите, что длинная транзакция удерживает блокировки на изменяемых документах — пользователи в это время не смогут их редактировать. Для очень больших объемов разбивайте обработку на порции с фиксацией транзакции после каждой порции и ведите журнал обработанных документов, чтобы при повторном запуске не обрабатывать их заново.
- 1Найдите ссылку на документ по номеру, дате или запросом
- 2Получите объект: ДокументОбъект = Ссылка.ПолучитьОбъект()
- 3Измените строки табличной части: перебор, Найти(), Добавить(), Удалить()
- 4Пересчитайте зависимые реквизиты: суммы, НДС, итоги
- 5Запишите объект с нужным режимом: Запись или Проведение
- 6Проверьте результат: откройте документ в интерфейсе и сверьте движения по регистрам
Проверка результата и границы самостоятельной работы
После программного изменения результат проверяют двумя способами. Визуально: открыть документ в пользовательском интерфейсе и убедиться, что строки, суммы и итоги соответствуют ожиданиям. По регистрам: сформировать отчет или проверить движения документа, чтобы убедиться, что проведение прошло и данные в учете обновились.
Признак того, что что-то пошло не так: документ записался, но не проводится с ошибкой; суммы в табличной части не сходятся с итогами документа; отчеты по регистрам показывают старые данные. В первом случае смотрите текст ошибки проведения, во втором — проверяйте пересчет зависимых реквизитов, в третьем — режим записи.
Самостоятельно безопасно выполнять изменения в тестовой копии базы и разовые правки отдельных документов текущего периода. К специалисту по 1С стоит обращаться, когда требуется массовая обработка закрытых периодов, изменение документов с корректировками НДС или доработка типовых механизмов проведения — ошибка в этих местах ведет к искажению учетных данных.
Как проверить результат
Документ проведен, в журнале нет признака ошибки
Документ записан, но не проведен, при попытке провести — ошибка
Сумма строки равна произведению количества на цену, итоги сходятся
Расхождения между количеством, ценой и суммой
Движения сформированы заново и отражают новые данные
Движения соответствуют старым данным документа
Частые вопросы
Можно ли изменить табличную часть, не получая объект документа?
Нет. Через ссылку данные доступны только для чтения. Любое изменение реквизитов или строк табличной части требует вызова ПолучитьОбъект() и последующей записи объекта. Прямые запросы к таблицам СУБД в обход платформы нарушают целостность данных и не поддерживаются.
Почему после изменения цены сумма в строке не пересчиталась?
Пересчет суммы в типовых конфигурациях выполняется в обработчиках событий формы, которые при программной записи объекта не срабатывают. Пересчитывайте зависимые реквизиты в коде вручную: сумму, сумму НДС, итоговые значения.
Как удалить все строки табличной части одной командой?
Используйте метод коллекции Очистить(): ДокументОбъект.Товары.Очистить(). После этого табличная часть будет пустой, изменения зафиксируются при записи объекта. Если документ проводится, убедитесь, что бизнес-логика конфигурации допускает проведение с пустой табличной частью.
Чем отличается Записать() от Записать(РежимЗаписиДокумента.Проведение)?
Первый вариант только сохраняет данные документа, второй дополнительно формирует движения по регистрам. Если документ был проведен, записывайте его с проведением, иначе данные документа и регистров разойдутся.
Почему при удалении строк в цикле удаляются не все нужные строки?
При удалении элемента коллекции индексы остальных строк смещаются, и перебор Для Каждого пропускает часть элементов. Удаляйте строки обходом коллекции с конца к началу по индексу либо сначала соберите строки в массив через НайтиСтроки() и удаляйте их отдельным циклом.
Что делать, если документ не записывается из-за даты запрета изменения данных?
Дата запрета редактирования — штатное ограничение, защищающее закрытые периоды. Либо измените документ вручную после согласования с ответственными за учет и временного снятия запрета, либо создайте корректировочный документ текущей датой. Обходить запрет установкой привилегированного режима без крайней необходимости не следует.
Что важно запомнить
Изменение табличной части всегда строится по одной схеме: ПолучитьОбъект() → изменение строк → пересчет зависимых реквизитов → Записать() с нужным режимом. Удаляйте строки обходом с конца, массовые обработки выполняйте в транзакциях и сначала проверяйте на копии базы.