Example 6: Sales Invoice Report
Objective
In this example we want to create the replica of the built-in Sales Invoice report. The data source, however, has been reshaped and restructured to be much more easier to use and maintain, also by end users. We are going to see some cool design techniques to achieve the same kind of layout as we are used to from the original Sales invoice report.
Final result
The template uses several conditional content segments, so the final result will vary based on the values from the data in the report DDSP file. For instance, the final document in the figure below contains the table with depreciation of prepayments and the table with information of prepaid and remaining amount. This would not show in the report, if there were no prepayment information in the DDSP file.
Design considerations
Although the report results on the figure above are not very complex, the nature of the Sales invoice report is such that it must cover many different scenarios: backorders, prepayments, taxes breakdown, totals, notes and remarks, different currencies, etc.
We must therefore design the template with following segments:
- First page header.
- First page footer and default footer.
- Upper part of the invoice.
- Invoice items with additional item information in the Description column.
- Backorder table.
- Charges table.
- Sales taxes totals table.
- Payment schedule table.
- Depreciation of payments table.
- Header notes
- Packing information table.
- Remarks.
- Tax text.
- Table with reamining amount due (in case of prepayment).
- Tax totals table.
- Totals table.
- Concluding information.
We will now learn how we can implement very complex report templates using various techniques in Docentric AX.
Using tables to position data on the report template
Word offers very powerful support for tables and we will take advantage of that in our reports. Using tables it is very easy to control exact position of the text and its alignment and wrapping. Instead of tables we could use tabs, but since the text wraps dynamically, we could run into problems with text longer than the space between tabs.
So we will use tables in our example.
Table borders visibility
We have many options for setting table borders in the final report: we may set them to be invisible or we may set them to many other combinations (see Word documentation for details). In our report we will set the borders on the upper part of the report to be invisible. Other tables, e.g. invoice items, will have some visible borders.
Table borders are set on the HOME tab on the menu ribbon, in the Paragraph button group, as shown on the figure below.
To set the table borders visibility during design of the report, we choose the LAYOUT tab on the menu ribbon and the View Gridlines icon. If we already see the gridlines, we can skip this. It is recommended to have this enabled, so that we can always see dotted lines where the table borders are, even if we set them to be transparent when previewed or printed.
Table cell alignment
The LAYOUT tab on the menu ribbon contains the Alignment button group where we can set horizontal or vertical alignment of currently selected cell(s).
Table cell margins
We can set how much empty space we want around the cell borders. This is done by selecting cell(s) first and then choosing the LAYOUT tab on the menu ribbon and clicking the Cell Margins icon in the Alignment button group. The dialog window opens, where we can set each of the margins as shown on the figure below.
Paragraph layout adjustments
We want to control some of the paragraph attributes as well: spacing, splitting and visibility of the paragraph markers. One way to do it is to prepare or modify a style where we set paragraph attributes as needed and then just apply this style to the text. Another way is to set paragraph attributes to individual paragraphs.
Visibility of the paragraph markers
Sometimes it is easier to design a document if we know exactly where the paragraphs are. We can switch this on and off by selecting the HOME tab on the menu ribbon and then clicking on the paragraph mark icon in the Paragraph button group as shown of the figure below.
Paragraph settings -> Keep with next
If we want to make sure that no page break happens between the two paragraphs then we can choose this option.
Paragraph settings -> Spacing
Paragraph spacing can be used to set the space before and after the paragraph and the space between the lines in the paragraph itself as the figure below demonstrates.
Docentric AX highlights
Hide or show a table row
After we select the table row, we click on the DOCENTRIC AX tab on the menu ribbon and we then click on the If tagging element. Now we have to enter the expression which returns boolean value. This cen be either a data field or an XPath expression.
Hide or show an entire table
We need to select the entire table first and then click on the DOCENTRIC AX tab on the menu ribbon and we then click on the If tagging element. There we enter conditional expression as explained above.
Explanation to the above figure:
- The entire table must be included in the If tagging element
- Child elements under the If tagging elements show what data will be affected
- This shows the expression which will determine whether the contents under the If element will be included in the report or not
Hide or show the text including surrounding paragraphs
In the figure below we have selected the paragraph before the text and the paragraph mark immediatelly after the text.
Binding If element to values from different data sections
Below is an example how to create an XPath expression in the If tagging element which uses values from different data sections.
Binding Field element to XPath expression
By using XPath expression we can shape data just the way we need. In the example below, we are using IIF functions to show the data based on the contents of fields.
We can see that the expression above is somewhat complex. The other fields which come below the one, explained above, also have XPath expressions in the If tagging element to toggle their visibility. They should only be visible if they contain value, if they don't the entire line must also be excluded. Therefore we have implemented the If tagging element on the new line marker after each field. This requires some more effort but it gets the job done.
Example where we should introduce data shaping in report DSP class
In the example, explained above, we need to collapse each line which is empty, and because we have four fields possibly empty, we have to exercise with XPath a lot. That's why it is better to prepare this data in X++.
Some other tips and tricks for more productive report design
Cursor positioning in and out of a tagging element
When we click on a tagging element the element we can see it's borders and a small tab which tells its type. If our element is nested, we can see borders and types of parent elements.
Explanation to the above figure:
- We clicked on the If tagging element which contains only a new line marker
- All these tagging elements are nested one within another in parent-child relationships
- Element tree shows us exact element where we are at the moment
When we want to enter text or another tagging element next to already existing one, we can use arrow keys to position the cursor out of the existing element. When we are out of the element, its border and tag with the type of the element disappears. In the figure below we can see on the left that cursor is in the Field tagging element and on the right the cursor is out of the Field tagging element after we have pressed right arrow key a couple of times.
Accessing the attribute of the first element in collection in XPath expression
Sometimes we want to access an attribute from the first element in the collection. This cen be done using XPath, as shown below:
SalesInvoiceLines_Tax[1]/@ShowTaxTrans
which reads the ShowTaxTrans attribute from the first SalesInvoiceLines_Tax element in the collection.
Sorting the collection
A collection is bound to the List tagging element. We can use Element tree to select a particular List element and set up its sorting.
Explanation to the above figure:
- Selected List element
- Sort dialog button
- Buttons to add or remove sorting field
- Sorting field selection and sort order
Setting up labels for multi-language reports
For one-language reports we can just use static text for data labels. For multi-language reports we must use Label tagging elements that are bound to the labels of the data fields and records from report data source. Label tagging elements can also be bound to so-called custom labels that are defined per report in the report setup data. We usually introduced custom labels for a report when we need different labels than those assigned to data fields and records included in report data source. At runtime all used Label tagging elements are replaced with the corresponding translations in the report runtime language.
Read more about multilingual reports >>
Using XPath with the Field tagging element
Field taging elements get their values from the data fields in the data source package file. One of the great features of Docentric AX is the XPath support in Field tagging elements. In this way it is possible to manipulate and shape the data in many ways.
Step by step instructions
We will create a new document and make it a Docentric AX template by clicking the Use As Template button on the DOCENTRIC AX ribbon tab. The file dialog will open where we must choose corresponding Data Source Package file (.ddsp).
Step 1: Headers and Footers
The document will have different first page header and footer, so we set this first (PAGE LAYOUT | Margins | Custom Margins | Layout | Different First Page).
First page header:
Data | Tagging Element | Binding Source | Binding Path |
---|---|---|---|
Company name | Field | General Data | CurrentCompany/@Name |
Address | Field | General Data | CurrentCompany/PrimaryPostalAddress/@Address |
Logo | Image | General Data | CurrentCompany/@Logo |
First page and default footer: see Example 3 for detailed instructions.
Step 2: Upper body of the invoice template
The upper part of the invoice looks like if it consisted of two columns with several rows in each of them. Each of this rows contain a label and a value from the data source. We are going to create a table with 4 columns to achieve this layout on the template. Leftmost two columns will contain labels and values on the left side and rightmost two columns will contain labels and values on the right side of the upper body.
The cell where the name and the address of the recepient is displayed, will consist of vertically merged cells to provide enough space for the data. Then we will add labels and data fields into those cells, so that we get the result as on the figure below:
Left side of the upper body:
Data | Tagging Element | Binding Source | Binding Path |
---|---|---|---|
Invoice recipient's name | Field | Main Data | SalesInvoiceHeader/@InvoicingName |
Invoice recipient's address | Field | Main Data | SalesInvoiceHeader/@InvoicingAddress |
Contact | Field | Main Data | SalesInvoiceHeader/@ContactPersonName |
VAT | Field | Main Data | SalesInvoiceHeader/@VatNum |
Packing duty license number | Label | Main Data | SalesInvoiceHeader/TaxLicenseNum/@SYS58658 |
Packing duty license number | Field | Main Data | SalesInvoiceHeader/@TaxLicenseNum |
List code | Label | Main Data | SalesInvoiceHeader/Listcode/@SYS21837 |
List code | Field | Main Data | SalesInvoiceHeader/Listcode/@Text |
Conditional content:
Data | Conditional Expression |
---|---|
VAT fixed-text label | SalesInvoiceHeaderExt/Flags/@PrintFreeTextInvoiceVATNum |
SalesInvoiceHeader/@VatNum data field | SalesInvoiceHeaderExt/Flags/@PrintFreeTextInvoiceVATNum |
ListCode custom label | (data-source('Parameters')/@SalesInvoiceDS_CountryRegionISOCode = 'FI' or data-source('Parameters')/@SalesInvoiceDS_CountryRegionISOCode = 'SE') and (SalesInvoiceHeader/Listcode/@Name = 'TriangularEUTrade' or SalesInvoiceHeader/Listcode/@Name = 'TriangularProductionOnToll') |
SalesInvoiceHeader/Listcode/@Text data field | (data-source('Parameters')/@SalesInvoiceDS_CountryRegionISOCode = 'FI' or data-source('Parameters')/@SalesInvoiceDS_CountryRegionISOCode = 'SE') and (SalesInvoiceHeader/Listcode/@Name = 'TriangularEUTrade' or SalesInvoiceHeader/Listcode/@Name = 'TriangularProductionOnToll') |
Right side of the upper body:
Data | Tagging Element | Binding Source | Binding Path |
---|---|---|---|
Telephone | Label | General Data | CurrentCompany/Phone/@SYS7869 |
Telephone | Field | General Data | CurrentCompany/@Phone |
Fax | Label | General Data | CurrentCompany/Telefax/@SYS7888 |
Fax | Field | General Data | CurrentCompany/@Telefax |
IBAN | Label | General Data | CurrentCompany/BankAccount/IBAN/@SYS71686 |
IBAN | Field | General Data | CurrentCompany/BankAccount/@IBAN |
Tax registration number | Label | General Data | CurrentCompany/RegistrationNumbers/CoRegNum/@SYS969 |
Tax registration number | Field | General Data | CurrentCompany/RegistrationNumbers/@CoRegNum |
VAT | Field | General Data | CurrentCompany/SalesTax/@VATNum |
Document title | Field | Parameters | @DocumentTitle |
Reference number | Field | General Data | SalesInvoiceHeader/@InvoiceReferenceNumber |
Number | Field | Main Data | SalesInvoiceHeaderExt/@InvoiceId |
Invoice date | Field | Main Data | SalesInvoiceHeader/@InvoiceDate |
Sales order | Label | Main Data | SalesInvoiceHeader/SalesId/@SYS9694 |
Sales order | Field | Main Data | SalesInvoiceHeader/@SalesId |
Requisition | Field | Main Data | SalesInvoiceHeader/@PurchaseOrder |
Your reference | Field | Main Data | SalesInvoiceHeader/@CustomerRef |
Our reference | Field | Main Data | SalesInvoiceHeader/@SalesAdministrator |
Payment | Label | Main Data | SalesInvoiceHeader/PaymentCondition/@SYS828 |
Payment | Field | Main Data | SalesInvoiceHeader/@PaymentCondition |
Invoice account | Label | Main Data | SalesInvoiceHeader/InvoiceAccount/@SYS25708 |
Invoice account | Field | Main Data | SalesInvoiceHeader/@InvoiceAccount |
Payment reference | Label | Main Data | SalesInvoiceHeader/PaymentReference/@SYS22514 |
Payment reference | Field | Main Data | SalesInvoiceHeader/@PaymentReference |
Payment ID | Label | Main Data | SalesInvoiceHeader/PaymId/@SYS11443 |
Payment ID | Field | Main Data | SalesInvoiceHeader/@PaymId |
Conditional content:
Data | Conditional Expression |
---|---|
The entire table row containing 'Reference number' fixed-text label and SalesInvoiceHeader/@InvoiceReferenceNumber data field | SalesInvoiceHeaderExt/Flags/@CustInvoiceRefNum_FI |
The entire table row containing 'Payment reference' custom label and SalesInvoiceHeader/@PaymentReference data field | SalesInvoiceHeaderExt/Misc/GiroType/@Name = 'BelSMS101' or SalesInvoiceHeaderExt/Misc/GiroType/@Name = 'BelSMS102' |
The entire table row containing 'Payment ID' custom label and SalesInvoiceHeader/@PaymId data field | SalesInvoiceHeaderExt/Flags/@IsPaymIdVisible |
Invoice items table:
Invoice items table lists invoice items, but we must apply condition, to get only those items which we want in this table (items collection includes other lines, e.g. prepayments, which we don't want to appear in this table).
Data | Tagging Element | Binding Source | Binding Path |
---|---|---|---|
Item Number | Label | Main Data | SalesInvoiceLines/ItemId/@SYS12836 |
Description | Label (fixed text) | "Description" | |
Quantity | Label | Main Data | SalesInvoiceLines/Qty/@SYS14578 |
Unit | Label | Main Data | SalesInvoiceLines/SalesUnitTxt/@SYS190813 |
Unit price | Label (fixed text) | "Unit price" | |
Discount percent | Label | Main Data | SalesInvoiceLines/DiscPercent/@SYS7813 |
Discount | Label | Main Data | SalesInvoiceLines/DiscountAmount/@SYS11829 |
Amount | Label | Main Data | SalesInvoiceLines/LineAmountInclTax/@SYS6928 |
Lines collection | List | Main Data | SalesInvoiceLines |
Line header | Field | Main Data | SalesInvoiceLines/@LineHeader |
ItemId | Field | Main Data | SalesInvoiceLines/@ItemId |
Name | Field | Main Data | SalesInvoiceLines/@Name |
ExternalItem | Field | Main Data | SalesInvoiceLines/@ExternalItem |
InventDimPrint | Field | Main Data | SalesInvoiceLines/@InventDimPrint |
Notes | Field | Main Data | SalesInvoiceLines/@Notes |
PackingSlip | Field | Main Data | SalesInvoiceLines/PackingSlip/@PackingSlip |
Qty | Field | Main Data | SalesInvoiceLines/@Qty |
U | Field | Main Data | SalesInvoiceLines/@SalesUnitTxt |
SalesPrice | Field | Main Data | SalesInvoiceLines/@SalesPrice |
DiscP | Field | Main Data | SalesInvoiceLines/@DiscPercent |
DiscAm | Field | Main Data | SalesInvoiceLines/@DiscountAmount |
Amount | Field | Main Data | SalesInvoiceLines/@LineAmountInclTax |
Conditional content:
Data | Conditional Expression |
---|---|
Entire sales items table | count(SalesInvoiceLines[@LineHeader != '' or @ExternalItemId != '' or InventDim/@InventDimPrint != '' or @Notes != '' or PackingSlip/@PackingSlip != '']) > 0 |
LineHeader field | @LineHeader != '' |
Additional item information | @ExternalItemId != '' or InventDim/@InventDimPrint != '' or @Notes != '' or PackingSlip/@PackingSlip != '' |
Additional item information - new line marker after the first line |
(@ExternalItemId != '' or InventDim/@InventDimPrint != '') and (@Notes != '' or PackingSlip/@PackingSlip != '') |
Additional item information - new line marker after the second line |
@Notes != '' and PackingSlip/@PackingSlip != '' |
Resources
IN THIS ARTICLE
- Objective
- Final result
- Design considerations
- Using tables to position data on the report template
- Paragraph layout adjustments
- Docentric AX highlights
- Some other tips and tricks for more productive report design
- Setting up labels for multi-language reports
- Using XPath with the Field tagging element
- Step by step instructions
- Resources