cancel
Showing results for 
Search instead for 
Did you mean: 

PDF Forms, JavaScript, Page Breaks, etc.

petrsourek
Explorer
0 Kudos

Hello Gurus,

I have few questions about SAP PDF Interactive forms. I am not doing interactive form, but simple printout, but I want some "layout changes" before I send it to printer/show the form to the user.

For example I am doing material movements and I want to make per page sums per unit. E.g.

I have 5 PCE, 10 KG, 20 L and I want to make subtotals/totals per unit, but I am unable to do it dynamically (or AM I?).

It is not a problem to get sums into a JS object in form e.g.

{ PCE : { Column1: Value, Column2: Value }, etc.}

The problem I see is that I am unable to do "dynamic page breaks" without conditional page break, but when I do conditional page break I need to find number of lines per page before the form start by counting and specifying how many lines there are per page, in ABAP (or is there any other way?) and then do conditional break on per page basis (working even though I don't find it elegant).

The other problem is, when I use conditional breaks I am unable to "duplicate" the subtotal line. When I try to access the instanceManager, it tells me, that it is null. So to do something like this.instanceManager.addInstance(1) is not usable (I am not telling that in official help I should use Layout:Ready function to fire up my own script to change layout, but on note I see, that I should not modify the layout DoH)

LiveCycle ES2 * Adobe LiveCycle Designer ES2

I hope I had explained myself clearly, although I had tried to describe what I have been through. The main question is. Am I able to do dynamic layout changes with script during AdobeForm generation? Or is there any way to get the dynamic per page subtotals to work, preferably with scripts, not counting in ABAP?

Thank you,

Regards,

Petr Sourek

Accepted Solutions (1)

Accepted Solutions (1)

petrsourek
Explorer
0 Kudos

Hello,

so I get it is impossible for simple PDF static form to get "dynamic" subtotals using JS so a little workaround, which I am not proud of, but well it works...

So if anyone needs it, you can:

- Draw form, in the table create header / data item (no footer is needed, we will redesign the data item row for subtotal),

- Count number of lines on 1st page (including totals) and on other pages, if signatures should be with the last data item on the page, count lines until signatures break on first and other pages

- Extend data structure of the table in DDIC and add field TOTALFIELD type char1,

- Create class, or FM, or whatever which will "create" table with (sub)totals for drawint the table into form, don't worry it is completely dynamic, so stick in the table and it will sum all the numeric fields (taken from domains)

Add constant into the class:

cTotalField       type string value 'TOTALFIELD' ,    "#EC NOTEXT

Static method - definition (not in new manner, compatible with 7.31 ABAP), parameter description using abapdocu:

"! Method for preparing the table for printing (all total calculations made in abap), all you need to know is number of lines per page and
"! then implement javascript which will create the table for user in initialization event of table row
"!
"! @parameter nFirstPageLines   | Number of lines on 1st page (mostly different, because there is header too), when omitted the nOtherPageLines is taken into account
"! @parameter nOtherPageLines   | Number of lines on other pages (not 1st one)
"! @parameter nFirstSignLines   | Number of lines on 1st page, when we have to make manual break (for signatures to be on the same page as at least one item)
"! @parameter nOtherSignLines   | Number of lines on other pages (not 1st one), when we have to make manual break (for signatures to be on the same page as at leas one item)
"! @parameter sUnitField        | Field with unit, according to which the sums are going to be dynamic
"! @parameter sAdditionalFields | Additional fields which are going to take value, eg. we are summing by unit field, but we want in total to show also the unit description,
"!                                but when we omit it, it will be shown empty
"! @parameter tTable            | Table to alter (any standard table should do) the only prerequisity is, that the structure has TOTALFIELD type char1. It will sum
"!                                all numeric fields taken from their domains and to the TOTALFIELD it will fill these values:
"!                             
"! <ul>
"!<li><em>B</em> - After this line, we need to break, because the signatures are not going to be on the same page,</li>
"!<li><em>S</em> - This line is subtotal, so we can in JS make different layout of the line,</li>
"!<li><em>T</em> - This line is total, so we can in JS make different layout of the line,</li>
"!</ul>
      prepareTableWithSums
        importing
          value(nFirstPageLines)  type i optional
          nOtherPageLines         type i
          value(nFirstSignLines)  type i optional
          nOtherSignLines         type i optional
          sUnitField              type any
          sAdditionalFields       type string
        changing
          tTable                  type standard table

Static method - implementation (not in new manner, compatible with 7.31 ABAP):

 method prepareTableWithSums .
    "-- Declarations
    "-- Trying new simpler syntax, since there should not be used the global vars, so I stripped G|L since they are useless
    "-- Then simple:
    "-- o   - object,
    "-- r   - reference,
    "-- b   - bool,
    "-- n   - number,
    "-- s   - string,
    "-- t   - internal table (it is a little diferrent so I am not using a as array)
    "-- st  - structure
    data:
      oTable      type ref to cl_abap_tableDescr ,
      oStruc      type ref to cl_abap_structDescr ,
      rDummyTab   type ref to data ,
      rDummySum   type ref to data ,
      bFirstUsed  type boolean ,
      bBreak      type boolean ,
      nI          type i ,
      nNumOfLines type i ,
      nProcLines  type i ,
      sUnit       type mseh6 ,
      sWhereField type string ,
      sWhereCond  type string ,
      tFields     type cl_abap_structDescr=>component_table ,
 
      tAddFields  type standard table of string ,
      tUnits      type standard table of mseh6 ,
      stUnit      like line of tUnits
      .
 
    "-- Field symbols
    field-symbols:
         type standard table ,
         type standard table ,
        like line of tAddFields ,
           like line of tFields ,
            type any ,
           type any ,
             type any ,
             type any ,
          type any ,
         type any
      .
 
    break-point id zCA_form_main .
 
    "-- When 1st page lines not set, use the same value as for other pages
    if nFirstPageLines is NOT supplied .
      nFirstPageLines = nOtherPageLines .
    endif .
 
    if nOtherSignLines is supplied and nFirstSignLines is NOT supplied .
      nFirstSignLines = nOtherSignLines .
    endif .
 
    "-- Get table description, structure description and fields of table structure
    oTable ?= cl_abap_tableDescr=>describe_by_data( tTable ) .
    oStruc ?= oTable->get_table_line_type( ) .
    tFields = oStruc->get_components( ) .
 
    "-- Get additional fields
    split sAdditionalFields
      at ';'
      into table tAddFields
      in character mode
      .
 
    "-- Strip non numeric value from components
    loop at tFields
      assigning       .
      "-- Check if field should be at output
      read table tAddfields
        transporting no fields
        with key
          table_line = -name
          .
      case sy-subrc .
        when 0 .
          continue .
      endcase .
 
      "-- Delete non numeric fields
      if -type->type_kind NE cl_abap_typeDescr=>typekind_decfloat    and
         -type->type_kind NE cl_abap_typeDescr=>typekind_decfloat16  and
         -type->type_kind NE cl_abap_typeDescr=>typekind_decfloat34  and
         -type->type_kind NE cl_abap_typeDescr=>typekind_float       and
         -type->type_kind NE cl_abap_typeDescr=>typekind_int         and
         -type->type_kind NE cl_abap_typeDescr=>typekind_int1        and
         -type->type_kind NE cl_abap_typeDescr=>typekind_int2        and
         -type->type_kind NE cl_abap_typeDescr=>typekind_num         and
         -type->type_kind NE cl_abap_typeDescr=>typekind_numeric     and
         -type->type_kind NE cl_abap_typeDescr=>typekind_packed
         .
        delete tFields .
      endif .
    endloop .
 
    "-- Create dynamic return table and sum table which will be summed
    create data rDummyTab type handle oTable .
    create data rDummySum type handle oTable .
 
    "-- Get their references
    assign rDummyTab->* to  .
    assign rDummySum->* to  .
    check  is assigned and  is assigned .
 
    "-- Init variables
    nI = 0 .
    nProcLines = 0 .
    nNumOfLines = lines( tTable ) .
    bFirstUsed = abap_false .
    bBreak = abap_false .
    sWhereField = sUnitField .
 
    break-point id zCA_form_main .
 
    "-- 1st page break
    "-- Do it only once at last page and only if signature lines are passed in
    if nFirstSignLines is supplied and bBreak eq abap_false and ( lines( tTable ) < nFirstPageLines ) .
      "-- Get number of units on last page
      free tUnits .
      loop at tTable
        assigning         .
        free stUnit .
        if  is assigned . unassign  . endif .
        assign component sUnitField
          of structure           to           .
        check  is assigned .
        stUnit =  .
        collect stUnit into tUnits .
      endloop .
 
      if bBreak = abap_false and ( lines( tTable ) + 2 * lines( tUnits ) > nFirstSignLines ) .
        bBreak = abap_true .
      endif .
    endif .
 
    "-- Do the math, by looping change table and append reference table
    loop at tTable
      assigning       .
 
      "-- Get sumIf field
      if  is assigned . unassign  . endif .
      assign component sWhereField
        of structure         to         .
      check  is assigned .
 
      "-- Get line in sum table with that field value
      if  is assigned . unassign  . endif .
      read table         assigning         with key
          (sWhereField) =           .
      if  is NOT assigned . "-- Not found, create
        append initial line to  assigning  .
 
        "-- Add flag
        if  is assigned . unassign  . endif .
        assign component cTotalField
          of structure           to           .
        if  is assigned .
           = 'S' .
          unassign  .
        endif .
 
        "-- Add sumif field value
        assign component sWhereField
          of structure           to           .
        if  is assigned .
           =  .
          unassign  .
        endif .
      endif .
 
      "-- Now do the math, count numeric fields into sum table
      loop at tFields
        assigning         .
 
        "-- Get values
        if  is assigned . unassign  . endif .
        if     is assigned . unassign  .    endif .
        assign component -name of structure  to  .
        assign component -name of structure  to  .
 
        check  is assigned and  is assigned .
 
        "-- Sum
        read table tAddFields
          transporting no fields
          with key
            table_line = -name
            .
        case sy-subrc .
          when 0 .
             =  .
          when others .
             =  +  .
        endcase .
      endloop .
 
      "-- Append normal table
      append  to  .
 
      "-- Increase iterator, for page/breaks
      nI = nI + 1 .
      nProcLines = nProcLines + 1 .
 
      "-- 1st or other pages, count if we are at the correct number of lines per page
      case bFirstUsed .
        when abap_true .
          "-- we need to subtract the subtotal lines
          if nI = nOtherPageLines - lines(  ) or ( nNumOfLines - nProcLines = 1 and bBreak eq abap_true )
             .
            case bBreak .
              when abap_true .
                "-- Get last line and assign break pattern
                nI = lines(  ) .
                if  is assigned . unassign  . endif .
                read table                   assigning                   index nI
                  .
                if  is assigned .
                  if  is assigned . unassign  . endif .
                  assign component cTotalField
                    of structure                     to                     .
                  if  is assigned .
                     = 'B' .
                    unassign  .
                  endif .
                  unassign  .
                endif .
            endcase .
 
            nI = 0 .
            append lines of  to  .
            free  .
 
            "-- Do it only once at last page and only if signature lines are passed in
            if nOtherSignLines is supplied and bBreak eq abap_false and ( nNumOfLines - nProcLines < nOtherPageLines ) .
              "-- Get number of units on last page
              free tUnits .
              loop at tTable
                assigning                 from nProcLines to nNumOfLines
                .
                free stUnit .
                if  is assigned . unassign  . endif .
                assign component sUnitField
                  of structure                   to                   .
                check  is assigned .
                stUnit =  .
                collect stUnit into tUnits .
              endloop .
 
              if  bBreak = abap_false and ( nNumOfLines - nProcLines + 2 * lines( tUnits ) > nOtherSignLines ) .
                bBreak = abap_true .
              endif .
            endif .
          endif .
 
        when abap_false .
          "-- we need to subtract the subtotal lines
          if nI = nFirstPageLines - lines(  ) or ( nNumOfLines - nProcLines = 1 and bBreak eq abap_true ) .
            bFirstUsed = abap_true .
 
            case bBreak .
              when abap_true .
                "-- Get last line and assign break pattern
                nI = lines(  ) .
                if  is assigned . unassign  . endif .
                read table                   assigning                   index nI
                  .
                if  is assigned .
                  if  is assigned . unassign  . endif .
                  assign component cTotalField
                    of structure                     to                     .
                  if  is assigned .
                     = 'B' .
                    unassign  .
                  endif .
                  unassign  .
                endif .
            endcase .
 
            nI = 0 .
            append lines of  to  .
            free  .
 
            "-- Do it only once at last page and only if signature lines are passed in
            if nOtherSignLines is supplied and ( nNumOfLines - nProcLines < nOtherPageLines ) .
              "-- Get number of units on last page
              free tUnits .
              loop at tTable
                assigning                 from nProcLines to nNumOfLines
                .
                free stUnit .
                if  is assigned . unassign  . endif .
                assign component sUnitField
                  of structure                   to                   .
                check  is assigned .
                stUnit =  .
                collect stUnit into tUnits .
              endloop .
 
              if  bBreak = abap_false and ( nNumOfLines - nProcLines + 2 * lines( tUnits ) > nOtherSignLines ) .
                bBreak = abap_true .
              endif .
            endif .
          endif .
      endcase .
    endloop .
 
    "-- Looped entire table, and append last page subtotals
    if lines(  ) > 0 .
      append lines of  to  .
      free  .
    endif .
 
    "-- Prepare total lines
    sWhereCond = |{ cTotalField } = 'S'| .
    free  .
 
    loop at       assigning       where
        (sWhereCond)
      .
      "-- Get sumIf field
      if  is assigned . unassign  . endif .
      assign component sWhereField
        of structure         to         .
      check  is assigned .
 
      "-- Get line in sum table with that field value
      if  is assigned . unassign  . endif .
      read table         assigning         with key
          (sWhereField) =           .
      if  is NOT assigned . "-- Not found, create
        append initial line to  assigning  .
 
        "-- Add flag
        if  is assigned . unassign  . endif .
        assign component cTotalField
          of structure           to           .
        if  is assigned .
           = 'T' .
          unassign  .
        endif .
 
        "-- Add sumif field value
        assign component sWhereField
          of structure           to           .
        if  is assigned .
           =  .
          unassign  .
        endif .
      endif .
 
      "-- Now do the math, count numeric fields into sum table
      loop at tFields
        assigning         .
 
        "-- Get values
        if  is assigned . unassign  . endif .
        if     is assigned . unassign  .    endif .
        assign component -name of structure  to  .
        assign component -name of structure  to  .
 
        check  is assigned and  is assigned .
 
        "-- Sum
        read table tAddFields
          transporting no fields
          with key
            table_line = -name
            .
        case sy-subrc .
          when 0 .
             =  .
          when others .
             =  +  .
        endcase .
      endloop .
    endloop .
 
    append lines of  to  .
    free  .
    unassign  .
 
    free tTable .
    tTable =  .
    free  .
    unassign  .
  endmethod .

- To the form interface add the table creation,

"-- Create table with sums for chapter1
zCAClPDFForm=>prepareTableWithSums(
  exporting
    nFirstPageLines     = 25
    nOtherPageLines   = 31
    nFirstSignLines      = 12
    nOtherSignLines    = 25
    sUnitField               = 'VRKME'
    sAdditionalFields    = 'MSEHT'
  changing
    tTable            = tItems
) .

- Add three variables to the form (form properties):

bBreak value false

bPreviousSubtotal value false

nPageBreakAt value 0

- at the data item, define new conditional break and in the code select javascript, client and server, insert this line

this.index === parseInt(nPageBreakAt.value) && parseInt(nPageBreakAt.value) > 0

- At the data item, go to script and select initialization event (here we don't know page number, but we can redraw the item according to the TOTALFIELD value) so insert this javascript, client and server:

data.Layout1.table.dataRow::initialize - (JavaScript, both)
 
// How to make dynamic totals SAP and Adobe ways, Oh Steve you should have made Adobe go Bancrupt...
// Get the value from totalField
try {
  function convertToTotalLine(oLine) {
    // This will simply create total line, so we merge columns, change alignment, etc. alter to your needs
    oLine.C1.colSpan = "3";
    oLine.C2.presence = "hidden";
    oLine.C3.presence = "hidden";
    oLine.C1.para.hAlign = "right";
    oLine.C1.font.weight = "bold";
    oLine.C1.border.presence = "hidden";
    oLine.C1.value.text.maxChars = "0";
  }
 
  // Process only when NOT null
  if ((xfa.resolveNode('xfa.record.TITEM.DATA[' + this.index.toString() + '].TOTALFIELD') !== null) && (xfa.resolveNode('xfa.record.TITEM.DATA[' + this.index.toString() + '].TOTALFIELD').value !== null)) {
 
    switch(xfa.resolveNode('xfa.record.TITEM.DATA[' + this.index.toString() + '].TOTALFIELD').value.toString()) {
      case 'S':
        convertToTotalLine(this);
        this.C1.rawValue = 'Per page sums';
       
        if (bBreak.value === 'true') {
          bPreviousSubtotal.value = 'true';
        }
       
        break;
      case 'T':
        convertToTotalLine(this);
        this.C1.rawValue = 'Total for document';
        break;
      case 'B':
        bBreak.value = 'true';
        break;
    }
  } else {
    if ((bBreak.value === 'true') && (bPreviousSubtotal.value = 'true')) {
      nPageBreakAt.value = this.index.toString();
      bBreak.value = 'false';
      bPreviousSubtotal.value = 'false';
    }
  }
} catch(e) {
    xfa.host.messageBox(e.message);
}

- And last but not least, we need to make the conditional breaks to work, so at data item create formcalc script for event ready::form and insert this script:

data.Layout1.table.dataRow::ready:form - (FormCalc, both)

// This will make conditional break to work
$.#breakBefore.target = "contentArea";

// Instead of Header write the table header name (it can also be a subformset when you are doing e.g. 3 lines of table header
// wrap them into subform set and insert its name here
$.#breakBefore.leader = "Header";

And that's it, I am in the middle of tests at the moment, there might be a little calculation problems, but it seems to be working.

Regards,
Petr Sourek

Answers (0)