В «1С:Комплексной конфигурации 7.7» (которая объединяет контуры торгового и бухгалтерского учета) разработчики и системные администраторы часто сталкиваются с неприятной проблемой: при взаиморасчетах с контрагентами данные в различных отчетах и формах документов начинают «рваться». Бухгалтерия видит закрытый долг в оборотно-сальдовой ведомости и актах сверки, а менеджеры по продажам при выписке расходных накладных продолжают видеть старую задолженность покупателя.
Давайте подробно разберем, почему возникает этот архитектурный конфликт, проанализируем типичные ошибки при попытке его решения и рассмотрим три надежных способа исправления ситуации, проверенных многолетней практикой сообщества 1С.
Для начала проанализируем ситуацию изнутри программной логики платформы 1С 7.7. В Комплексной конфигурации учет взаиморасчетов ведется параллельно в двух независимых плоскостях:
Регистр.Покупатели, Регистр.Поставщики или обобщенному регистру взаиморасчетов). Именно к этим регистрам обращается глобальная функция глТекущийДолг(). Она использует быстрый метод получения остатков СводныйОстаток, что обеспечивает мгновенный вывод информации при выборе контрагента в документах.Документ «Бухгалтерская справка» является исключительно бухгалтерским инструментом. Он создает проводки напрямую в журнал проводок, но не имеет встроенных алгоритмов проведения по регистрам оперативного учета. Когда бухгалтер списывает долг или делает перенос задолженности этим документом, сальдо по счету 62 меняется, а вот в оперативном регистре долг зависает без изменений. Функция глТекущийДолг() обращается к регистру и возвращает неактуальные данные.
У начинающих разработчиков часто возникает соблазн переписать глобальную функцию глТекущийДолг() так, чтобы она получала остатки напрямую из бухгалтерского контура. Рассмотрим типичный пример такого кода:
Ит = СоздатьОбъект("БухгалтерскиеИтоги");
Ит.ИспользоватьРазделительУчета(Фирм.ЮрЛицо);
Ит.ИспользоватьСубконто("Контрагенты", ТКонтрагент, 2,);
Ит.ВыполнитьЗапрос(ДатаДок, ДатаДок, СчетПоКоду("62"), , , 1,,"С");
Долг = Ит.СКД()-Ит.СКК();
Давайте разберем по шагам, почему этот подход является опасным и категорически не рекомендуется к использованию в рабочих базах данных:
СводныйОстаток() работает мгновенно, так как платформа хранит уже рассчитанные итоги на конец каждого месяца. Объект БухгалтерскиеИтоги при вызове метода ВыполнитьЗапрос() на конкретную дату вынужден динамически рассчитывать все проводки с начала периода. Если этот код будет срабатывать при открытии формы каждого торгового документа или при выборе контрагента, многопользовательская база данных начнет зависать и блокироваться.СКД() - СКК() по родителю счета «62» вернет свернутое сальдо. Но в реальном учете у контрагента может висеть долг по одному договору (счет 62.1) и аванс по другому договору (счет 62.2). Оперативный учет должен видеть эти суммы раздельно, а прямой запрос к БУ исказит картину.Посмотрим на действительно правильные варианты решения этой проблемы, которые не ломают логику системы.
Самый простой и методологически верный способ — запретить бухгалтерам использовать «Бухгалтерскую справку» для корректировки расчетов с контрагентами. Для этих целей в Комплексной конфигурации 7.7 предназначен специализированный документ «Корректировка долга» (или аналогичные ему документы взаимозачета).
Документ «Корректировка долга» спроектирован так, что при проведении он одновременно делает движения по регистрам оперативного учета и формирует необходимые бухгалтерские проводки по счетам 60 и 62. Научите бухгалтеров работать с этим документом, и рассинхронизация контуров учета прекратится.
Если слова и инструкции на бухгалтеров не действуют, применим жесткое программное ограничение. Мы можем доработать модуль документа «Бухгалтерская справка» так, чтобы программа физически не позволяла проводить документ, если в его проводках задействованы счета расчетов с контрагентами.
Для реализации этого метода откроем конфигуратор, найдем документ БухгалтерскаяСправка и в его модуле проведения (процедура ОбработкаПроведения или предопределенная процедура модуля документа ПриЗаписи) добавим следующий алгоритм контроля:
// Алгоритм проверки счетов взаиморасчетов при проведении Бухгалтерской справки
ВыбратьСтроки();
Пока ПолучитьСтроку() = 1 Цикл
Если (СчетДт.Выбран() = 1) ИЛИ (СчетКт.Выбран() = 1) Тогда
// Проверяем, не относятся ли счета к группе 60, 62 или 76
Если (СчетДт.ПринадлежитГруппе(СчетПоКоду("60")) = 1) ИЛИ
(СчетДт.ПринадлежитГруппе(СчетПоКоду("62")) = 1) ИЛИ
(СчетКт.ПринадлежитГруппе(СчетПоКоду("60")) = 1) ИЛИ
(СчетКт.ПринадлежитГруппе(СчетПоКоду("62")) = 1) Тогда
Сообщить("Ошибка проведения документа! Счета 60, 62 нельзя использовать в Бухгалтерской справке!", "!");
Сообщить("Для корректировки расчетов с контрагентами используйте документ 'Корректировка долга'.", "i");
СтатусВозврата(0);
Возврат;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Этот простой шаг спасет базу данных от появления скрытых расхождений, так как пользователи будут технически лишены возможности вносить несогласованные правки в обход оперативного учета.
Если бизнес-процессы вашей компании требуют регулярного использования именно ручных операций для сложных корректировок взаиморасчетов, нам придется доработать проведение «Бухгалтерской справки», чтобы она транслировала свои изменения в оперативные регистры.
Проанализируем ситуацию: чтобы сделать движение по оперативному регистру взаиморасчетов, программе недостаточно знать только контрагента и сумму. Ей необходима полная аналитика: Договор (или сделка) и Кредитный документ (если ведется учет по заказам/счетам). В «Бухгалтерской справке» на счетах 60 и 62 эта аналитика присутствует в виде субконто.
В процедуру проведения «Бухгалтерской справки» мы можем встроить алгоритм автоматического формирования движений по регистрам. Ниже приведен пример реализации логики для регистра Покупатели:
// Фрагмент кода проведения Бухгалтерской справки по оперативному регистру
РегПокупатели = СоздатьОбъект("Регистр.Покупатели");
ВыбратьСтроки();
Пока ПолучитьСтроку() = 1 Цикл
// Проверяем проводки по дебету счета 62 (увеличение долга покупателя)
Если СчетДт = СчетПоКоду("62.1") Тогда
// Извлекаем аналитику из субконто проводки
ТекКонтрагент = СубконтоДт1; // Первое субконто - Контрагент
ТекДоговор = СубконтоДт2; // Второе субконто - Договор
Если (ПустоеЗначение(ТекКонтрагент) = 0) И (ПустоеЗначение(ТекДоговор) = 0) Тогда
РегПокупатели.Фирма = Фирма; // Юрлицо документа
РегПокупатели.Договор = ТекДоговор;
РегПокупатели.Сделка = ТекДоговор.ОсновнойДоговор; // или иная логика определения сделки
РегПокупатели.СуммаВал = Сумма;
РегПокупатели.СуммаУпр = Сумма; // Пересчет по курсу при необходимости
// Выполняем движение "Приход" по регистру
РегПокупатели.ДвижениеПриходВыполнить();
КонецЕсли;
// Проверяем проводки по кредиту счета 62 (уменьшение долга покупателя/оплата)
ИначеЕсли СчетКт = СчетПоКоду("62.1") Тогда
ТекКонтрагент = СубконтоКт1;
ТекДоговор = СубконтоКт2;
Если (ПустоеЗначение(ТекКонтрагент) = 0) И (ПустоеЗначение(ТекДоговор) = 0) Тогда
РегПокупатели.Фирма = Фирма;
РегПокупатели.Договор = ТекДоговор;
РегПокупатели.Сделка = ТекДоговор.ОсновнойДоговор;
РегПокупатели.СуммаВал = Сумма;
РегПокупатели.СуммаУпр = Сумма;
// Выполняем движение "Расход" по регистру
РегПокупатели.ДвижениеРасходВыполнить();
КонецЕсли;
КонецЕсли;
КонецЦикла;
Важное примечание: При выборе этого пути будьте предельно внимательны к заполнению субконто в Бухгалтерской справке. Если бухгалтер забудет указать Договор во втором субконто, движение по регистру сформируется с пустым измерением, что приведет к зависанию остатков по аналитике «Без договора» внутри оперативного учета.
Мы проанализировали три различных подхода к устранению рассинхронизации взаиморасчетов в «1С:Комплексной 7.7». Оптимальным решением с точки зрения надежности и производительности системы является **Решение №1** (использование штатного документа «Корректировка долга») в сочетании с жестким программным ограничением из **Решения №2**. Это позволит сохранить высокую скорость работы базы данных и полностью исключить человеческий фактор.