Или как создать рабочий лист 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 в процессе. 🙂
Полезные ссылки:
Ваш Питрофф.
“…В этот раз хотелось изящного, быстрого и дешевого решения, поэтому от просьбы “оценить задачу по-быстрому” пришлось уклониться…” – напрасно 🙂 Разработка с нуля модуля, читающего excel-файлы при помощи Apache POI заняла примерно день, так что решение оказалось вполне себе быстрым 🙂
При этом пикантность ситуации была в том, что excel-файл был старым, который еще не xml, так что вариантов-то особо и не было. POI вообще мощная штука, очень много чего умеет.
Модуль надо еще задеплоить на сервер PI, и это еще одно из ограничивающих условий было.
Очень сложно объяснить поддержке, сидящей в Бангалоре, зачем тебе на продуктивном сервере PI что-то не очень для них понятное. 🙂
А так – да, в модуль адаптера много чего положить можно.