В процессе ведения бухгалтерского учета в конфигурациях на платформе 1С:Предприятие 8 часто возникает ситуация, когда на счетах учета (например, на счетах учета ТМЦ, таких как 10, 41 или 43) зависают некорректные остатки. Типичный пример — «зависание» количественного остатка при нулевой сумме (когда количество равно 1, а сумма равна 0) или наоборот. Такое происходит из-за ручных корректировок, неверной последовательности проведения документов, округлений или ошибок при импорте данных.
Для исправления подобных ошибок программисты часто пишут внешние обработки, которые автоматически находят расхождения и формируют корректирующие проводки. Однако начинающие разработчики сталкиваются с рядом технических сложностей и неочевидных ошибок платформы при попытке программно создать документ ОперацияБух (Операция (бухгалтерский и налоговый учет)). В этой статье мы подробно разберем, как правильно реализовать данный механизм, избежав типовых ловушек, таких как блокировка периода или незаписанные движения.
Рассмотрим ситуацию, когда нам необходимо списать количественный остаток (равный 1) по счету, на котором сумма равна нулю или меньше нуля. Автор темы на форуме попытался решить задачу следующим кодом:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйОстатки.Субконто1,
| ХозрасчетныйОстатки.Субконто2,
| ХозрасчетныйОстатки.Субконто3,
| ХозрасчетныйОстатки.Счет,
| ХозрасчетныйОстатки.Валюта,
| ХозрасчетныйОстатки.НалоговоеНазначение,
| ХозрасчетныйОстатки.СуммаОстаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки КАК ХозрасчетныйОстатки
|ГДЕ
| ХозрасчетныйОстатки.КоличествоОстаток = 1
| И ХозрасчетныйОстатки.СуммаОстаток <= 0";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Док = Документы.ОперацияБух.СоздатьДокумент();
Док.Дата = ТекущаяДата();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
СтрокаДвиж = Док.Движения.Хозрасчетный.Добавить();
СтрокаДвиж.СчетДт = ПланыСчетов.Хозрасчетный.Вспомогательный;
СтрокаДвиж.СчетКт = ВыборкаДетальныеЗаписи.Счет;
БухгалтерскийУчет.УстановитьСубконто(СтрокаДвиж.СчетКт, СтрокаДвиж.СубконтоКт, 1, ВыборкаДетальныеЗаписи.Субконто1);
БухгалтерскийУчет.УстановитьСубконто(СтрокаДвиж.СчетКт, СтрокаДвиж.СубконтоКт, 2, ВыборкаДетальныеЗаписи.Субконто2);
БухгалтерскийУчет.УстановитьСубконто(СтрокаДвиж.СчетКт, СтрокаДвиж.СубконтоКт, 3, ВыборкаДетальныеЗаписи.Subkonto3);
СтрокаДвиж.КоличествоДт = 0;
СтрокаДвиж.КоличествоКт = 1;
КонецЦикла;
Док.Записать(РежимЗаписиДокумента.Запись);
При выполнении этого кода разработчик столкнулся с ошибкой: «Редактирование данных этого периода запрещено. Изменения не могут быть записаны...». Проанализируем, почему возникает эта ошибка и какие еще критические проблемы присутствуют в данном алгоритме.
В любой рабочей базе данных 1С практически всегда установлена «Дата запрета изменения данных». Это необходимо, чтобы пользователи случайно не изменили учетные данные в закрытых периодах (например, в прошлом месяце или году).
Когда мы программно добавляем новую запись в регистр бухгалтерии с помощью метода Добавить(), платформа создает новую строку движения с пустыми значениями реквизитов по умолчанию. Ключевым реквизитом для любого периодического регистра является Период (дата проводки). По умолчанию пустая дата в 1С соответствует значению 01.01.0001 00:00:00 (начало нашей эры).
Поскольку пустая дата гарантированно меньше установленной в системе даты запрета изменения данных, платформа при попытке записи документа считает, что мы пытаемся записать проводку в глубоком прошлом (в запрещенном периоде). Из-за этого срабатывает механизм контроля, и транзакция откатывается с ошибкой.
Решение проблемы: Для каждой добавляемой строки движения необходимо явно указывать период, равный дате создаваемого документа:
СтрокаДвиж.Период = Док.Дата;
Даже если мы решим проблему с периодом, приведенный выше код не выполнит свою задачу до конца. Проанализируем архитектурные ошибки, которые обязательно нужно исправить.
В 1С при работе с коллекциями движений документа (такими как регистры бухгалтерии, накопления или сведений) существует важное правило. Платформа не запишет движения в базу данных, если свойству Записывать коллекции движений явно не присвоить значение Истина. Без этого документ ОперацияБух будет успешно создан и записан, но табличная часть проводок останется абсолютно пустой.
Перед циклом добавления проводок или перед записью документа обязательно нужно указать:
Док.Движения.Хозрасчетный.Записывать = Истина;
Бухгалтерский учет в 1С всегда ведется в разрезе организаций. В запросе автора организация отсутствует. Если в базе данных ведется учет по нескольким юридическим лицам, то остатки будут получены по всем организациям без разбора. При попытке записать проводку без указания организации мы либо получим ошибку проведения, либо создадим «пустые» записи, которые полностью исказят Оборотно-сальдовую ведомость. Необходимо выбирать организацию из остатков и транслировать ее как в шапку документа, так и в каждую проводку:
Док.Организация = ВыборкаДетальныеЗаписи.Организация;СтрокаДвиж.Организация = ВыборкаДетальныеЗаписи.Организация;
В запросе автора стоит условие СуммаОстаток <= 0. Если сумма остатка строго меньше нуля (так называемая «краснота» на счете), то простое списание количества без указания суммы приведет к тому, что количественный остаток закроется, а отрицательная сумма так и останется висеть на счете. Проводка должна не только списывать количество, но и корректировать сумму остатка, чтобы полностью закрыть позицию.
Обратите внимание на опечатку во втором вызове функции УстановитьСубконто в коде автора из сообщения №7. Вместо определенной переменной СтрокаДвиж там ошибочно используется сТРОКАдВИЖ.СубконтоКт. При выполнении такого кода программа выдаст ошибку компиляции: «Переменная не определена (сТРОКАдВИЖ)».
Учитывая все архитектурные требования платформы 1С:Предприятие 8, разработаем правильный и надежный алгоритм. Мы будем группировать остатки по организациям, чтобы для каждой организации создавался свой отдельный документ ОперацияБух. Это позволит сохранить логическую целостность учета.
Рассмотрим по шагам готовый код, который можно использовать во внешней обработке:
// Шаг 1. Формируем запрос для получения некорректных остатков
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйОстатки.Организация КАК Организация,
| ХозрасчетныйОстатки.Счет КАК Счет,
| ХозрасчетныйОстатки.Субконто1 КАК Субконто1,
| ХозрасчетныйОстатки.Субконто2 КАК Субконто2,
| ХозрасчетныйОстатки.Субконто3 КАК Субконто3,
| ХозрасчетныйОстатки.СуммаОстаток КАК СуммаОстаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(&ПериодГраница, , , ) КАК ХозрасчетныйОстатки
|ГДЕ
| ХозрасчетныйОстатки.КоличествоОстаток = 1
| И ХозрасчетныйОстатки.СуммаОстаток <= 0";
// Задаем границу периода, чтобы получить актуальные остатки на текущий момент
ГраницаОстатков = Новый Граница(ТекущаяДата(), ВидГраницы.Включая);
Запрос.УстановитьПараметр("ПериодГраница", ГраницаОстатков);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Сообщить("Некорректных остатков для корректировки не обнаружено.");
Возврат;
КонецЕсли;
// Шаг 2. Обходим выборку результатов запроса
Выборка = РезультатЗапроса.Выбрать();
// Для оптимизации и порядка будем группировать документы по организациям.
// Будем использовать структуру для хранения созданных документов по Организациям.
СозданныеДокументы = Новый Соответствие;
Пока Выборка.Следующий() Цикл
// Получаем или создаем документ "ОперацияБух" для текущей организации
ДокОперация = СозданныеДокументы.Получить(Выборка.Организация);
Если ДокОперация = Неопределено Тогда
// Создаем новый документ
ДокОперация = Документы.ОперацияБух.СоздатьДокумент();
ДокОперация.Дата = ТекущаяДата();
ДокОперация.Организация = Выборка.Организация;
ДокОперация.Содержание = "Автоматическая корректировка нулевых количественных остатков";
// ВАЖНО: Включаем признак записи движений по регистру бухгалтерии
ДокОперация.Движения.Хозрасчетный.Записывать = Истина;
// Сохраняем ссылку на создаваемый объект в соответствие
СозданныеДокументы.Вставить(Выборка.Организация, ДокОперация);
КонецЕсли;
// Шаг 3. Добавляем новую проводку в документ
НоваяПроводка = ДокОперация.Движения.Хозрасчетный.Добавить();
// Заполняем обязательные системные реквизиты проводки
НоваяПроводка.Период = ДокОперация.Дата; // Защита от ошибки даты запрета изменения данных
НоваяПроводка.Организация = ДокОперация.Организация;
// Настраиваем счета дебета и кредита (делаем реверс остатка)
// Списываем количество по Кредиту корректируемого счета через Вспомогательный счет
НоваяПроводка.СчетДт = ПланыСчетов.Хозрасчетный.Вспомогательный;
НоваяПроводка.СчетКт = Выборка.Счет;
// Заполняем аналитику (Субконто) Кредита с помощью типового метода БСП
БухгалтерскийУчет.УстановитьСубконто(НоваяПроводка.СчетКт, НоваяПроводка.СубконтоКт, 1, Выборка.Субконто1);
БухгалтерскийУчет.УстановитьСубконто(НоваяПроводка.СчетКт, НоваяПроводка.СубконтоКт, 2, Выборка.Субконто2);
БухгалтерскийУчет.УстановитьСубконто(НоваяПроводка.СчетКт, НоваяПроводка.СубконтоКт, 3, Выборка.Субконто3);
// Заполняем количественные показатели
НоваяПроводка.КоличествоДт = 0;
НоваяПроводка.КоличествоКт = 1; // Списываем зависшую 1 единицу
// Шаг 4. Если на счете висела отрицательная сумма ("краснота"), мы должны её списать
Если Выборка.СуммаОстаток < 0 Тогда
// Отрицательная сумма в кредите активного счета увеличит его дебет,
// поэтому мы переносим её как отрицательное сторно
НоваяПроводка.Сумма = Выборка.СуммаОстаток;
Иначе
НоваяПроводка.Сумма = 0;
КонецЕсли;
КонецЦикла;
// Шаг 5. Записываем все созданные документы в базу данных
Для Каждого КлючИЗначение Из СозданныеДокументы Цикл
ТекущийДокумент = КлючИЗначение.Значение;
Попытка
ТекущийДокумент.Записать(РежимЗаписиДокумента.Запись);
Сообщить("Успешно создан и записан документ " + ТекущийДокумент.Ссылка + " по организации: " + ТекущийДокумент.Организация);
Исключение
ВызватьИсключение "Ошибка при записи документа корректировки: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
КонецЦикла;
Изучив предложенный вариант реализации программы, можно выделить несколько преимуществ перед стандартным подходом новичков:
Попытка ... Исключение гарантирует, что при возникновении непредвиденной ошибки в базе данных транзакция будет корректно прервана, а разработчик получит понятное системное сообщение.ОперацияБух на каждое юридическое лицо. Это снижает нагрузку на базу данных и упрощает последующий аудит документов пользователями.БухгалтерскийУчет.УстановитьСубконто гарантирует правильное заполнение субконто в соответствии с типами данных, определенными в плане счетов конкретной конфигурации.НоваяПроводка.Период = ДокОперация.Дата; исключается возникновение ошибок, связанных с датами запрета редактирования данных.Таким образом, мы детально разобрали особенности работы с регистром бухгалтерии в 1С при программном создании ручных операций, исправили типичные архитектурные ошибки начинающих разработчиков и подготовили надежное решение для исправления количественно-суммовых расхождений на счетах учета.