WEB-приложение Сведение отчетности   (02.10.2017)
Разработка xslt-преобразований
Для реализации загрузки отчетов из xml-документов произвольной структуры необходимо предпринять следующие шаги:
  1. Внимательно изучить структуру, с которой придется иметь дело. Если схемы валидации документов такой структуры не существует, ее придется разработать самостоятельно.
            Например, нам нужно иметь дело с документами следующей структуры:
    <data>
      <report agent="РосПищеСвет" form="Форма 160.285">
        <subreport ЗаСпички="100" ЗаМыло="200" Качество="Хорошее" />
        <subreport code="Разрез 1" ЗаСпички="10" ЗаМыло="20" Качество="Среднее" />
        <subreport code="Разрез 2" ЗаСпички="80" ЗаМыло="280" Качество="Отличное" />
      </report>
    </data>
          
    Для валидации документов такой структуры создадим xsd-схему:
    <xs:schema
      xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:element name="data">
      <!--  Корневой элемент документа имеет имя "data" -->
        <xs:complexType>
          <xs:sequence>
            <xs:element name="report" maxOccurs="unbounded">
            <!--  Корневой элемент включает в себя любое количество элементов "report" -->
              <xs:complexType>
                <xs:sequence>
                  <xs:element maxOccurs="unbounded" name="subreport">
                  <!--  Элемент "report" включает в себя любое количество элементов "subreport" -->
                    <xs:complexType>
                      <xs:attribute name="code" type="xs:string" use="optional" />
                      <!--  Элемент "subreport" имеет необязательный атрибут "code" -->
                      <xs:anyAttribute processContents="skip" />
                      <!--  Элемент "subreport" имеет неизвестное заранее количество любых атрибутов
                        Это будут значения показателей.
                       -->
                    </xs:complexType>
                  </xs:element>
                </xs:sequence>
                <xs:attribute name="agent" type="xs:string" use="required" />
                <!--  Элемент "report" имеет обязательный строковый атрибут "agent" -->
                <xs:attribute name="form" type="xs:string" use="required" />
                <!--  Элемент "report" имеет обязательный строковый атрибут "form" -->
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
          
  2. Теперь нам нужно научиться превращать документы в документы стандартной структуры Парус 8:
    <REPORTS>
      <!-- отчет -->
      <REPORT>
        <!-- обязательные элементы -->
        <AGENT>Мнемокод контрагента</AGENT>
        <FORM>Мнемокод формы</FORM>
        <BDATE>Дата отчета</BDATE>
        <KIND>0-первичный отчет, 1-сводный отчет</KIND>
    
        <!-- необязательные элементы -->
        <CATALOG>Наименование каталога</CATALOG>
        <CHECKED>0-непроверен;1-проверен</CHECKED>
        <STATE>Состояние отчета</STATE>
        <SENT>Отправлен</SENT>
    
        <!-- подотчеты -->
        <SUBREPORTS>
    
          <!-- главный подотчет -->
          <SUBREPORT>
            <!-- значения одиночных показателей -->
            <VALUES>
              <VALUE>
                <CODE>Мнемокод показателя</CODE>
                <!-- один из трех вариантов -->
                <NVAL>Число</NVAL>
                <SVAL>Строка</SVAL>
                <DVAL>Дата</DVAL>
              </VALUE>
              <VALUE>
                ...
              </VALUE>
            </VALUES>
    
            <!-- таблицы показателей -->
            <TABLES>
              <TABLE>
                <NAME>Наименование таблицы</NAME>
                <!-- строки таблицы -->
                <ROWS>
                  <ROW>
                    <VALUE>
                      <CODE>Мнемокод показателя в составе таблицы</CODE>
                      <!-- один из трех вариантов -->
                      <NVAL>Число</NVAL>
                      <SVAL>Строка</SVAL>
                      <DVAL>Дата</DVAL>
                    </VALUE>
                    <VALUE>
                      ...
                    </VALUE>
                  </ROW>
                  <ROW>
                    ...
                  </ROW>
                </ROWS>
              </TABLE>
              <TABLE>
                ...
              </TABLE>
            </TABLES>
          </SUBREPORT>
          <SUBREPORT>
            <CODE>Мнемокод разреза</CODE>
              ...
          </SUBREPORT>
        </SUBREPORTS>
      </REPORT>
      <REPORT>
        ...
      </REPORT>
    </REPORTS>
        
            Преобразование нашего исходного документа к стандартной структуре:
    <xsl:stylesheet version="1.0"
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                    xmlns:parus="parus.xslt"
                    xmlns:vbs="parus.vbscript"
                    exclude-result-prefixes="xsl msxsl parus vbs">
    <!-- Пространства имен:
      xsl - стандартное пространство имен элементов xslt
      msxsl - расширение Microsoft пространства имен xslt
      parus - "наше" пространство имен. Принадлежащие ему идентификаторы описаны ниже.
      vbs - "локальное" пространство имен, через которое мы будем получать доступ к скриптовым функциям. -->
    
    <!-- нам понадобятся возможнсти, которые не предоставляются стандартными средствами xslt-транслятора.
    Напрмер, нам будет нужна текущая дата. Определелим в локальном пространстве имен скриптовую функцию,
    возвращающую отформатированную дату -->
      <msxsl:script language="VBScript" implements-prefix="vbs">
      <![CDATA[
        function today()
          today = Year(Date) & "-" & Month(Date) & "-" & Day(Date)
        end function
      ]]>
      </msxsl:script>
    
      <xsl:output encoding="UTF-8"
                  method="xml"/>
    
      <!-- шаблон корневого узла. Он будет применен один и только один раз. -->
      <xsl:template match="/">
        <!-- Элемент целевой структуры -->
        <REPORTS>
    
          <!-- Для каждого элемента "report" исходной структуры ... -->
          <xsl:for-each select="data/report">
            <REPORT>
              <!-- выводим сообщение пользователю. Он увидит его протоколе загрузки -->
              <xsl:value-of select="parus:Message(concat('Сообщение пользователю при загрузке файла ', parus:FileName(), '.'))"/>
    
              <!-- выводим сообщение в отладочный монитор. -->
              <xsl:value-of select="parus:DebugString('Сообщение в монитор.')"/>
    
              <!-- эти элементы бререм из атрибутов узла "report" -->
              <AGENT><xsl:value-of select="@agent"/></AGENT>
              <FORM><xsl:value-of select="@form"/></FORM>
    
              <!-- Формируем дату как результат вызова скриптовой функции -->
              <BDATE><xsl:value-of select="vbs:today()" /></BDATE>
    
              <!-- константа -->
              <KIND>0</KIND>
    
              <SUBREPORTS>
                <!-- цикл по элементам "subreport" -->
                <xsl:for-each select="subreport">
                  <SUBREPORT>
    
                    <!-- если "subreport" имеет атрибут "code", то использовать его -->
                    <xsl:if test="@code">
                      <CODE><xsl:value-of select="@code"/></CODE>
                    </xsl:if>
    
                    <VALUES>
                      <!-- цикл по всем атрибутам элемента "subreport" -->
                      <xsl:for-each select="@*">
                        <!-- если атрибут имеет имя, отличное от "code", то это показатель -->
                        <xsl:if test="not(name(.)='code')">
                          <VALUE>
                            <CODE><xsl:value-of select="name(.)"/></CODE>
                            <SVAL><xsl:value-of select="." /></SVAL>
                          </VALUE>
                        </xsl:if>
                      </xsl:for-each>
                    </VALUES>
                  </SUBREPORT>
                </xsl:for-each>
              </SUBREPORTS>
            </REPORT>
          </xsl:for-each>
        </REPORTS>
      </xsl:template>
    </xsl:stylesheet>
          
    В результате применения этого преобразования к нашему документу мы получим следующий документ:
    <REPORTS>
      <REPORT>
        <AGENT>РосПищеСвет</AGENT>
        <FORM>Форма 160.285</FORM>
        <BDATE>2015-10-15</BDATE>
        <KIND>0</KIND>
        <SUBREPORTS>
          <SUBREPORT>
            <VALUES>
              <VALUE>
                <CODE>ЗаСпички</CODE>
                <SVAL>100</SVAL>
              </VALUE>
              <VALUE>
                <CODE>ЗаМыло</CODE>
                <SVAL>200</SVAL>
              </VALUE>
              <VALUE>
                <CODE>Качество</CODE>
                <SVAL>Хорошее</SVAL>
              </VALUE>
            </VALUES>
          </SUBREPORT>
          <SUBREPORT>
            <CODE>Разрез 1</CODE>
            <VALUES>
              <VALUE>
                <CODE>ЗаСпички</CODE>
                <SVAL>10</SVAL>
              </VALUE>
              <VALUE>
                <CODE>ЗаМыло</CODE>
                <SVAL>20</SVAL>
              </VALUE>
              <VALUE>
                <CODE>Качество</CODE>
                <SVAL>Среднее</SVAL>
              </VALUE>
            </VALUES>
          </SUBREPORT>
          <SUBREPORT>
            <CODE>Разрез 2</CODE>
            <VALUES>
              <VALUE>
                <CODE>ЗаСпички</CODE>
                <SVAL>80</SVAL>
              </VALUE>
              <VALUE>
                <CODE>ЗаМыло</CODE>
                <SVAL>280</SVAL>
              </VALUE>
              <VALUE>
                <CODE>Качество</CODE>
                <SVAL>Отличное</SVAL>
              </VALUE>
            </VALUES>
          </SUBREPORT>
        </SUBREPORTS>
      </REPORT>
    </REPORTS>
          
    Этот документ вполне соответствует стандартной структуре, и, если в БД существуют соответствующая форма, контрагент, показатели и разрезы, то он загрузится.
        Элементы пространства имен с uri "parus.xslt":
  • Функция FileName () возвращает имя загруженного пользователем файла.
  • Функция Message (Text: string) передает строковый аргумент пользователю в протокол загрузки отчета. Возвращает пустую строку. Если вы хотите передать этой функции что-то, отличное от строки, то преобразуйте аргумент в строку функцией string. Например, <xsl:value-of select="parus:Message(string(@code))" />
  • Функция DebugString (Text: string) передает строковый аргумент в отладочный монитор. Возвращает пустую строку. Если вы хотите передать этой функции что-то, отличное от строки, то преобразуйте аргумент в строку функцией string. Например, <xsl:value-of select="parus:DebugString(string(@code))" />
  • Функция SessionValue (Name: string) возвращает сохраненное в сессии значение с именем, определяемым параметром Name. Например, <xsl:value-of select="parus:SessionValue("agent")" />
Этими функциями вы можете пользоваться в своих xslt-преобразованиях.