Как обрезать длину номера документа в XML-файле при обмене через EnterpriseData?

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При интеграции сторонних систем, таких как «МойСклад», с решениями на платформе 1С:Предприятие, разработчики часто сталкиваются с ограничением длины номера документа. В типовых конфигурациях 1С (УТ, ERP, КА) длина номера документа обычно ограничена 11 символами, в то время как внешние системы могут генерировать более длинные идентификаторы. Если номер в XML-файле превышает допустимый предел, загрузка документа завершится ошибкой.

В данной статье мы разберем несколько способов программной предобработки XML-файла формата EnterpriseData для изменения или обрезки значения тега Номер. Мы проанализируем разные подходы: от потокового чтения до использования регулярных выражений и XSLT-преобразований.

Метод 1. Потоковое чтение и запись (ЧтениеXML и ЗаписьXML)

Это самый эффективный и рекомендуемый метод для работы с большими файлами EnterpriseData. Потоковые объекты 1С позволяют обрабатывать файлы практически любого размера, не загружая их целиком в оперативную память. Мы будем последовательно читать исходный файл и записывать данные в новый (временный) файл, модифицируя значение нужного нам узла.

Разберем алгоритм по шагам:

  1. Создаем объект ЧтениеXML и открываем исходный файл.
  2. Создаем объект ЗаписьXML для записи модифицированного файла.
  3. В цикле читаем узлы. Если встречаем тип узла НачалоЭлемента с именем "Номер", считываем его текстовое содержимое, обрезаем его и записываем в новый файл.
  4. Все остальные узлы переносим "как есть".

Рассмотрим пример кода:


Функция ОбрезатьНомераВXML(ПутьКФайлу, ПутьКРезультату)
    Чтение = Новый ЧтениеXML;
    Чтение.ОткрытьФайл(ПутьКФайлу);
    
    Запись = Новый ЗаписьXML;
    Запись.ОткрытьФайл(ПутьКРезультату, "UTF-8");
    
    Пока Чтение.Прочитать() Цикл
        ТипУзла = Чтение.ТипУзла;
        
        Если ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "Номер" Тогда
            // Записываем начало тега Номер
            Запись.ЗаписатьНачалоЭлемента(Чтение.Имя);
            
            // Читаем значение внутри тега
            Чтение.Прочитать(); 
            Если Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
                СтарыйНомер = Чтение.Значение;
                // Обрезаем номер до 11 символов, берем ПРАВУЮ часть
                НовыйНомер = Прав(СтарыйНомер, 11); 
                Запись.ЗаписатьТекст(НовыйНомер);
            КонецЕсли;
            
            // Пропускаем до конца элемента Номер в исходном чтении
            Пока Чтение.ТипУзла <> ТипУзлаXML.КонецЭлемента Цикл
                Чтение.Прочитать();
            КонецЦикла;
            
            Запись.ЗаписатьКонецЭлемента();
        Иначе
            // Переносим все остальные типы узлов без изменений
            Запись.ЗаписатьТекущий(Чтение);
        КонецЕсли;
    КонецЦикла;
    
    Запись.Закрыть();
    Чтение.Закрыть();
КонецФункции

Важный нюанс: Обратите внимание на функцию Прав. В документах часто префиксная часть (например, "INV-2023-") является статической, а уникальная цифровая часть находится в конце. Если обрезать номер слева (функция Лев), вы рискуете получить множество документов с одинаковыми номерами, что приведет к ошибкам уникальности в 1С.

Метод 2. Использование регулярных выражений (RegExp)

Если файл не слишком велик (до нескольких десятков мегабайт) и структура тегов плоская, можно использовать объект VBScript.RegExp. Этот метод позволяет быстро найти все вхождения тега Номер и заменить их содержимое.

Проанализируем пример использования регулярного выражения:


Функция ОбработатьЧерезRegExp(ПутьКФайлу)
    ТекстФайла = Новый ТекстовыйДокумент;
    ТекстФайла.Прочитать(ПутьКФайлу);
    ВесьТекст = ТекстФайла.ПолучитьТекст();
    
    RegExp = Новый COMОбъект("VBScript.RegExp");
    RegExp.IgnoreCase = Истина;
    RegExp.Global = Истина;
    RegExp.MultiLine = Истина;
    
    // Шаблон ищет содержимое между тегами <Номер> и </Номер>
    RegExp.Pattern = "<Номер>(.*?)</Номер>";
    
    // Для сложной обрезки лучше использовать MatchCollection и цикл, 
    // но для простой замены можно подготовить функцию.
    // В 1С проще всего перебрать совпадения.
    Matches = RegExp.Execute(ВесьТекст);
    Для Каждого Match Из Matches Цикл
        ПолнаяСтрока = Match.Value; // <Номер>000000123456</Номер>
        ЗначениеНомера = Match.SubMatches(0);
        
        Если СтрДлина(ЗначениеНомера) > 11 Тогда
            НовоеЗначение = Прав(ЗначениеНомера, 11);
            НоваяСтрока = "<Номер>" + НовоеЗначение + "</Номер>";
            ВесьТекст = СтрЗаменить(ВесьТекст, ПолнаяСтрока, НоваяСтрока);
        КонецЕсли;
    КонецЦикла;
    
    ТекстФайла.УстановитьТекст(ВесьТекст);
    ТекстФайла.Записать(ПутьКФайлу);
КонецФункции

Осторожно: Регулярные выражения не учитывают иерархию XML. Если в файле встретится тег Номер внутри комментария или в другом контексте (например, номер телефона контрагента, если тег называется так же), он тоже будет обрезан. Используйте этот метод, только если уверены в уникальности имени тега для документов.

Метод 3. Преобразование через XSLT

Это наиболее «профессиональный» способ трансформации XML. В 1С есть встроенный объект ПреобразованиеXSL. Мы создаем небольшой скрипт (XSL-шаблон), который описывает правила преобразования файла. Основное преимущество в том, что сам код на 1С остается минимальным, а вся логика трансформации выносится в декларативный шаблон.

Рассмотрим пример XSL-шаблона:


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- Копируем всё как есть -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- Правило специально для тега Номер -->
    <xsl:template match="Номер">
        <Номер>
            <xsl:choose>
                <xsl:when test="string-length(.) > 11">
                    <xsl:value-of select="substring(., string-length(.) - 10)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </Номер>
    </xsl:template>
</xsl:stylesheet>

Для применения этого шаблона в 1С используем следующий код:


Процедура ПрименитьXSLT(ПутьКФайлу, ПутьКШаблону, ПутьВыхода)
    Преобразование = Новый ПреобразованиеXSL;
    Преобразование.ЗагрузитьИзФайла(ПутьКШаблону);
    Преобразование.ПреобразоватьИзФайла(ПутьКФайлу, ПутьВыхода);
КонецПроцедуры

Метод 4. Обработка на стороне 1С (Расширение БСП)

Прежде чем менять сам XML-файл, стоит выяснить, можно ли решить задачу средствами самой 1С при загрузке. Если обмен реализован на базе Библиотеки Стандартных Подсистем (БСП), в ней предусмотрены механизмы переопределения логики загрузки без правки исходных файлов.

Выясним причину, почему это может быть лучше:

  1. Целостность данных: В EnterpriseData номер может использоваться как ключ связи в других объектах. Если вы обрежете номер в теге документа, но не обрежете его в ссылках (например, в связанных платежах), связь будет разорвана.
  2. Обновляемость: Изменение внешней обработки или использование расширения 1С не требует промежуточных манипуляций с файлами на диске.

В модуле менеджера обмена или через расширение можно переопределить обработчик ПередЗаписьюПолученногоОбъекта. Рассмотрим пример:


Процедура ПередЗаписьюПолученногоОбъекта(Объект, ДанныеИксдто, КомпонентыОбмена) Экспорт
    // Проверяем, есть ли у объекта свойство Номер
    МетаданныеОбъекта = Объект.Метаданные();
    Если МетаданныеОбъекта.СтандартныеРеквизиты.Найти("Номер") <> Неопределено Тогда
        Если СтрДлина(Объект.Номер) > 11 Тогда
            Объект.Номер = Прав(Объект.Номер, 11);
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Риски и рекомендации

Проанализируем основные опасности, связанные с обрезкой номеров:

1. Нарушение уникальности. Как уже упоминалось, если номера документов в «МойСклад» отличаются только в первых символах (например, MSK-001 и SPB-001), то при обрезке префикса вы получите два документа с номером 001. 1С не позволит записать второй документ, если контроль уникальности включен.

2. Проблемы синхронизации. Если при повторной выгрузке 1С сопоставляет объекты по номеру и дате, то обрезанный номер может не совпасть с тем, что ожидает система (или наоборот, произойдет ложное сопоставление). Рекомендуем настраивать синхронизацию по Уникальному Идентификатору (GUID), который передается в поле Ссылка внутри XML. Если сопоставление идет по GUID, модификация номера безопасна для процесса синхронизации.

3. Ссылочная целостность. В формате EnterpriseData данные сильно связаны. Если вы меняете номер в одном месте, убедитесь, что это не повлияет на поиск этого объекта в других узлах XML. Именно поэтому метод с использованием XMLReader/XMLWriter или XSLT является более надежным, так как он позволяет единообразно изменить все теги Номер во всем документе.

Подводя итог, для разовых задач или небольших файлов отлично подойдет RegExp. Для промышленной эксплуатации и работы с большими объемами данных лучше использовать потоковое чтение XML или XSLT-трансформацию.

← На главную