При разработке и доработке механизмов заполнения документа Выписка на основании оформленных платежных поручений в платформе 1С:Предприятие 7.7 разработчики часто сталкиваются с ошибкой времени выполнения: Документ не может быть проведен! при попытке вызвать метод Провести() для объекта Документ.ПлатежноеПоручение. Разберем архитектурные особенности учета безналичных расчетов в типовых конфигурациях 1С 7.7, проанализируем ошибки в коде заполнения и реализуем корректный алгоритм отбора неоплаченных платежных поручений.
Выясним причину такого поведения платформы. В типовых конфигурациях (например, «1С:Бухгалтерский учет 7.7») документ Платежное поручение представляет собой лишь намерение совершить платеж (поручение банку) и печатную форму. Он не формирует бухгалтерских проводок по счету 51 «Расчетные счета» и не делает движений по регистрам.
В дереве метаданных конфигуратора для вида документа ПлатежноеПоручение флаг проведения отключен. Попытка вызвать метод ДокПлПор.Провести() на уровне встроенного языка 1С 7.7 для непроводимого по метаданным документа всегда приводит к исключению и прерыванию работы программы. Фактическое списание денежных средств и бухгалтерские проводки формируются исключительно документом Выписка.
Рассмотрим исходный программный код, который часто пытаются использовать для заполнения табличной части выписки:
Процедура ЗаполнитьПлПор()
ТП = СоздатьОбъект("ТаблицаЗначений");
ТП.НоваяКолонка("ПервичныйДокумент");
ТП.НоваяКолонка("ВидДвижения");
ТП.НоваяКолонка("НазначениеПлатежа");
ТП.НоваяКолонка("КоррСчет");
ТП.НоваяКолонка("Расход");
ТП.НоваяКолонка("Субконто1");
ТП.НоваяКолонка("Субконто2");
ТП.НоваяКолонка("БанковскийСчет");
ДокПлПор = СоздатьОбъект("Документ.ПлатежноеПоручение");
ДокПлПор.ВыбратьДокументы(ДатаДок, ДатаДок);
Пока ДокПлПор.ПолучитьДокумент() = 1 Цикл
Если ПометкаУдаления() = 0 Тогда
Если ДокПлПор.РасчетныйСчет = БанковскийСчет Тогда
ТП.НоваяСтрока();
ТП.ПервичныйДокумент = ДокПлПор.ТекущийДокумент();
ТП.ВидДвижения = Перечисление.ВидыДвиженийПоРасчетномуСчету.Оплата;
ТП.НазначениеПлатежа = ДокПлПор.СодержаниеПлатежа;
ТП.КоррСчет = ДокПлПор.Счет;
ТП.Расход = ДокПлПор.Сумма;
ТП.Субконто1 = ДокПлПор.Контрагент;
ТП.Субконто2 = ДокПлПор.Договор;
ТП.БанковскийСчет = ДокПлПор.РасчетныйСчет;
КонецЕсли;
КонецЕсли;
КонецЦикла;
ТП.Свернуть("ПервичныйДокумент,ВидДвижения,НазначениеПлатежа,КоррСчет,Расход,Субконто1,Субконто2,БанковскийСчет", );
ЗагрузитьТабличнуюЧасть(ТП);
ТП.ВыбратьСтроки();
Пока ТП.ПолучитьСтроку() = 1 Цикл
ТП.Очистить();
КонецЦикла;
КонецПроцедуры
Проанализируем критические недочеты данного кода:
ПометкаУдаления() без указания объекта проверяет пометку текущего документа (выписки), а не перебираемого платежного поручения. Необходимо обращаться к выборке: ДокПлПор.ПометкаУдаления() = 0.ТП.Очистить() внутри перебора строк является синтаксической и логической ошибкой. Метод Очистить() за один вызов полностью удаляет все строки и колонки таблицы значений. После передачи данных через ЗагрузитьТабличнуюЧасть(ТП) очищать временный объект вручную вовсе не требуется.Посмотрим, как в типовом журнале платежных поручений отображаются пиктограммы оплаты. Факт оплаты рассчитывается динамически: система проверяет, присутствует ли ссылка на конкретное платежное поручение в реквизите ПервичныйДокумент строк проведенных документов Выписка.
Если в журнале отсутствуют статусные значки (галочки), разберем возможные причины:
Разберем по шагам создание процедуры, которая выбирает платежные поручения за указанную дату, проверяет совпадение расчетного счета и исключает уже оплаченные поручения.
Процедура ЗаполнитьПоПлатежнымПоручениям()
Перем СписокОплаченных;
Перем ТЗ;
Перем ДокВыписка, ДокПП;
СписокОплаченных = СоздатьОбъект("СписокЗначений");
// Шаг 1: Собираем все платежные поручения, уже включенные в проведенные выписки
ДокВыписка = СоздатьОбъект("Документ.Выписка");
ДокВыписка.ВыбратьДокументы(ДатаДок, ДатаДок);
Пока ДокВыписка.ПолучитьДокумент() = 1 Цикл
Если (ДокВыписка.Проведен() = 1) И (ДокВыписка.ТекущийДокумент() <> ТекущийДокумент()) Тогда
ДокВыписка.ВыбратьСтроки();
Пока ДокВыписка.ПолучитьСтроку() = 1 Цикл
Если ПустоеЗначение(ДокВыписка.ПервичныйДокумент) = 0 Тогда
СписокОплаченных.ДобавитьЗначение(ДокВыписка.ПервичныйДокумент);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
// Шаг 2: Формируем таблицу значений для загрузки в табличную часть
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("ПервичныйДокумент");
ТЗ.НоваяКолонка("ВидДвижения");
ТЗ.НоваяКолонка("НазначениеПлатежа");
ТЗ.НоваяКолонка("КоррСчет");
ТЗ.НоваяКолонка("Расход");
ТЗ.НоваяКолонка("Субконто1");
ТЗ.НоваяКолонка("Субконто2");
ТЗ.НоваяКолонка("БанковскийСчет");
// Шаг 3: Выбираем не проведенные, не удаленные и еще не оплаченные платежные поручения
ДокПП = СоздатьОбъект("Документ.ПлатежноеПоручение");
ДокПП.ВыбратьДокументы(ДатаДок, ДатаДок);
Пока ДокПП.ПолучитьДокумент() = 1 Цикл
// Проверяем пометку удаления конкретного платежного поручения
Если ДокПП.ПометкаУдаления() = 0 Тогда
Если ДокПП.РасчетныйСчет = БанковскийСчет Тогда
// Проверяем, не было ли это ПП оплачено ранее
Если СписокОплаченных.Принадлежит(ДокПП.ТекущийДокумент()) = 0 Тогда
ТЗ.НоваяСтрока();
ТЗ.ПервичныйДокумент = ДокПП.ТекущийДокумент();
ТЗ.ВидДвижения = Перечисление.ВидыДвиженийПоРасчетномуСчету.Оплата;
ТЗ.НазначениеПлатежа = ДокПП.СодержаниеПлатежа;
ТЗ.КоррСчет = ДокПП.Счет;
ТЗ.Расход = ДокПП.Сумма;
ТЗ.Субконто1 = ДокПП.Контрагент;
ТЗ.Субконто2 = ДокПП.Договор;
ТЗ.БанковскийСчет = ДокПП.РасчетныйСчет;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Шаг 4: Загружаем подготовленные данные в текущий документ
ЗагрузитьТабличнуюЧасть(ТЗ);
КонецПроцедуры
Рассмотрим стандартные механизмы 1С 7.7, позволяющие избежать написания собственного кода: