Автоматизация регулярных операций — одна из ключевых задач программиста 1С. Типичный пример такой задачи: каждый месяц необходимо начислять арендную плату по множеству договоров. Чтобы не делать это вручную, мы можем реализовать алгоритм, который сам найдет актуальные договоры, проверит, было ли уже начисление, и при необходимости создаст новый документ, заполнив его всеми необходимыми данными. Рассмотрим подробно, как это реализовать.
Для решения задачи нам потребуется обработать три типа объектов: документ Договор аренды помещения, документ Начисление арендной платы и, опционально, регистры для контроля взаиморасчетов. Логика работы программного кода будет следующей:
ДокументОбъект.НачислениеАренднойПлаты.Использование запросов — самый эффективный способ получения данных в 1С. Мы выберем все договоры, у которых дата окончания больше текущей даты, и которые не помечены на удаление. Проанализируем пример кода для получения этой выборки:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДоговорАренды.Ссылка КАК Ссылка,
| ДоговорАренды.Арендодатель КАК Арендодатель,
| ДоговорАренды.СуммаАренднойПлаты КАК Сумма
|ИЗ
| Документ.ДоговорАрендыПомещения КАК ДоговорАренды
|ГДЕ
| ДоговорАренды.ДатаОкончания > &ТекущаяДата
| И ДоговорАренды.Проведен = ИСТИНА
| И ДоговорАренды.ПометкаУдаления = ЛОЖЬ";
Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата());
РезультатЗапроса = Запрос.Выполнить();
ТаблицаДоговоров = РезультатЗапроса.Выгрузить();
В данном запросе мы получаем ссылку на договор, контрагента (арендодателя) и сумму, указанную в договоре. Метод Выгрузить() позволяет нам получить Таблицу Значений, с которой удобно работать в дальнейшем.
Часто возникает ситуация: документ начисления за месяц уже создан, но в середине месяца подписали еще один договор. Обычная проверка «существует ли документ за месяц» тут не сработает. Рассмотрим более надежный способ через Левое Соединение в запросе. Мы соединим таблицу действующих договоров с уже созданными строками начислений за этот месяц. Если связи нет (ЕСТЬ NULL), значит, начисление еще не выполнялось.
Запрос.Текст =
"ВЫБРАТЬ
| Договоры.Ссылка КАК Договор,
| Договоры.Арендодатель КАК Арендодатель,
| Договоры.СуммаАренднойПлаты КАК Сумма
|ИЗ
| Документ.ДоговорАрендыПомещения КАК Договоры
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.НачислениеАренднойПлаты.ТЧНачисления КАК Начисления
| ПО Договоры.Ссылка = Начисления.ДоговорАренды
| И Начисления.Ссылка.Дата МЕЖДУ &НачалоМесяца И &КонецМесяца
| И Начисления.Ссылка.Проведен = ИСТИНА
|ГДЕ
| Договоры.ДатаОкончания > &ТекущаяДата
| И Договоры.Проведен = ИСТИНА
| И Начисления.ДоговорАренды ЕСТЬ NULL";
Запрос.УстановитьПараметр("НачалоМесяца", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецМесяца", КонецМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата());
Этот подход позволяет системе видеть только те договоры, по которым в текущем периоде еще не было движений. Это исключает дублирование и позволяет «дозаполнять» начисления в любое время.
Теперь, когда у нас есть список договоров, требующих начисления, перейдем к созданию объекта документа. Для этого используется метод СоздатьДокумент() менеджера документа. Разберем процесс заполнения реквизитов и строк табличной части в цикле Для Каждого.
Если НЕ ТаблицаДоговоров.Количество() = 0 Тогда
НовыйДокумент = Документы.НачислениеАренднойПлаты.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Комментарий = "Создано автоматически: " + ТекущаяДата();
// Заполняем табличную часть
Для Каждого СтрокаДоговора Из ТаблицаДоговоров Цикл
НоваяСтрока = НовыйДокумент.ТЧНачисления.Добавить();
// Используем ЗаполнитьЗначенияСвойств для одинаковых имен колонок
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДоговора);
// Или заполняем вручную, если имена отличаются
НоваяСтрока.ДоговорАренды = СтрокаДоговора.Договор;
НоваяСтрока.СуммаНачисления = СтрокаДоговора.Сумма;
КонецЦикла;
// Подсчет итога по документу
НовыйДокумент.СуммаДокумента = НовыйДокумент.ТЧНачисления.Итог("СуммаНачисления");
// Запись и проведение
Попытка
НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Документ начисления успешно создан и проведен!");
Исключение
Сообщить("Ошибка при проведении документа: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЕсли;
Обратите внимание на использование блока Попытка...Исключение. При программном создании документов это критически важно, так как проведение может сорваться из-за нехватки прав, незаполненных обязательных реквизитов или блокировок базы данных. Метод КраткоеОписаниеОшибки() поможет быстро понять причину сбоя.
Рассмотрим вопрос о том, где именно должен выполняться этот код. В обсуждении часто предлагают запускать проверку ПриНачалеРаботыСистемы. Однако это плохая практика по нескольким причинам:
Правильное решение: Использовать РегламентныеЗадания. Вы можете вынести процедуру создания начислений в общий модуль с признаком «Сервер» и настроить расписание в консоли регламентных заданий (например, раз в сутки в 01:00). Это позволит системе работать полностью автономно, а бухгалтер будет видеть уже готовые документы в списке утром.
Многие начинающие программисты пытаются использовать метод ТЧ.Загрузить(ТаблицаЗначений). Рассмотрим причины, по которым это может вызвать ошибку «Несоответствие типов»:
Загрузить() требует, чтобы имена колонок в ТЗ и ТЧ полностью совпадали. Если в ТЗ колонка называется Сумма, а в ТЧ — СуммаНачисления, данные не будут перенесены.ВерсияДанных), которых нет в табличной части документа, это может привести к ошибке.Именно поэтому ручной обход в цикле или использование ЗаполнитьЗначенияСвойств считается более надежным и гибким подходом.
Мы проанализировали процесс автоматизации начислений по аренде. Главные моменты, которые стоит запомнить: всегда проверяйте наличие уже созданных документов, чтобы избежать дублирования (лучше через ЛЕВОЕ СОЕДИНЕНИЕ), используйте регламентные задания для фоновой работы и всегда обрабатывайте исключения при записи документов. Такая архитектура сделает вашу систему надежной и удобной для конечных пользователей.