Как эффективно работать с Электронными Счетами-Фактурами (ЭСЧФ) в 1С 7.7 и 8.x в Беларуси?

Программист 1С v8.3 (Обычные формы) Налоговый учет
← На главную

С 1 июля 2016 года электронные счета-фактуры (ЭСЧФ) стали обязательными для всех плательщиков НДС в Республике Беларусь. Это вызвало значительные трудности у организаций, использующих 1С, поскольку требовалась глубокая интеграция учетных систем с Порталом ЭСЧФ. Мы подробно разберем, как настроить работу с ЭСЧФ в 1С версий 7.7 и 8.x, рассмотрим типичные проблемы и предложим готовые решения.

Для успешной работы с ЭСЧФ необходимо внимательно изучать документацию от разработчиков, доступную в установочных файлах eInvVat, а также на официальном сайте Портала ЭСЧФ (vat.gov.by). Важно следить за обновлениями XSD-схем, которые описывают структуру XML-документов, так как портал может периодически менять их формат без предварительного уведомления.

Формирование исходящих ЭСЧФ из 1С

Создание исходящих ЭСЧФ — ключевой этап интеграции. Рассмотрим несколько важных аспектов.

Правила нумерации ЭСЧФ

Каждый ЭСЧФ имеет уникальный номер, который формируется по строгому принципу:

  1. Первые 9 цифр — это УНП пользователя, который составляет ЭСЧФ.
  2. Следующие 4 цифры — год составления ЭСЧФ.
  3. Последние 10 цифр — свободный порядковый номер с учетом лидирующих нулей. Для каждого УНП нумерация сбрасывается в начале года.

Например: 999999999-2016-0000000001.

Особенности форматирования данных для XML

При формировании XML-файлов для отправки на портал ЭСЧФ могут возникать ошибки, связанные с некорректным форматированием данных. Особое внимание следует уделить датам и числовым значениям.

Форматирование даты

Портал требует формат даты ГГГГ-ММ-ДД (например, 2016-04-19). Для обеспечения этого формата в 1С 7.7 и 8.x используйте следующие методы:

Для 1С 7.7:

В 1С 7.7 необходимо вручную добавлять лидирующие нули для месяца и числа, если они меньше 10:


ДатаГод(Док.ДатаДок) + "-" + ?(ДатаМесяц(Док.ДатаДок) < 10, "0", "") + ДатаМесяц(Док.ДатаДок) + "-" + ?(ДатаЧисло(Док.ДатаДок) < 10, "0", "") + ДатаЧисло(Док.ДатаДок);

Или используя функцию Прав():


ДатаГод(Док.ДатаДок) + "-" + Прав("00" + ДатаМесяц(Док.ДатаДок), 2) + "-" + Прав("00" + ДатаЧисло(Док.ДатаДок), 2);
Для 1С 8.x:

В 1С 8.x можно использовать функцию Формат() с соответствующей строкой формата:

ДатаСтрокой = Формат(Дата, "ДФ=yyyy-MM-dd"); // Пример: "2016-04-19"

Для отдельных частей даты:


ДатаГод(Док.ДатаДок) + "-" + Формат(ДатаМесяц(Док.ДатаДок), "Ч(0)2.0") + "-" + Формат(ДатаЧисло(Док.ДатаДок), "Ч(0)2.0");
Точность числовых значений

Портал ЭСЧФ очень чувствителен к точности числовых значений, особенно в полях, таких как totalCostVat. Были случаи, когда портал принимал только 3 знака после запятой. При автоматической выгрузке из 1С убедитесь, что ваши числовые поля округляются до нужной точности перед записью в XML.


// Пример округления до 3 знаков после запятой в 1С 8.х
СуммаОкругленная = Окр(Сумма, 3);
Проблема с полем dateIssuance

Иногда при выгрузке ЭСЧФ из 1С и последующей загрузке на портал вручную возникала ошибка, связанная с полем dateIssuance. Опыт показывает, что это поле может быть не нужно в выгружаемом файле, так как портал добавляет его автоматически при приеме ЭСЧФ. Если вы сталкиваетесь с такой ошибкой, попробуйте временно закомментировать или удалить это поле из формируемого XML.

Различия в работе с XML-объектами между 1С 7.7 и 8.x

При адаптации кода для работы с XML между версиями 1С 7.7 и 8.x часто возникают проблемы из-за различий в объектных моделях. Например, код, который без проблем работает в 1С 7.7, может выдавать ошибку Значение не является значением объектного типа (Document) в 1С 8.x.

Это связано с тем, что в 1С 8.x объект InvVatXml (предположительно, результат работы компоненты EVatService) может возвращать другой тип данных или иметь другую структуру для доступа к элементам XML. Например, в 1С 7.7 прямой доступ через InvVatXml.Document.GetXmlNodeValue(...) мог быть корректным, тогда как в 1С 8.x для парсинга XML-строки может потребоваться использование других механизмов, таких как ЧтениеXML или ДокументXML.

Решение: В 1С 8.x для работы с XML-документом, полученным от веб-сервиса, скорее всего, потребуется сначала преобразовать его в объект ДокументXML или использовать методы, специфичные для текущей реализации объекта InvVatXml в вашей обработке. Если EVatService.GetEDoc() возвращает XML в виде строки, его нужно сначала прочитать:


// Пример для 1С 8.х
InvVatXmlСтрока = EVatService.GetEDoc(InvVatNumber); // Предполагаем, что возвращается XML-строка
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(InvVatXmlСтрока);
ДокументXML = Новый ДокументXML;
ДокументXML.Прочитать(ЧтениеXML);
// Теперь используйте методы ДокументXML для доступа к узлам
УНП = ДокументXML.ПолучитьЭлементПоИмени("issuance/provider/unp").ТекстовоеСодержимое;

Всегда используйте отладчик для проверки типа и структуры объекта, возвращаемого внешними компонентами или веб-сервисами.

Парсинг и обработка входящих ЭСЧФ

Получение и обработка входящих ЭСЧФ также требует внимания, особенно при работе с данными, предоставленными порталом в формате CSV.

Изменения в формате CSV-файлов

Портал ЭСЧФ неоднократно менял формат файлов камерального контроля CSV. В частности, разделитель может меняться с точки с запятой (;) на символ с кодом 1 (Символ(1)). Кроме того, структура файла может содержать пустые строки или метаданные в первых строках.

Важно: Всегда начинайте чтение данных из CSV-файла с 3-й или 4-й строки, чтобы пропустить шапку и потенциально пустые строки.

Предлагаем универсальный код для парсинга CSV-файлов, который адаптируется к различным разделителям и именам колонок:


Функция РазложитьСтрокуВМассивПодстрок(Знач Строка, Знач Разделитель = ",", Знач ПропускатьПустыеСтроки = Неопределено)
    Результат = Новый Массив;
    Если ПропускатьПустыеСтроки = Неопределено Тогда
        ПропускатьПустыеСтроки = ?(Разделитель = " ", Истина, Ложь);
        Если ПустаяСтрока(Строка) Тогда
            Если Разделитель = " " Тогда
                Результат.Добавить("");
            КонецЕсли;
            Возврат Результат;
        КонецЕсли;
    КонецЕсли;
    Позиция = Найти(Строка, Разделитель);
    Пока Позиция > 0 Цикл
        Подстрока = Лев(Строка, Позиция - 1);
        Если Не ПропускатьПустыеСтроки Или Не ПустаяСтрока(Подстрока) Тогда
            Результат.Добавить(Подстрока);
        КонецЕсли;
        Строка = Сред(Строка, Позиция + СтрДлина(Разделитель));
        Позиция = Найти(Строка, Разделитель);
    КонецЦикла;
    Если Не ПропускатьПустыеСтроки Или Не ПустаяСтрока(Строка) Тогда
        Результат.Добавить(Строка);
    КонецЕсли;
    Возврат Результат;
КонецФункции

Процедура СравнитьСБазойНажатие(Элемент)
    перем Разделитель;
    Таблица = Новый ТаблицаЗначений;
    Таблица.Очистить();
    Таблица.Колонки.Очистить();
    // Предполагается наличие элемента формы "Таблица"
    // ЭлементыФормы.Таблица.Колонки.Очистить(); 

    ЗагружаемыйФайл = Новый ТекстовыйДокумент;
    ЗагружаемыйФайл.Прочитать(ВыбФайл); // ВыбФайл - переменная с путем к файлу

    // Шапка по умолчанию - третья строчка. Если портал меняет, нужно скорректировать.
    Шапка = ЗагружаемыйФайл.ПолучитьСтроку(3); 
    Разделитель = ";"; // Или Символ(1), если портал изменил

    МассивКол = РазложитьСтрокуВМассивПодстрок(Шапка, Разделитель);

    Для Каждого ИмяСтолбца Из МассивКол Цикл
        ИмяБезПробелов = СтрЗаменить(ИмяСтолбца, " ", "");
        ИмяБезПробелов = СтрЗаменить(ИмяБезПробелов, """", "");
        ИмяБезПробелов = СтрЗаменить(ИмяБезПробелов, "№", "Номер");
        ИмяБезПробелов = СтрЗаменить(ИмяБезПробелов, "(", "");
        ИмяБезПробелов = СтрЗаменить(ИмяБезПробелов, ")", "");
        ИмяБезПробелов = СтрЗаменить(ИмяБезПробелов, "–", "");
        Таблица.Колонки.Добавить(ИмяБезПробелов, , ИмяСтолбца);
        // Если используете элемент формы ТаблицаЗначений:
        // НовыйСтолбец = ЭлементыФормы.Таблица.Колонки.Добавить(ИмяБезПробелов, ИмяСтолбца);
        // НовыйСтолбец.Данные = ИмяБезПробелов;
    КонецЦикла;

    Для НомерСтроки = 4 По ЗагружаемыйФайл.КоличествоСтрок Цикл
        // Состояние("Обрабатывается " + Строка(Формат(?(ЗагружаемыйФайл.КоличествоСтрок=0,0,((100*НомерСтроки)/ЗагружаемыйФайл.КоличествоСтрок)),"ЧЦ=3; ЧДЦ=0"))+" %");
        // ОбработкаПрерыванияПользователя;

        Строка = ЗагружаемыйФайл.ПолучитьСтроку(НомерСтроки);
        МассивКол = РазложитьСтрокуВМассивПодстрок(Строка, Разделитель);

        НоваяСтрочка = Таблица.Добавить();
        Если МассивКол.Количество <> Таблица.Колонки.Количество Тогда
            Продолжить; // Вероятно, в тексте содержится разделитель, пропускаем строку
        КонецЕсли;

        Для НомерСтолбца = 1 По МассивКол.Количество Цикл
            ТекущееЗначение = МассивКол[НомерСтолбца - 1];
            ИмяКолонки = Таблица.Колонки[НомерСтолбца - 1].Имя;
            НоваяСтрочка[ИмяКолонки] = ТекущееЗначение;
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

Ошибки при получении входящих ЭСЧФ (индексация)

При получении списков входящих ЭСЧФ через веб-сервис EVatService может возникать ошибка Плохой тип переменной, особенно если используется цикл Для i = 1 по InvList.Count Цикл. В большинстве объектных моделей 1С (как 7.7, так и 8.x) коллекции индексируются с нуля.

Решение: Используйте цикл, начинающийся с 0 и до Count - 1:


InvList = EVatService.GetList(ДатаНачала);
Для i = 0 По InvList.Count - 1 Цикл
    InvVatNumber = InvList.GetItemAttribute(i, "document/number");
    InvVatXml = EVatService.GetEDoc(InvVatNumber);
    // Дальнейшая обработка ЭСЧФ
КонецЦикла;

Ошибки валидации XML (тэг description)

Были случаи, когда от контрагентов поступали ЭСЧФ без обязательного тега description, хотя в схеме он прописан как обязательный. Это вызывало ошибки валидации при попытке загрузить такой XML. Если ваша 1С не пропускает такие файлы, а контрагент выставляет их успешно, это может указывать на то, что портал иногда нестрого относится к валидации или использует устаревшую схему. Рекомендуется связаться с контрагентом для исправления ошибки и убедиться, что он формирует ЭСЧФ согласно актуальным требованиям. Если проблема сохраняется, обратитесь в техподдержку портала.

Работа с ЭЦП и сертификатами (СОС)

Для корректной работы с ЭСЧФ необходимо регулярно обновлять списки отозванных сертификатов (СОС). Без актуальных СОС электронная подпись может не проверяться, что приведет к ошибкам при отправке и получении ЭСЧФ.

Автоматическое обновление СОС из 1С

Мы предлагаем примеры кода для автоматического обновления СОС, используя внешние утилиты wget и AvCmUt3.exe, которые входят в состав программного обеспечения Авест.

Обновление СОС для 1С 7.7:

Процедура ОбновитьСОС()
    // Удаляем старые СОС во временной папке
    КомандаСистемы("del %TEMP%\\*.crl >NUL: 2>&1");

    // Пути к утилитам Авест (могут отличаться в зависимости от установки)
    // Для 32-х битных систем, часто: "c:\\Program Files\\Avest\\AvPCM_MNS\\"
    // Для 64-х битных систем, часто: "c:\\Program Files (x86)\\Avest\\AvPCM_MNS\\"
    // Убедитесь, что пути указаны верно и утилиты доступны.
    // Примерный путь:
    ПутьК_Avest = "c:\\Program Files\\Avest\\AvPCM_MNS\\"; 

    скачатьФайл = """"+ ПутьК_Avest + "wget"" -q -T 30 -O %TEMP%\\";
    импортСОС = """"+ ПутьК_Avest + "AvCmUt3.exe"" -C %TEMP%\\";

    // Учет прокси, если требуется (PX_USER, PX_PASS должны быть настроены в системных переменных)
    сайтФайлаМНС = " %PX_USER% %PX_PASS% http:" + Симв(47) + Симв(47) + "www.portal.nalog.gov.by/ca/";
    сайтФайлаПКИ = " %PX_USER% %PX_PASS% http:" + Симв(47) + Симв(47) + "pki.gov.by/certs/-/";
    выводВНоль = " >NUL";

    // Загрузка СОС Корневого Удостоверяющего центра Министерства по налогам и сборам
    имяФайла = "mns-ca.crl";
    ВыполненоУспешно = 0;
    Попытка
        КомандаСистемы(скачатьФайл + имяФайла + сайтФайлаМНС + имяФайла);
        ВыполненоУспешно = 1;
    Исключение
        Сообщить("СОС " + имяФайла + " - не скачан !!!");
    КонецПопытки;
    Если ВыполненоУспешно = 1 Тогда
        КомандаСистемы(импортСОС + имяФайла + выводВНоль);
    КонецЕсли;

    // Загрузка СОС Корневого УЦ РУП "Информационно-издательский центр по налогам и сборам"
    имяФайла = "rup.crl";
    ВыполненоУспешно = 0;
    Попытка
        КомандаСистемы(скачатьФайл + имяФайла + сайтФайлаМНС + имяФайла);
        ВыполненоУспешно = 1;
    Исключение
        Сообщить("СОС " + имяФайла + " - не скачан !!!");
    КонецПопытки;
    Если ВыполненоУспешно = 1 Тогда
        КомандаСистемы(импортСОС + имяФайла + выводВНоль);
    КонецЕсли;

    // Загрузка СОС Корневого удостоверяющего центра (КУЦ)
    имяФайла = "kuc.crl";
    ВыполненоУспешно = 0;
    Попытка
        КомандаСистемы(скачатьФайл + имяФайла + сайтФайлаПКИ + имяФайла);
        ВыполненоУспешно = 1;
    Исключение
        Сообщить("СОС " + имяФайла + " - не скачан !!!");
    КонецПопытки;
    Если ВыполненоУспешно = 1 Тогда
        КомандаСистемы(импортСОС + имяФайла + выводВНоль);
    КонецЕсли;

    // Загрузка СОС Республиканского удостоверяющего центра (РУЦ)
    имяФайла = "ruc.crl";
    ВыполненоУспешно = 0;
    Попытка
        КомандаСистемы(скачатьФайл + имяФайла + сайтФайлаПКИ + имяФайла);
        ВыполненоУспешно = 1;
    Исключение
        Сообщить("СОС " + имяФайла + " - не скачан !!!");
    КонецПопытки;
    Если ВыполненоУспешно = 1 Тогда
        КомандаСистемы(импортСОС + имяФайла + выводВНоль);
    КонецЕсли;

    // Удаляем лог-файл (если он генерируется утилитой AvCmUt3.exe)
    // Убедитесь, что каталогИБ указан верно
    // КомандаСистемы("del " + СокрЛП(КаталогИБ) + "AvCmUt3.log"); 
КонецПроцедуры
Обновление СОС для 1С 8.x:

В 1С 8.x можно использовать аналогичный подход, добавив определение разрядности операционной системы для правильного выбора пути к утилитам Авест:


Перем Рез; // Разрядность ОС
Перем ИМНС_СОС_Выбран; // Флаг, выбран ли сертификат ИМНС
Перем НЦЭУ_СОС_Выбран; // Флаг, выбран ли сертификат НЦЭУ

// Функция для определения разрядности системы
Функция ОпределитьРазрядностьОС()
    WshShell = Новый COMОбъект("WScript.Shell");
    Возврат ?(Найти(WshShell.Environment("SYSTEM").item("PROCESSOR_ARCHITECTURE"), "64") > 0, 64, 32);
КонецФункции

Процедура ОбновитьСОС_8х(ПутьАвест)
    // Удаляем старые СОС во временной папке
    КомандаСистемы("del %TEMP%\\*.crl >NUL: 2>&1");

    скачатьФайл = ПутьАвест + """wget"" -q -T 30 -O %TEMP%\\";
    импортСОС = ПутьАвест + """AvCmUt3.exe"" -C %TEMP%\\";

    // Учет прокси, если требуется (PX_USER, PX_PASS должны быть настроены в системных переменных)
    сайтФайлаМНС = " %PX_USER% %PX_PASS% http:" + Симв(47) + Симв(47) + "www.portal.nalog.gov.by/ca/";
    сайтФайлаПКИ = " %PX_USER% %PX_PASS% http:" + Симв(47) + Симв(47) + "pki.gov.by/certs/-/";
    выводВНоль = " >NUL";

    // Загрузка СОС Корневого Удостоверяющего центра Министерства по налогам и сборам
    имяФайла = "mns-ca.crl";
    ВыполненоУспешно = 0;
    Попытка
        КомандаСистемы(скачатьФайл + имяФайла + сайтФайлаМНС + имяФайла);
        ВыполненоУспешно = 1;
    Исключение
        Сообщить("СОС " + имяФайла + " - не скачан !!!");
    КонецПопытки;
    Если ВыполненоУспешно = 1 Тогда
        КомандаСистемы(импортСОС + имяФайла + выводВНоль);
    КонецЕсли;

    // Загрузка СОС Корневого УЦ РУП "Информационно-издательский центр по налогам и сборам"
    имяФайла = "rup.crl";
    ВыполненоУспешно = 0;
    Попытка
        КомандаСистемы(скачатьФайл + имяФайла + сайтФайлаМНС + имяФайла);
        ВыполненоУспешно = 1;
    Исключение
        Сообщить("СОС " + имяФайла + " - не скачан !!!");
    КонецПопытки;
    Если ВыполненоУспешно = 1 Тогда
        КомандаСистемы(импортСОС + имяФайла + выводВНоль);
    КонецЕсли;

    // Загрузка СОС Корневого удостоверяющего центра (КУЦ)
    имяФайла = "kuc.crl";
    ВыполненоУспешно = 0;
    Попытка
        КомандаСистемы(скачатьФайл + имяФайла + сайтФайлаПКИ + имяФайла);
        ВыполненоУспешно = 1;
    Исключение
        Сообщить("СОС " + имяФайла + " - не скачан !!!");
    КонецПопытки;
    Если ВыполненоУспешно = 1 Тогда
        КомандаСистемы(импортСОС + имяФайла + выводВНоль);
    КонецЕсли;

    // Загрузка СОС Республиканского удостоверяющего центра (РУЦ)
    имяФайла = "ruc.crl";
    ВыполненоУспешно = 0;
    Попытка
        КомандаСистемы(скачатьФайл + имяФайла + сайтФайлаПКИ + имяФайла);
        ВыполненоУспешно = 1;
    Исключение
        Сообщить("СОС " + имяФайла + " - не скачан !!!");
    КонецПопытки;
    Если ВыполненоУспешно = 1 Тогда
        КомандаСистемы(импортСОС + имяФайла + выводВНоль);
    КонецЕсли;

    // Удаляем лог-файл
    КомандаСистемы("del " + СокрЛП(ПутьАвест) + "AvCmUt3.log");
КонецПроцедуры

Процедура ОбновитьСОС(Кнопка)
    Рез = ОпределитьРазрядностьОС(); // Определяем разрядность ОС

    Если ИМНС_СОС_Выбран Тогда // Предполагаем, что это флажок на форме
        Если Рез = 64 Тогда
            ПутьАвест = """C:\Program Files (x86)\Avest\AvPCM_MNS\""";
        Иначе
            ПутьАвест = """C:\Program Files\Avest\AvPCM_MNS\""";
        КонецЕсли;
        ОбновитьСОС_8х(ПутьАвест);
    ИначеЕсли НЦЭУ_СОС_Выбран Тогда // Предполагаем, что это флажок на форме
        Если Рез = 64 Тогда
            ПутьАвест = """C:\Program Files (x86)\Avest\AvPCM_nces\""";
        Иначе
            ПутьАвест = """C:\Program Files\Avest\AvPCM_nces\""";
        КонецЕсли;
        ОбновитьСОС_8х(ПутьАвест);
    Иначе
        Предупреждение("Выберите вид сертификата (ИМНС или НЦЭУ).");
    КонецЕсли;
КонецПроцедуры

Важно: Убедитесь, что пути к утилитам wget и AvCmUt3.exe (например, c:\Program Files\Avest\AvPCM_MNS\ или c:\Program Files (x86)\Avest\AvPCM_nces\) указаны корректно для вашей системы.

Существуют разные бат-файлы для обновления СОС. Например, get_crl.bat из папки c:\Program Files (x86)\Avest\AvPCM_nces\ может работать иначе, чем аналогичный файл из c:\Program Files (x86)\Avest\AvPCM_MNS\. Если стандартный бат-файл не работает, используйте приведенный код.

Работа с договорами и контрагентами

Корректное заполнение данных о контрагентах и договорах является залогом успешной отправки ЭСЧФ.

Извлечение номера договора из строки

Часто номер договора хранится в наименовании, например, "договор №12345 от 01.01.2015". Для извлечения номера можно использовать строковые функции 1С:


// Предполагаем, что наименование договора находится в Док.Договор.Наименование
ИсходнаяСтрока = Док.Договор.Наименование;

// Найдем позицию слова "договор" и возьмем часть строки после него
ПозицияДоговор = Найти(ИсходнаяСтрока, "договор");
Если ПозицияДоговор > 0 Тогда
    СтрокаПослеДоговора = Сред(ИсходнаяСтрока, ПозицияДоговор + СтрДлина("договор")); // Получим " №12345 от 01.01.2015"

    // Найдем позицию слова "от" и возьмем левую часть
    ПозицияОт = Найти(СтрокаПослеДоговора, "от");
    Если ПозицияОт > 0 Тогда
        НомерДоговораСимволы = Лев(СтрокаПослеДоговора, ПозицияОт - 1); // Получим " №12345 "

        // Удалим символ № и лишние пробелы
        НомерДоговораЧистый = СтрЗаменить(НомерДоговораСимволы, "№", "");
        НомерДоговораЧистый = СокрЛП(НомерДоговораЧистый); // Результат: "12345"
    КонецЕсли;
КонецЕсли;

Важно: Этот метод зависит от строгого соблюдения формата наименования. Любые отклонения (например, "дог.12345" или "договор_12345") могут нарушить логику парсинга. Рекомендуем хранить номер договора в отдельном реквизите для большей надежности и удобства.

Дополнительные функции и советы

Хранение XML-файлов в базе данных

Для удобства и централизации данных можно хранить сгенерированные XML-файлы ЭСЧФ прямо в базе 1С. Для этого необходимо преобразовать двоичные данные файла в формат Base64 и сохранить как строку, а затем выполнять обратное преобразование при необходимости.


Функция ПолучитьТекстBase64(Объект, ТипОперации, Строка64 = "")
    Если ТипОперации = "Преобразование" Тогда
        // Объект - это путь к файлу или ДвоичныеДанные
        Если ТипЗнч(Объект) = Тип("Строка") Тогда // Если передан путь к файлу
            ДвоичныеДанныеФайла = Новый ДвоичныеДанные(Объект);
        Иначе
            ДвоичныеДанныеФайла = Объект;
        КонецЕсли;
        Строка64 = Base64Строка(ДвоичныеДанныеФайла);
        Возврат Строка64;
    ИначеЕсли ТипОперации = "Востановление" Тогда
        // Строка64 - это строка Base64, сохраненная в базе
        ДвоичныеДанныеФайла = Base64Значение(Строка64);
        ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
        ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
        Возврат ИмяВременногоФайла; // Возвращаем путь к созданному временному файлу
    КонецЕсли;
    Возврат Неопределено;
КонецФункции

// Пример использования:
// Сохранение файла в Base64
// ПутьКФайлу = "C:\Путь\К\ЭСЧФ.xml";
// СтрокаBase64 = ПолучитьТекстBase64(ПутьКФайлу, "Преобразование");
// // Теперь СтрокаBase64 можно сохранить в реквизите типа Строка неограниченной длины

// Восстановление файла из Base64
// ПутьКВосстановленномуФайлу = ПолучитьТекстBase64(Неопределено, "Востановление", СтрокаBase64);
// // ПутьКВосстановленномуФайлу будет содержать путь к временному XML-файлу

Механизм выгрузки с использованием флагов

Для эффективной работы с массовой выгрузкой ЭСЧФ рекомендуется использовать систему флагов в документах 1С. Например, можно добавить реквизиты "ТребуетсяВыгрузкаЭСЧФ" (Булево) и "ЭСЧФВыгружен" (Булево).

  1. При проведении документа (реализации, акта) устанавливаем флаг "ТребуетсяВыгрузкаЭСЧФ = Истина".
  2. Периодически запускаем обработку, которая отбирает все документы с "ТребуетсяВыгрузкаЭСЧФ = Истина" и "ЭСЧФВыгружен = Ложь".
  3. После успешной выгрузки ЭСЧФ на портал, для этих документов устанавливаем "ЭСЧФВыгружен = Истина".

Это позволит автоматизировать процесс и избежать повторной выгрузки уже отправленных документов.

Проверка соответствия ЭСЧФ протоколу обмена

Перед подписанием и отправкой исправленного ЭСЧФ важно убедиться, что он соответствует протоколу обмена. Это помогает избежать ошибок и отклонений со стороны портала.


Функция ЭСЧФСоответствуетПротоколуВх(Документ, Отказ = Ложь, Ошибки = Неопределено) Экспорт
    Если Документ.ТипЭСЧФ = ПредопределенноеЗначение("Перечисление.ТипыЭСЧФ.Исправленный") Тогда
        // Правило-56: Проверка наличия неаннулированных ЭСЧФ, ссылающихся на текущий
        КвитанцияДокумента = РегистрыСведений.КвитанцииВходящих.СоздатьМенеджерЗаписи();
        КвитанцияДокумента.Год = Документ.НомерГод;
        КвитанцияДокумента.Объект = Документ.НомерОбъект;
        КвитанцияДокумента.Номер = Документ.НомерНомер;
        КвитанцияДокумента.Прочитать();

        Если Не КвитанцияДокумента.Выбран И Ошибки <> Неопределено Тогда
            // ОтразитьОшибку(Ошибки, "Статус не найден", "КЭСЧФ"); // Пользовательская функция
        ИначеЕсли КвитанцияДокумента.Статус = ПредопределенноеЗначение("Перечисление.СтатусыЭСЧФ.НаСогласовании") Тогда
            ЗапросПоНомеруПродавцаИлиКомитента = Новый Запрос;
            ЗапросПоНомеруПродавцаИлиКомитента.УстановитьПараметр("Номер", Документ.КЭСЧФ);
            МассивСтатусов = Новый Массив;
            МассивСтатусов.Добавить(ПредопределенноеЗначение("Перечисление.СтатусыЭСЧФ.Аннулирован"));
            МассивСтатусов.Добавить(ПредопределенноеЗначение("Перечисление.СтатусыЭСЧФ.ВыставленАннулированПоставшиком"));
            ЗапросПоНомеруПродавцаИлиКомитента.УстановитьПараметр("МассивСтатусов", МассивСтатусов);
            ЗапросПоНомеруПродавцаИлиКомитента.Текст =
                "ВЫБРАТЬ
                |    ЭлектронныйСчетФактураПолученный.Номер,
                |    КвитанцииВходящих.Статус,
                |    КвитанцииВходящих.ДатаСостояния
                |ИЗ
                |    Документ.ЭлектронныйСчетФактураПолученный КАК ЭлектронныйСчетФактураПолученный
                |        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КвитанцииВходящих КАК КвитанцииВходящих
                |        ПО ЭлектронныйСчетФактураПолученный.НомерГод = КвитанцииВходящих.Год
                |        И ЭлектронныйСчетФактураПолученный.НомерОбъект = КвитанцииВходящих.Объект
                |        И ЭлектронныйСчетФактураПолученный.НомерНомер = КвитанцииВходящих.Номер
                |ГДЕ
                |    (ЭлектронныйСчетФактураПолученный.ПоставщикНомерСчетаКомитента = &Номер
                |        ИЛИ ЭлектронныйСчетФактураПолученный.ПоставщикНомерСчетаПродавца = &Номер)
                |    И НЕ КвитанцииВходящих.Статус В (&МассивСтатусов)";
            РезультатЗапросаПоНомеруПродавцаИлиКомитента = ЗапросПоНомеруПродавцаИлиКомитента.Выполнить();
            Если Не РезультатЗапросаПоНомеруПродавцаИлиКомитента.Пустой() Тогда
                Отказ = Истина;
                Если Ошибки <> Неопределено Тогда
                    ВыборкаРезультатаЗапросаПоНомеруПродавцаИлиКомитента = РезультатЗапросаПоНомеруПродавцаИлиКомитента.Выбрать();
                    // ОтразитьОшибку(Ошибки, "Получатель может подписать исправленный со статусом ""На согласовании"" только тогда, | когда в БД нет неаннулированных ЭСЧФ, в которых в 11 или 12 поле указан номер из 5 | поля данного исправленного ЭСЧФ со статусом ""На согласовании"".", "КЭСЧФ");
                    // Пока ВыборкаРезультатаЗапросаПоНомеруПродавцаИлиКомитента.Следующий() Цикл
                    //     ОтразитьОшибку(Ошибки, "Ссылается на документ № " + ВыборкаРезультатаЗапросаПоНомеруПродавцаИлиКомитента.Номер + " со статусом " + ВыборкаРезультатаЗапросаПоНомеруПродавцаИлиКомитента.Статус + " от " + ВыборкаРезультатаЗапросаПоНомеруПродавцаИлиКомитента.ДатаСостояния, "КЭСЧФ");
                    // КонецЦикла;
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;

        // Правило-57: Проверка наличия других исправленных ЭСЧФ на согласовании к данному ЭСЧФ
        ЗапросИсправленныйНаСогласовании = Новый Запрос;
        ЗапросИсправленныйНаСогласовании.УстановитьПараметр("КЭСЧФ", Документ.Номер);
        ЗапросИсправленныйНаСогласовании.УстановитьПараметр("НаСогласовании", ПредопределенноеЗначение("Перечисление.СтатусыЭСЧФ.НаСогласовании"));
        ЗапросИсправленныйНаСогласовании.Текст =
            "ВЫБРАТЬ
            |    ЭлектронныйСчетФактураПолученный.Ссылка КАК СсылкаНаИсправленныйНаСогласовании
            |ИЗ
            |    Документ.ЭлектронныйСчетФактураПолученный КАК ЭлектронныйСчетФактураПолученный
            |        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КвитанцииВходящих КАК КвитанцииВходящих
            |        ПО ЭлектронныйСчетФактураПолученный.НомерГод = КвитанцииВходящих.Год
            |        И ЭлектронныйСчетФактураПолученный.НомерОбъект = КвитанцииВходящих.Объект
            |        И ЭлектронныйСчетФактураПолученный.НомерНомер = КвитанцииВходящих.Номер
            |ГДЕ
            |    ЭлектронныйСчетФактураПолученный.КЭСЧФ = &КЭСЧФ
            |    И КвитанцииВходящих.Статус = &НаСогласовании";
        РезультатЗапросаИсправленногоНаСогласовании = ЗапросИсправленныйНаСогласовании.Выполнить();
        Если Не РезультатЗапросаИсправленногоНаСогласовании.Пустой() Тогда
            Отказ = Истина;
            Если Ошибки <> Неопределено Тогда
                ВыборкаИсправленногоНаСогласовании = РезультатЗапросаИсправленногоНаСогласовании.Выбрать();
                ВыборкаИсправленногоНаСогласовании.Следующий();
                // ОтразитьОшибку(Ошибки, "Получатель не может дать согласие на аннулирование ЭСЧФ, если к данному ЭСЧФ есть исправленный ""на согласовании"" | (" + СокрЛП(ВыборкаИсправленногоНаСогласовании.СсылкаНаИсправленныйНаСогласовании) + "). При подписании получателем исправленного | ЭСЧФ, исходный ЭСЧФ должен быть аннулирован автоматически", "КЭСЧФ");
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
    Возврат Отказ;
КонецФункции

Проверка товаров на принадлежность к Перечню 287

Для определения правильной ставки НДС и соответствия законодательству важно проверять коды ТН ВЭД ЕАЭС на принадлежность к перечням, утвержденным указами Президента. Например, Указ №287 от 21 июня 2007 г. "О налогообложении продовольственных товаров и товаров для детей".


Функция ВходитВПеречень287(Код) Экспорт
    // Код - Строка, код ТН ВЭД ЕАЭС в формате '9999999999'
    Первый = Лев(Код, 4);
    Второй = Лев(Код, 6);
    Третий = Лев(Код, 9);
    Возврат Код = "0101210000" Или Второй = "010221" Или Код = "0103100000" Или Код = "0104101000"
        Или Код = "0104201000" Или Первый = "0105" Или Код = "0106141090" Или Первый = "0201"
        Или Первый = "0202" Или Первый = "0203" Или Первый = "0204" И Не Второй = "020450"
        Или Второй = "020500" Или Первый = "0206" И Не ( Код = "0206101000" Или Код = "0206220001"
        Или Код = "0206291000" Или Код = "0206300001" Или Код = "0206300003" Или Код = "0206410001"
        Или Код = "0206490001" Или Второй = "020680" Или Второй = "020690" ) Или Первый = "0207"
        И Не ( Третий = "020743000" Или Третий = "020745930" Или Третий = "020753000" Или Третий = "020755930" )
        Или Второй = "020810" Или Код = "0209101100" Или Код = "0209101900" Или Второй = "160100"
        Или Второй = "160232" Или Второй = "160239" Или Второй = "160241" Или Второй = "160242"
        Или Второй = "160249" Или Второй = "160250" Или Первый = "0401" Или Первый = "0402"
        Или Первый = "0403" Или Первый = "0404" Или Второй = "040510" Или Второй = "040520"
        Или Второй = "040610" Или Второй = "040630" И Не Код = "0406301000" Или Код = "0406906900"
        Или Код = "0406908600" Или Третий = "040690990" Или Третий = "151211910" Или Код = "1512199002"
        Или Код = "1512199009" Или Третий = "151411900" Или Третий = "151419900" Или Третий = "151491900"
        Или Третий = "151499900" Или Первый = "1517" Или Код = "2106909804" Или Первый = "2302"
        Или Первый = "2303" Или Второй = "230990" Или Первый = "1201" Или Второй = "120400"
        Или Первый = "1205" Или Второй = "120600" Или Код = "2304000001" Или Код = "2304000009"
        Или Первый = "2306" И Не ( Код = "2306500000" Или Код = "2306600000" ) Или Первый = "1905"
        И Не Код = "1905905500" Или Код = "2102103100" Или Код = "2102103900" Или Код = "1704906100"
        Или Код = "1704906500" Или Код = "1704907100" Или Код = "1704907500" Или Второй = "100630"
        Или Код = "1006400000" Или Второй = "110100" Или Первый = "1102" Или Первый = "1103"
        Или Первый = "1104" Или Первый = "1105" Или Код = "1106100000" Или Код = "1107101100"
        Или Код = "1107109100" Или Первый = "1208" Или Первый = "1902" Или Код = "1602100010"
        Или Код = "1702500000" Или Код = "1901100000" Или Код = "2104200010" Или Первый = "2202"
        И Не Код = "2202901001" Или Второй = "290544" Или Второй = "382460" Или Первый = "0701"
        Или Третий = "070200000" Или Код = "0703101900" Или Код = "1212210000" Или Первый = "2001"
        Или Первый = "2002" Или Первый = "2003" Или Первый = "2004" Или Первый = "2005"
        Или Первый = "2006" Или Первый = "2006" Или Первый = "2008" Или Первый = "2009"
        И Не Код = "2005202000" Или Первый = "1701" Или Код = "0409000000" Или Код = "0407210000"
        Или Код = "0407291000" Или Код = "0407901000" Или Код = "0408118000" Или Код = "0408198100"
        Или Код = "0408198900" Или Код = "0408918000" Или Код = "0408998000" Или Первый = "0302"
        Или Первый = "0303" Или Первый = "0304" Или Первый = "0305" Или Первый = "1604"
        И Не ( Код = "1604110000" Или Код = "1604141600" Или Код = "1604160000" Или Код = "1604191000"
        Или Код = "1604193100" Или Код = "1604199710" Или Код = "1604201000" Или Код = "1604203000"
        Или Код = "1604204000" Или Код = "1604209010" Или Код = "1604310000" Или Код = "16043200" )
        Или Код = "3407000000" Или Код = "3922100000" Или Код = "3926100000" Или Первый = "4202"
        Или Код = "4421909800" Или Код = "9619002100" Или Код = "9619003100" Или Код = "9619003900"
        Или Код = "9619005101" Или Код = "9619005901" Или Код = "9619009001" Или Код = "4820105000"
        Или Код = "4820200000" Или Код = "4820300000" Или Код = "4820900000" Или Код = "6101209000"
        Или Код = "6101309000" Или Код = "6101908000" Или Код = "6102109000" Или Код = "6102209000"
        Или Код = "6102309000" Или Код = "6102909000" Или Первый = "6103" Или Первый = "6104"
        Или Первый = "6105" Или Первый = "6106" Или Первый = "6107" Или Первый = "6108"
        Или Первый = "6109" Или Первый = "6110" Или Первый = "6111" Или Первый = "6112"
        Или Первый = "6115" Или Первый = "6116" Или Первый = "6201" Или Первый = "6202"
        Или Первый = "6203" Или Первый = "6204" Или Первый = "6205" Или Первый = "6206"
        Или Первый = "6207" Или Первый = "6208" Или Первый = "6209" Или Первый = "6211"
        Или Код = "6216000000" Или Первый = "6401" Или Первый = "6402" И Не ( Второй = "640212"
        Или Код = "6402190000" ) Или Первый = "6403" И Не Код = "6403190000" Или Первый = "6404"
        И Не Код = "6404110000" Или Первый = "6405" Или Код = "6504000000" Или Первый = "6506"
        Или Второй = "871500" Или Первый = "8716" Или Код = "9403202009" Или Третий = "940350000"
        Или Код = "9403700009" Или Второй = "940421" Или Второй = "940429" Или Второй = "950300"
        Или Второй = "950490";
КонецФункции

Интеграция с порталом: сложности и перспективы

Изначально интеграция с Порталом ЭСЧФ была сопряжена с определенными трудностями. Разработчики портала и компания Авест (предоставляющая ПО для ЭЦП) предложили утилиты, которые позволяли подписывать и отправлять ЭСЧФ из папки через командную строку. Это усложняло прямую интеграцию из 1С, так как требовало запуска внешних процессов.

Основной причиной такого подхода было необходимость реализации шифрования канала связи (TLS) собственными средствами, что затрудняло прямое использование веб-сервисов (SOAP) из 1С.

Несмотря на это, разработчики 1С и сторонние интеграторы продолжают искать более удобные методы, такие как использование COM-объектов или Native API, которые позволили бы производить работу с TLS и ЭЦП более прозрачно для учетных систем. Хотя изначально в 2016 году веб-сервисы были отклонены, функционал портала постоянно дорабатывается, и в современных типовых конфигурациях 1С для Беларуси уже реализован встроенный механизм обмена с порталом ЭСЧФ, что значительно упрощает процесс.

Типичные ошибки и их устранение

Работая с ЭСЧФ, пользователи и программисты сталкиваются с рядом характерных ошибок.

Ошибка при работе с файлами

Ошибка при записи файла: xml_file=xml_fso.CreateTextFile(прм_ИмяФайла, -1, 0); может возникнуть, если вы пытаетесь перезаписать файл, который уже открыт или используется другим процессом. Убедитесь, что файл закрыт перед попыткой его создания или перезаписи.

Если функция xml_fso.FileExists() возвращает -1, это означает, что файл существует. Возврат 0 означает, что файл не существует. Проверьте логику работы с файлами и правильность путей.

Нестабильность работы портала

Многие ошибки, особенно во времена всеобщей выгрузки ЭСЧФ, связаны с нестабильной работой портала. Возможны ситуации, когда 1С выдает успешную отправку, но на портале ЭСЧФ нет, или возникают ошибки типа "IN_PROGRESS" ("В разработке") на длительное время. В таких случаях рекомендуется:

  1. Повторить запрос статуса через некоторое время (иногда до суток).
  2. Попробовать загрузить XML-файл вручную на портал для получения более детальной ошибки.
  3. Использовать возврат статуса сразу после отправки, чтобы получить ответ от портала.

Регулярно проверяйте новости на портале vat.gov.by о регламентных работах и обновлениях.

Отладка ошибок

При возникновении ошибок в 1С (особенно в 1С 8.x) всегда используйте отладчик. Установите точку останова перед проблемной строкой и посмотрите значения всех переменных. Используйте функцию "Остановка по ошибке" в отладчике, чтобы точно определить место возникновения проблемы и состояние данных.

Таким образом, работа с ЭСЧФ в 1С требует комплексного подхода, глубокого понимания как учетных процессов, так и технических аспектов интеграции. Используя предоставленные решения и постоянно адаптируясь к изменениям на Портале ЭСЧФ, вы сможете эффективно автоматизировать этот важный аспект налогового учета.

← На главную