Количество консолидированных строк и значение Ha sh - получение остатка в XSLT - PullRequest
0 голосов
/ 25 января 2020

У меня есть входной файл XML, который выглядит следующим образом:

<?xml version='1.0' encoding='UTF-8'?>
<root>
      <entry>
             <ID>T-1149</ID>
             <Item_ID>FM1</Item_ID>
             <Item_Amount>
                        <Amount>20.00</Amount>
             </Item_Amount>
      </entry>
      <entry>
             <ID>T-1149</ID>
             <Item_ID>FM1</Item_ID>
             <Item_Amount>
                         <Amount>10.00</Amount>
             </Item_Amount>
      </entry>
      <entry>
             <ID>T-1142</ID>
             <Item_ID>FM1</Item_ID>
             <Item_Amount>
                          <Amount>10.00</Amount>
             </Item_Amount>
      </entry>
      <entry>
             <ID>T-1142</ID>
             <Item_ID>FM2</Item_ID>
             <Item_Amount>
                         <Amount>-50.00</Amount>
             </Item_Amount>
      </entry>
</root>    

Требования:

  1. Консолидация записи, если они имеют тот же ID и Item_ID, и суммировать общую сумму. Подсчитайте количество строк в выводе, включая заголовок и трейлер.

  2. Получите общее значение Ха sh на основе идентификатора и суммы. Алгоритм:
    (1) Определить позицию.
    ID выровнен по правому краю с длиной поля Char (30)
    Сумма выровнена по левому краю с длиной поля Sign (27)
    (2) Преобразуйте каждый алфавитный символ c или значение знака в число. Ниже приведено примерное отображение символов.
    T = 30, - = 42,. = 41, пробел = 40
    (3) Разделить поле идентификатора на две части:
    • Значение A (15 alphanumeri c value) - сложить преобразованные числа из позиции 1 - 15
    • Значение B (15 alphanumeri c value) - сложить преобразованные числа из позиции 16 - 30
    (4) Разделить поле Amount на две части:
    • Value C (15 sign sign) - сложить преобразованные числа из позиции 1 - 15
    • Значение D (12-значное значение) - сложите преобразованные числа из позиции 16 - 27
    (5) Затем примените формулу: REMAINDER = mod (abs ((B + D)) - (A + C)), 13)
    (6) итоговая сумма хэша = сумма всех REMAINDER.

Вывод с фиксированной шириной должен выглядеть следующим образом:

Header1
T-1149                        FM1                      30.00
T-1142                        FM1                      10.00
T-1142                        FM2                     -50.00
TRAILER 5 15

Где 5 - общее количество строк, вкл. заголовок и трейлер и 15 - это га sh всего.

Пример расчета для первой записи:

T-1149 
VALUE A: 30+42+1+1+4+9+40*9 =447
VALUE B: 40*15 = 600
VALUE C: 40*15 = 600
VALUE D: 3+41+40*7= 324
REMAINDER=6

У меня есть следующий код:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all" version="3.0">

    <xsl:output method="text"/>


    <xsl:mode on-no-match="shallow-skip" use-accumulators="#all"/>

    <xsl:accumulator name="A" as="xs:integer?" initial-value="()">
        <xsl:accumulator-rule match="root/entry/ID" phase="end"
            select="accumulator-after('values') => subsequence(1, 15) => sum()"/>
    </xsl:accumulator>

    <xsl:accumulator name="B" as="xs:integer?" initial-value="()">
        <xsl:accumulator-rule match="root/entry/ID" phase="end"
            select="accumulator-after('values') => subsequence(16, 27) => sum()"/>
    </xsl:accumulator>

    <xsl:accumulator name="C" as="xs:integer?" initial-value="()">
        <xsl:accumulator-rule
            match="root/entry/Item_Amomunt/Amount"
            phase="end" select="accumulator-after('values') => subsequence(1, 15) => sum()"/>
    </xsl:accumulator>

    <xsl:accumulator name="D" as="xs:integer?" initial-value="()">
        <xsl:accumulator-rule
         match="root/entry/Item_Amomunt/Amount"
            phase="end" select="accumulator-after('values') => subsequence(16, 27) => sum()"/>
    </xsl:accumulator>

    <xsl:accumulator name="values" as="xs:integer*" initial-value="()">
        <xsl:accumulator-rule match="entry" select="()"/>
        <xsl:accumulator-rule
            match="entry/ID/text() | entry/Item_Amount/Amount/text()"
            select="
                analyze-string(., '.')//*:match ! (
                if (. = '.')
                then
                    41
                else
                    if (. = ' ')
                    then
                        40
                    else
                        if (. = '-')
                        then
                            42
                        else
                            if (. = ',')
                            then
                                42
                         else
                            if (. = 'T')
                            then
                               30 
                            else
                                xs:integer(.))"
        />
    </xsl:accumulator>

    <xsl:accumulator name="remainder" as="xs:integer?" initial-value="()">
        <xsl:accumulator-rule match="root/entry" select="()"/>
        <xsl:accumulator-rule
            match="root/entry" phase="end"
            select="
                abs((accumulator-after('B') + accumulator-after('D')) -
                (accumulator-after('A') + accumulator-after('C'))) mod 13"
        />
    </xsl:accumulator>

    <xsl:accumulator name="remainder-sum" as="xs:integer?" initial-value="()">
        <xsl:accumulator-rule match="root" select="0"/>
        <xsl:accumulator-rule match="root/entry"
            select="$value + accumulator-after('remainder')"/>
    </xsl:accumulator> 

    <xsl:variable name="count_invoice_line"
        select="count(root/entry)"/>

    <xsl:variable name="header_line">
        <xsl:value-of select="1"/>
    </xsl:variable>

    <xsl:variable name="trailer_line">
        <xsl:value-of select="1"/>
    </xsl:variable> 


    <xsl:variable name="RightPadding"
        select="'                                                                                                                                                                                                                                                                                                                                                                                                                
     '"/>

    <xsl:variable name="LeftPadding"
        select="'                                                                                         
    '"/>
    <xsl:function name="mf:PadLeft">
        <xsl:param name="string"/>
        <xsl:param name="length"/>
        <xsl:variable name="leftPad">
            <xsl:value-of
                select="substring($LeftPadding, 1, $length - string-length(string($string)))"/>
        </xsl:variable>
        <xsl:sequence select="concat($leftPad, $string)"/>
    </xsl:function>

    <xsl:function name="mf:PadRight">
        <xsl:param name="string"/>
        <xsl:param name="length"/>
        <xsl:sequence select="substring(concat($string, $RightPadding), 1, $length)"/>
    </xsl:function>
    <xsl:template match="/">

       <xsl:apply-templates/>
      <xsl:sequence
        select="accumulator-after('remainder-sum')"/>
        <Control_Header_Record>
            <RowIdentifier>
                <xsl:value-of select="mf:PadRight('HEADER', 6)"/>
            </RowIdentifier>          
        </Control_Header_Record>
        <Detail>
            <xsl:for-each-group select="root/entry"
                group-by="concat(ID, ' ', Item_ID)">
                  <ID><xsl:value-of select="mf:PadRight(ID, 30)"/></ID>
                  <Item_ID><xsl:value-of select="mf:PadRight(Item_ID, 3)"/>
                    <xsl:value-of select="mf:PadLeft(Amount, 27)"/>
                </Row_Indentifier>

        </Detail>
        <Trailer_Record>
            <xsl:variable name="total_feed_line">
                <xsl:value-of select="$count_invoice_line + $header_line + $trailer_line"/>
            </xsl:variable>          
            <RowIdentifier><xsl:value-of select="mf:PadRight('TRAILER',8)"/></RowIdentifier>
            <Total_Feed_Line><xsl:value-of select="mf:PadRight($total_feed_line,2)"/></Total_Feed_Line>
            <Hash_Total_Value><xsl:value-of select="mf:PadRight($remainder-sum,15)"/></Hash_Total_Value>
        </Trailer_Record>
     </xsl:template>

Проблема с кодами:
1. Не получается получить правильные итоговые строки в трейлере. Когда две строки объединены в одну строку, он все равно считает элемент как две, поскольку он основан на xpath «root / entry». Я попытался создать переменную и присвоить ей номер внутри l oop, но она не добавляется должным образом. Примерно так:

<xsl:variable name="counter"/>  <-- Global variable
<xsl:value-of select="$counter + 1"> <-- inside the loop
<xsl:value-of select=$counter> <-- added in the trailer

Значение ha sh не добавляет «пробел» для вычислений. Например, если у меня есть T-1149, он суммирует числа, но не пробел от позиции 7 до 15.

T-1149 ЗНАЧЕНИЕ A: 30 + 42 + 1 + 1 + 4 + 9 = 89 ЗНАЧЕНИЕ B: 0 ЗНАЧЕНИЕ C: 0 ЗНАЧЕНИЕ D: 3 + 41 = 44 REMAINDER = 6

Я попытался создать переменную и поместить ее в аккумулятор вместо узла, но это не работает Например,

<xsl:variable name="var1" select="1149                          "/>
<xsl:variable name="var2" select="                      12.00"/>

<xsl:accumulator name="A" as="xs:integer?" initial-value="()">
    <xsl:accumulator-rule match="$var1" phase="end"
        select="accumulator-after('values') => subsequence(1, 15) => sum()"/>
</xsl:accumulator>

Я планирую сначала преобразовать входной файл (из другой таблицы стилей), отформатировать и добавить пространство и ввести его в основную таблицу стилей, но проблема заключается в том, как поместить переменную внутри правила аккумулятора вместо узла. Можно ли сделать ту же таблицу стилей и в XSLT2.0? Я не знаком с XSLT3. Спасибо за помощь.

...