“А у меня – Excel!”

Или как создать рабочий лист Excel из SAP PI без особых заморочек.

Попалась тут под руку задачка – одна из интегрируемых систем просит присылать данные поставщиков в формате листа Excel.
Формат простейший, что-то вроде такого:

Формат вроде несложный, но не “родной” для SAP PI. В практике приходилось работать с разными форматами, но в основном – с использованием Java в виде модулей адаптера или java-мэппинга.

В этот раз хотелось изящного, быстрого и дешевого решения, поэтому от просьбы “оценить задачу по-быстрому” пришлось уклониться и взять тайм-аут на подумать/потестировать. Запрос к мировому разуму дал несколько ссылок на решение, причем покрытое пылью времен 🙂 и давно существующее. 🙂

Нам на помощь пришел тот факт, что продукты Microsoft Office с некоторого времени перевели свои файлы в … XML-формат! То есть можно сделать соответствующий XML-документ, а Excel его считает как родной.

XML-формат документа Excel выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" 
xmlns:x="urn:schemas-microsoft-com:office:excel" 
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="https://www.w3.org/TR/html401/">
<Worksheet ss:Name="CognaLearn+Intedashboard">
<Table>
<Column ss:Index="1" ss:AutoFitWidth="0" ss:Width="110"/>
<Row>
<Cell><Data ss:Type="String">ID</Data></Cell>
<Cell><Data ss:Type="String">Project</Data></Cell>
<Cell><Data ss:Type="String">Reporter</Data></Cell>
<Cell><Data ss:Type="String">Assigned To</Data></Cell>
....

Перевод в такой формат может быть сделан любым инструментом меппинга – XSLT/Java/UDF.

Для создания такого файла я решил использовать простую промежуточную структуру XML с данными и XSLT-маппинг. В данном случае XSLT предпочтительней других инструментов, поскольку в целевом документе много пространств имен, а SAP PI в стандарте не умеет их добавлять штатными инструментами.
Исходником для данных может быть IDoc, Proxy, XML – эту часть задачи я сознательно не раскрываю. Но перед формированием excel ее нужно будет привести к удобному для xslt формату c помощью Message Mapping, например.
Структура данных для перевода в Excel простая (data type, message type), в ней есть строчки и столбцы с данными.

XSLT получился вот такой:

<?xml version="1.0" encoding="utf-8"?>
<?mso-application progid="Excel.Sheet"?>
<xsl:stylesheet version="1.0"
xmlns:html="http://www.w3.org/TR/REC-html40"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns="urn:schemas-microsoft-com:office:spreadsheet"
    xmlns:o="urn:schemas-microsoft-com:office:office" 
    xmlns:x="urn:schemas-microsoft-com:office:excel"
    xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">

<xsl:template match="/">
        <Workbook>
            <Styles>
                <Style ss:ID="Default" ss:Name="Normal">
                    <Alignment ss:Vertical="Bottom" />
                    <Borders />
                    <Font />
                    <Interior />
                    <NumberFormat />
                    <Protection />
                </Style>
                <Style ss:ID="s21">
                    <Font ss:Size="22" ss:Bold="1" />
                </Style>
                <Style ss:ID="s22">
                    <Font ss:Size="14" ss:Bold="1" />
                </Style>
                <Style ss:ID="s23">
                    <Font ss:Size="12" ss:Bold="1" />
                </Style>
                <Style ss:ID="s24">
                    <Font ss:Size="10" ss:Bold="1" />
                </Style>
            </Styles>

            <Worksheet ss:Name="CONTR">
                <Table>
                    <xsl:call-template name="CONTR_XML2XSL" />
                </Table>
            </Worksheet>
			<Worksheet ss:Name="USERS">
                <Table>
                    <xsl:call-template name="USERS_XML2XSL" />
                </Table>
           .........
        </Workbook>

    </xsl:template>

<xsl:template name="DISTR_XML2XSL">
        <Row>
                        <Cell>
                            <Data ss:Type="String">Код</Data>
                        </Cell>
                        <Cell>
                            <Data ss:Type="String">Название компании (краткое)</Data>
                        </Cell>
						<Cell>
                            <Data ss:Type="String">ИНН</Data>
                        </Cell>
						<Cell>
                            <Data ss:Type="String">КПП</Data>
                        </Cell>
						<Cell>
                            <Data ss:Type="String">Код контрагента</Data>
                        </Cell>
		.......
                    </Row>
        <xsl:for-each
            select="//CONTR">
            <Row>
                <Cell>
                    <Data ss:Type="String">
                        <xsl:value-of select="CODE" />
                    </Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">
                        <xsl:value-of select="NAME" />
                    </Data>
                </Cell>
				 <Cell>
                    <Data ss:Type="String">
                        <xsl:value-of select="INN" />
                    </Data>
                </Cell>
				 <Cell>
                    <Data ss:Type="String">
                        <xsl:value-of select="KPP" />
                    </Data>
                </Cell>
				.......
            </Row> 
        </xsl:for-each>
</xsl:template>
	
<xsl:template name="USERS_XML2XSL">
........
<xsl:template match="MT_Comp_Info">
</xsl:template>

</xsl:stylesheet>

Основные блоки преобразования здесь следующие:
<template match=”/”> соответствует корневому элементу исходного документа и первым “запускается” при преобразовании. Все, что содержится внутри этого тега – становится телом конечного xml-файла.
<xsl:call-template name=”CONTR_XML2XSL”> – вызывает соответствующий блок xslt – в нем уже идет перенос данных.
<data ss:type=”String”> – несет в себе содержимое ячеек, в виде обычного текста или тега <xsl:value-of select=”XYZ”> – для переноса данных из исходного XML

Далее делаем operation mapping с двумя шагами – преобразование из исходного IDoc/XML/Proxy (в случае прокси можно заменить этот шаг, заложив в прокси сразу формирование нужной структуры) в простую структуру, затем xslt – меппинг для получения XML в формате MIcrosoft Excel.

Конфигурируем интерфейс, используем файловый адаптер – и на выходе получаем искомый Excel-документ. И никакой Java в процессе. 🙂

Полезные ссылки:

Ваш Питрофф.

2 thoughts on ““А у меня – Excel!”

  1. Николай

    “…В этот раз хотелось изящного, быстрого и дешевого решения, поэтому от просьбы “оценить задачу по-быстрому” пришлось уклониться…” – напрасно 🙂 Разработка с нуля модуля, читающего excel-файлы при помощи Apache POI заняла примерно день, так что решение оказалось вполне себе быстрым 🙂
    При этом пикантность ситуации была в том, что excel-файл был старым, который еще не xml, так что вариантов-то особо и не было. POI вообще мощная штука, очень много чего умеет.

    Reply
    1. pitroff.ru Post author

      Модуль надо еще задеплоить на сервер PI, и это еще одно из ограничивающих условий было.
      Очень сложно объяснить поддержке, сидящей в Бангалоре, зачем тебе на продуктивном сервере PI что-то не очень для них понятное. 🙂

      А так – да, в модуль адаптера много чего положить можно.

      Reply

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *