Создание и изменение объектов

Создаём из кода новые элементы справочников и документы, заполняем реквизиты и сохраняем их в базу — с проверкой перед записью и проведением документа.

Запись (.Записать()) — это момент, когда объект из памяти сохраняется в базу данных. До вызова .Записать() все ваши изменения существуют только в переменной и пропадут, если не сохранить.

В прошлом уроке мы читали данные. Теперь научимся их создавать и менять. Это ежедневная работа 1С-разработчика: обработка загружает номенклатуру из внешнего файла, при регистрации сделки автоматически создаётся документ, по кнопке заводится новый контрагент. Во всех случаях схема одна: получить объект (новый или существующий), заполнить его реквизиты и вызвать .Записать(). Дьявол — в деталях: что заполнять, как проверять и когда документ нужно ещё и провести.

Зачем это нужно на практике

Ручной ввод данных — это долго и с ошибками. Код позволяет создавать сотни записей за секунды: загрузить прайс поставщика, сформировать документы по расписанию, синхронизировать данные с сайтом. Но у автоматической записи есть цена: если заполнить объект неверно или пропустить обязательный реквизит, в базе окажется «мусор», который потом придётся вычищать вручную. Поэтому важно не просто «записать», а записать корректно.

Создаём элемент справочника

Новый элемент справочника рождается методом СоздатьЭлемент() у менеджера справочника. Он возвращает пустой объект, у которого надо заполнить реквизиты и вызвать .Записать():

// Создаём новый элемент справочника Контрагенты
НовыйКонтрагент = Справочники.Контрагенты.СоздатьЭлемент();

// Заполняем реквизиты
НовыйКонтрагент.Наименование = "ООО Ромашка";
НовыйКонтрагент.ИНН = "7701234567";
НовыйКонтрагент.КПП = "770101001";

// Сохраняем в базу
НовыйКонтрагент.Записать();

Сообщить("Создан контрагент, ссылка: " + НовыйКонтрагент.Ссылка);

Пока .Записать() не вызван, контрагента в базе нет — есть только заполненный объект в переменной. Кстати, Код элемента обычно можно не заполнять: если у справочника настроена автонумерация, платформа присвоит следующий код сама при записи. А чтобы создать не элемент, а группу (папку), есть парный метод СоздатьГруппу().

Изменение существующего элемента отличается лишь тем, как мы получаем объект — не создаём новый, а берём объект уже имеющегося элемента через .ПолучитьОбъект() из прошлого урока:

// Меняем ИНН у существующего контрагента
КонтрагентСсылка = Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка");

Если НЕ КонтрагентСсылка.Пустая() Тогда
    КонтрагентОбъект = КонтрагентСсылка.ПолучитьОбъект();
    КонтрагентОбъект.ИНН = "7707777777";
    КонтрагентОбъект.Записать();
КонецЕсли;

Проверяем перед записью: ЗначениеЗаполнено()

Прежде чем сохранять объект, стоит убедиться, что ключевые реквизиты действительно заполнены. Для этого есть глобальная функция ЗначениеЗаполнено() — она возвращает Ложь для «пустых» значений: пустой строки, нуля, пустой даты, пустой ссылки, Неопределено. Это удобнее, чем сравнивать с "" вручную, потому что работает для любого типа:

НовыйКонтрагент = Справочники.Контрагенты.СоздатьЭлемент();
НовыйКонтрагент.Наименование = "ООО Василёк";
НовыйКонтрагент.ИНН = "";  // забыли заполнить

// Проверяем ДО записи
Если НЕ ЗначениеЗаполнено(НовыйКонтрагент.Наименование) Тогда
    Сообщить("Не заполнено наименование — запись отменена");
ИначеЕсли НЕ ЗначениеЗаполнено(НовыйКонтрагент.ИНН) Тогда
    Сообщить("Не заполнён ИНН — запись отменена");
Иначе
    НовыйКонтрагент.Записать();
    Сообщить("Контрагент записан");
КонецЕсли;

Результат:

Не заполнён ИНН — запись отменена

Проверять реквизиты лучше в самом объекте — в его модуле есть событие ОбработкаПроверкиЗаполнения, которое платформа вызывает автоматически при записи. Но пока вы пишете обработки, ручная проверка через ЗначениеЗаполнено() — простой и надёжный приём.

Создаём документ и проводим его

Документы создаются похоже — методом СоздатьДокумент(), но у них есть своя специфика: дата, табличные части (строки товаров) и понятие проведения. Проведение — это когда документ не просто сохраняется, а формирует движения в регистрах (например, списывает товар со склада). Разберём на приходной накладной:

// Создаём новый документ ПоступлениеТоваров
Поступление = Документы.ПоступлениеТоваров.СоздатьДокумент();
Поступление.Дата = ТекущаяДата();
Поступление.Контрагент = Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка");

// Добавляем строку в табличную часть "Товары"
НоваяСтрока = Поступление.Товары.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("000000001");
НоваяСтрока.Количество = 10;
НоваяСтрока.Цена = 250;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;

// Записываем С ПРОВЕДЕНИЕМ — документ сформирует движения в регистрах
Поступление.Записать(РежимЗаписиДокумента.Проведение);

Ключевая строка — последняя. Метод .Записать() у документа принимает режим записи. Если передать РежимЗаписиДокумента.Проведение, документ будет проведён (сформирует движения). Если вызвать .Записать() без параметра — документ просто сохранится как черновик, но склад не изменится. Есть и обратная операция — РежимЗаписиДокумента.ОтменаПроведения, чтобы отменить проведение.

Как это работает

Когда вы вызываете .Записать(), платформа делает несколько вещей по порядку. Сначала срабатывают события модуля объекта: ОбработкаПроверкиЗаполнения (для проверки обязательных полей), затем ПередЗаписью, потом данные пишутся в базу, и наконец ПриЗаписи. Для документа с проведением после записи вызывается ещё и ОбработкаПроведения — именно там формируются движения по регистрам. Всё это платформа заворачивает в неявную транзакцию: если на любом шаге возникнет ошибка, изменения откатятся целиком, и в базе не останется полузаписанного документа. Про транзакции подробно — в следующем уроке.

Важная деталь: после успешной записи нового объекта его свойство Ссылка становится «настоящей» — теперь на этот элемент можно ссылаться из других объектов. До первой записи ссылка у нового объекта ещё не привязана к реальной записи в базе.

Частые ошибки

  • Забывают .Записать(). Заполнили объект, а сохранить не вызвали — и удивляются, что в базе ничего не появилось. Изменения живут в памяти до записи.
  • Пишут документ без проведения, ожидая движений. Простой .Записать() сохраняет документ, но не формирует движения. Нужны движения — передавайте РежимЗаписиДокумента.Проведение.
  • Не проверяют заполненность и получают «мусор» в базе. Автоматическая запись без ЗначениеЗаполнено() легко заносит записи с пустыми обязательными реквизитами.
  • Сравнивают строку с "" вместо ЗначениеЗаполнено(). Для ссылок, чисел и дат сравнение с пустой строкой не работает; ЗначениеЗаполнено() корректно проверяет любой тип.
  • Забывают заполнить Дата документа. У нового документа дата пустая; без неё документ либо не проведётся корректно, либо встанет не в тот период.

Итоги

  • Новый элемент — СоздатьЭлемент(), новый документ — СоздатьДокумент(); изменение существующего — через .ПолучитьОбъект().
  • Изменения в объекте сохраняются в базу только вызовом .Записать().
  • Перед записью проверяйте ключевые реквизиты функцией ЗначениеЗаполнено() — она понимает «пустоту» для любого типа.
  • Строки документа добавляются в табличную часть методом .Добавить().
  • Чтобы документ сформировал движения, записывайте его с РежимЗаписиДокумента.Проведение; без режима это просто сохранение.
Проверьте себя
1. Вы создали документ ПоступлениеТоваров, заполнили реквизиты и строки и вызвали Поступление.Записать() без параметров. Товар на складе не изменился. Почему?
AДокумент записан, но не проведён — движения формируются только при записи с РежимЗаписиДокумента.Проведение
BНельзя записывать документ методом .Записать(), нужен отдельный метод .Провести()
CТабличная часть заполняется методом .Новый(), а не .Добавить()
DДокументы вообще не могут менять остатки на складе
2. Что вернёт функция ЗначениеЗаполнено() для реквизита типа Дата, в который ничего не записали (пустая дата)?
AИстина, потому что реквизит существует
BЛожь — пустая дата считается незаполненным значением
CОшибку, ЗначениеЗаполнено() работает только со строками
DНеопределено