2
.. i18n: OpenOffice.org reports
3
.. i18n: ======================
9
.. i18n: **The document flow**
14
.. i18n: OpenOffice.org reports are the most commonly used report formats. OpenOffice.org Writer is used (in combination with [[1]]) to generate a RML template, which in turn is used to generate a pdf printable report.
17
OpenOffice.org reports are the most commonly used report formats. OpenOffice.org Writer is used (in combination with [[1]]) to generate a RML template, which in turn is used to generate a pdf printable report.
19
.. i18n: .. figure:: images/ooo_report_overview.png
21
.. i18n: :align: center
24
.. figure:: images/ooo_report_overview.png
28
.. i18n: **The internal process**
31
**The internal process**
33
.. i18n: .. figure:: images/process_ooo.png
35
.. i18n: :align: center
38
.. figure:: images/process_ooo.png
42
.. i18n: **The .SXW template file**
45
**The .SXW template file**
47
.. i18n: * We use a .SXW file for the template, which is the OpenOffice 1.0 format. The template includes expressions in brackets or OpenOffice fields to point where the data from the OpenERP server will be filled in. This document is only used for developers, as a help-tool to easily generate the .RML file. OpenERP does not need this .SXW file to print reports.
50
* We use a .SXW file for the template, which is the OpenOffice 1.0 format. The template includes expressions in brackets or OpenOffice fields to point where the data from the OpenERP server will be filled in. This document is only used for developers, as a help-tool to easily generate the .RML file. OpenERP does not need this .SXW file to print reports.
52
.. i18n: **The .RML template**
57
.. i18n: * We generate a .RML file from the .SXW file using Open SXW2RML. A .RML file is a XML format that represent a .PDF document. It can be converted to a .PDF after. We use RML for more easy processing: XML syntax seems to be more common than PDF syntax.
60
* We generate a .RML file from the .SXW file using Open SXW2RML. A .RML file is a XML format that represent a .PDF document. It can be converted to a .PDF after. We use RML for more easy processing: XML syntax seems to be more common than PDF syntax.
62
.. i18n: **The report engine**
67
.. i18n: * The Open Report Engine process the .RML file inserting data from the database at each expression.
70
* The Open Report Engine process the .RML file inserting data from the database at each expression.
72
.. i18n: in the .RML file will be replaced by the name of the country of the partner of the printed invoice. This report engine produce the same .RML file where all expressions have been replaced by real data.
75
in the .RML file will be replaced by the name of the country of the partner of the printed invoice. This report engine produce the same .RML file where all expressions have been replaced by real data.
77
.. i18n: **The final document**
80
**The final document**
82
.. i18n: * Finally the .RML file is converted to PDF or HTML as needed, using OpenReport's scripts.
85
* Finally the .RML file is converted to PDF or HTML as needed, using OpenReport's scripts.
87
.. i18n: Creating a SXW
88
.. i18n: --------------
94
.. i18n: You can design reports using *OpenOffice*. Here, as an example, is the file **server/bin/addons/sale/report/order.sxw**.
97
You can design reports using *OpenOffice*. Here, as an example, is the file **server/bin/addons/sale/report/order.sxw**.
99
.. i18n: .. figure:: images/writer_report.png
101
.. i18n: :align: center
104
.. figure:: images/writer_report.png
108
.. i18n: .. _dynamic-report-content:
110
.. i18n: Dynamic content in OpenOffice reports
111
.. i18n: -------------------------------------
114
.. _dynamic-report-content:
116
Dynamic content in OpenOffice reports
117
-------------------------------------
119
.. i18n: **Dynamic content**
124
.. i18n: In the .SXW/.RML reports, you can put some Python code that accesses the OpenERP objects in brackets. The context of the code (the variable's values you can use) is the following:
127
In the .SXW/.RML reports, you can put some Python code that accesses the OpenERP objects in brackets. The context of the code (the variable's values you can use) is the following:
129
.. i18n: **Available variables**
132
**Available variables**
134
.. i18n: Here are Python objects/variables available:
137
Here are Python objects/variables available:
139
.. i18n: * **objects** : the list of objects to be printed (invoices for example).
140
.. i18n: * **data** : comes from the wizard
141
.. i18n: * **time** : the Python time module (see Python documentation for more information).
142
.. i18n: * **user** : the user object launching the report.
145
* **objects** : the list of objects to be printed (invoices for example).
146
* **data** : comes from the wizard
147
* **time** : the Python time module (see Python documentation for more information).
148
* **user** : the user object launching the report.
150
.. i18n: **Available functions**
153
**Available functions**
155
.. i18n: Here are Python functions you can use:
158
Here are Python functions you can use:
160
.. i18n: * **setLang('fr')** : change the language used in automated translation (fields...).
161
.. i18n: * **repeatIn(list, varname[, tagname])** : repeat the current part of the template
162
.. i18n: (whole document, current section, current row in the table) for each
163
.. i18n: object in the list. Use varname in the template's tags. Since versions
164
.. i18n: 4.1.X, you can use an optional third argument that is the name of the
165
.. i18n: .RML tag you want to loop on.
166
.. i18n: * **setTag('para','xpre')** : replace the enclosing RML tag (usually 'para') with an other (xpre is a preformatted paragraph), in the (converted from sxw)rml document (?)
167
.. i18n: * **removeParentNode('tr')** : removes the parent node of type 'tr', this parameter is usually used together with a conditional (see examples below)
170
* **setLang('fr')** : change the language used in automated translation (fields...).
171
* **repeatIn(list, varname[, tagname])** : repeat the current part of the template
172
(whole document, current section, current row in the table) for each
173
object in the list. Use varname in the template's tags. Since versions
174
4.1.X, you can use an optional third argument that is the name of the
175
.RML tag you want to loop on.
176
* **setTag('para','xpre')** : replace the enclosing RML tag (usually 'para') with an other (xpre is a preformatted paragraph), in the (converted from sxw)rml document (?)
177
* **removeParentNode('tr')** : removes the parent node of type 'tr', this parameter is usually used together with a conditional (see examples below)
179
.. i18n: Example of useful tags:
182
Example of useful tags:
184
.. i18n: * **[[ repeatIn(objects,'o') ]]** : Loop on each objects selected for the print
185
.. i18n: * **[[ repeatIn(o.invoice_line,'l') ]]** : Loop on every line
186
.. i18n: * **[[ repeatIn(o.invoice_line,'l', 'td') ]]** : Loop on every line and make
187
.. i18n: a new table cell for each line.
188
.. i18n: * **[[ (o.prop=='draft')and 'YES' or 'NO' ]]** : Print YES or NO according the field 'prop'
189
.. i18n: * **[[ round(o.quantity * o.price * 0.9, 2) ]]** : Operations are OK.
190
.. i18n: * **[[ '%07d' % int(o.number) ]]** : Number formating
191
.. i18n: * **[[ reduce(lambda x, obj: x+obj.qty , list , 0 ) ]]** : Total qty of list (try "objects" as list)
192
.. i18n: * **[[ user.name ]]** : user name
193
.. i18n: * **[[ setLang(o.partner_id.lang) ]]** : Localized printings
194
.. i18n: * **[[ time.strftime('%d/%m/%Y') ]]** : Show the time in format=dd/MM/YYYY, check python doc for more about "%d", ...
195
.. i18n: * **[[ time.strftime(time.ctime()[0:10]) ]]** or **[[ time.strftime(time.ctime()[-4:]) ]]** : Prints only date.
196
.. i18n: * **[[ time.ctime() ]]** : Prints the actual date & time
197
.. i18n: * **[[ time.ctime().split()[3] ]]** : Prints only time
198
.. i18n: * **[[ o.type in ['in_invoice', 'out_invoice'] and 'Invoice' or removeParentNode('tr') ]]** : If the type is 'in_invoice' or 'out_invoice' then the word 'Invoice' is printed, if it's neither the first node above it of type 'tr' will be removed.
201
* **[[ repeatIn(objects,'o') ]]** : Loop on each objects selected for the print
202
* **[[ repeatIn(o.invoice_line,'l') ]]** : Loop on every line
203
* **[[ repeatIn(o.invoice_line,'l', 'td') ]]** : Loop on every line and make
204
a new table cell for each line.
205
* **[[ (o.prop=='draft')and 'YES' or 'NO' ]]** : Print YES or NO according the field 'prop'
206
* **[[ round(o.quantity * o.price * 0.9, 2) ]]** : Operations are OK.
207
* **[[ '%07d' % int(o.number) ]]** : Number formating
208
* **[[ reduce(lambda x, obj: x+obj.qty , list , 0 ) ]]** : Total qty of list (try "objects" as list)
209
* **[[ user.name ]]** : user name
210
* **[[ setLang(o.partner_id.lang) ]]** : Localized printings
211
* **[[ time.strftime('%d/%m/%Y') ]]** : Show the time in format=dd/MM/YYYY, check python doc for more about "%d", ...
212
* **[[ time.strftime(time.ctime()[0:10]) ]]** or **[[ time.strftime(time.ctime()[-4:]) ]]** : Prints only date.
213
* **[[ time.ctime() ]]** : Prints the actual date & time
214
* **[[ time.ctime().split()[3] ]]** : Prints only time
215
* **[[ o.type in ['in_invoice', 'out_invoice'] and 'Invoice' or removeParentNode('tr') ]]** : If the type is 'in_invoice' or 'out_invoice' then the word 'Invoice' is printed, if it's neither the first node above it of type 'tr' will be removed.
217
.. i18n: One more interesting tag: if you want to print out the creator of an entry
218
.. i18n: (create_uid) or the last one who wrote on an entry (write_uid) you have to add
219
.. i18n: something like this to the class your report refers to:
222
One more interesting tag: if you want to print out the creator of an entry
223
(create_uid) or the last one who wrote on an entry (write_uid) you have to add
224
something like this to the class your report refers to:
226
.. i18n: .. code-block::python
228
.. i18n: 'create_uid': fields.many2one('res.users', 'User', readonly=1)
231
.. code-block::python
233
'create_uid': fields.many2one('res.users', 'User', readonly=1)
235
.. i18n: and then in your report it's like this to print out the corresponding name:
238
and then in your report it's like this to print out the corresponding name:
240
.. i18n: .. code-block::python
242
.. i18n: o.create_uid.name
245
.. code-block::python
249
.. i18n: Sometimes you might want to print out something only if a certain condition is
250
.. i18n: met. You can construct it with the python logical operators "not", "and" and
251
.. i18n: "or". Because every object in python has a logical value (TRUE or FALSE) you can
252
.. i18n: construct something like this:
255
Sometimes you might want to print out something only if a certain condition is
256
met. You can construct it with the python logical operators "not", "and" and
257
"or". Because every object in python has a logical value (TRUE or FALSE) you can
258
construct something like this:
260
.. i18n: .. code-block::python
262
.. i18n: (o.prop=='draft') and 'YES' or 'NO' #prints YES or NO
265
.. code-block::python
267
(o.prop=='draft') and 'YES' or 'NO' #prints YES or NO
269
.. i18n: It works like this: `and` is higher priority than `or`, so that expression is
270
.. i18n: equivalent to this one:
273
It works like this: `and` is higher priority than `or`, so that expression is
274
equivalent to this one:
276
.. i18n: .. code-block::python
278
.. i18n: ((o.prop=='draft') and 'YES') or 'NO'
280
.. i18n: If `o.prop` is `'draft'`, then it evaluates like this:
281
.. i18n: #. `o.prop == 'draft'` is `True`.
282
.. i18n: #. `True and 'YES'` is `'YES'`. Because the left side is a "true" value, the
283
.. i18n: `and` expression evaluates to the right side.
284
.. i18n: #. `'YES' or 'NO'` is `'YES'`. Because the left side is a "true" value, the
285
.. i18n: `or` expression short cuts and ignores the right side. It evaluates to
286
.. i18n: the left side.
289
.. code-block::python
291
((o.prop=='draft') and 'YES') or 'NO'
293
If `o.prop` is `'draft'`, then it evaluates like this:
294
#. `o.prop == 'draft'` is `True`.
295
#. `True and 'YES'` is `'YES'`. Because the left side is a "true" value, the
296
`and` expression evaluates to the right side.
297
#. `'YES' or 'NO'` is `'YES'`. Because the left side is a "true" value, the
298
`or` expression short cuts and ignores the right side. It evaluates to
301
.. i18n: If `o.prop` is something else like `'confirm'`, then it evaluates like this:
302
.. i18n: #. `o.prop == 'draft'` is `False`.
303
.. i18n: #. `False and 'YES'` is `False`. Because the left side is a "false" value, the
304
.. i18n: `and` expression short cuts and ignores the right side. It evaluates to
305
.. i18n: the left side.
306
.. i18n: #. `False or 'NO'` is `'NO'`. Because the left side is a "false" value, the
307
.. i18n: `or` expression evaluates to the right side.
310
If `o.prop` is something else like `'confirm'`, then it evaluates like this:
311
#. `o.prop == 'draft'` is `False`.
312
#. `False and 'YES'` is `False`. Because the left side is a "false" value, the
313
`and` expression short cuts and ignores the right side. It evaluates to
315
#. `False or 'NO'` is `'NO'`. Because the left side is a "false" value, the
316
`or` expression evaluates to the right side.
318
.. i18n: One can use very complex structures. To learn more, see the python manual's
319
.. i18n: section on `Python's boolean operators`_.
322
One can use very complex structures. To learn more, see the python manual's
323
section on `Python's boolean operators`_.
325
.. i18n: python function "filter" can... filter: try something like:
328
python function "filter" can... filter: try something like:
330
.. i18n: repeatIn(filter( lambda l: l.product_id.type=='service' ,o.invoice_line), 'line')
333
repeatIn(filter( lambda l: l.product_id.type=='service' ,o.invoice_line), 'line')
335
.. i18n: for printing only product with type='service' in a line's section.
338
for printing only product with type='service' in a line's section.
340
.. i18n: To display binary field image on report (to be checked)
343
To display binary field image on report (to be checked)
345
.. i18n: [[ setTag('para','image',{'width':'100.0','height':'80.0'}) ]] o.image or setTag('image','para')
349
[[ setTag('para','image',{'width':'100.0','height':'80.0'}) ]] o.image or setTag('image','para')
359
.. i18n: Open Report Manual
360
.. i18n: ++++++++++++++++++
373
.. i18n: The OpenERP's report engine.
376
The OpenERP's report engine.
378
.. i18n: Open Report is a module that allows you to render high quality PDF document
379
.. i18n: from an OpenOffice template (.sxw) and any relational database. It can be used
380
.. i18n: as an OpenERP module or as a standalone program.
383
Open Report is a module that allows you to render high quality PDF document
384
from an OpenOffice template (.sxw) and any relational database. It can be used
385
as an OpenERP module or as a standalone program.
387
.. i18n: SXW to RML script setup - Windows users
388
.. i18n: """""""""""""""""""""""""""""""""""""""
391
SXW to RML script setup - Windows users
392
"""""""""""""""""""""""""""""""""""""""
394
.. i18n: In order to use the 'tiny_sxw2rml.py' Python script you need the following packages installed:
397
In order to use the 'tiny_sxw2rml.py' Python script you need the following packages installed:
399
.. i18n: * Python (http://www.python.org)
400
.. i18n: * ReportLab (http://www.reportlab.org)/(Installation)
401
.. i18n: * Libxml for Python (http://users.skynet.be/sbi/libxml-python)
404
* Python (http://www.python.org)
405
* ReportLab (http://www.reportlab.org)/(Installation)
406
* Libxml for Python (http://users.skynet.be/sbi/libxml-python)
408
.. i18n: SXW to RML script setup - Linux (Open source) users
409
.. i18n: """""""""""""""""""""""""""""""""""""""""""""""""""
412
SXW to RML script setup - Linux (Open source) users
413
"""""""""""""""""""""""""""""""""""""""""""""""""""
415
.. i18n: The **tiny_sxw2rml.py** can be found in the **base_report_designer** OpenERP module at this location::
417
.. i18n: server/bin/addons/base_report_designer/wizard/tiny_sxw2rml/tiny_sxw2rml.py
420
The **tiny_sxw2rml.py** can be found in the **base_report_designer** OpenERP module at this location::
422
server/bin/addons/base_report_designer/wizard/tiny_sxw2rml/tiny_sxw2rml.py
424
.. i18n: Ensure normalized_oo2rml.xsl is available to tiny_sxw2rml otherwise you will get an error like:
427
Ensure normalized_oo2rml.xsl is available to tiny_sxw2rml otherwise you will get an error like:
429
.. i18n: * failed to load external entity normalized_oo2rml.xsl
432
* failed to load external entity normalized_oo2rml.xsl
434
.. i18n: Running tiny_sxw2rml
435
.. i18n: """"""""""""""""""""
441
.. i18n: When you have all that installed just edit your report template and run the script with the following command:
444
.. i18n: tiny_sxw2rml.py template.sxw > template.rml
447
When you have all that installed just edit your report template and run the script with the following command:
450
tiny_sxw2rml.py template.sxw > template.rml
452
.. i18n: Note: **tiny_sxw2rml.py** help suggests that you specify the output file with: "-o OUTPUT" but this does not seem to work as of V0.9.3
455
Note: **tiny_sxw2rml.py** help suggests that you specify the output file with: "-o OUTPUT" but this does not seem to work as of V0.9.3
457
.. i18n: OpenERP Server PDF Output
458
.. i18n: --------------------------
461
OpenERP Server PDF Output
462
--------------------------
464
.. i18n: Server PDF Output
465
.. i18n: +++++++++++++++++
473
.. i18n: To generate the pdf from the rml file, OpenERP needs a rml parser.
478
To generate the pdf from the rml file, OpenERP needs a rml parser.
482
.. i18n: The parsers are generally put into the report folder of the module. Here is the code for the sale order report:
487
The parsers are generally put into the report folder of the module. Here is the code for the sale order report:
489
.. i18n: .. code-block:: python
492
.. i18n: from report import report_sxw
494
.. i18n: class order(report_sxw.rml_parse):
495
.. i18n: def __init__(self, cr, uid, name, context):
496
.. i18n: super(order, self).__init__(cr, uid, name, context)
497
.. i18n: self.localcontext.update({
498
.. i18n: 'time': time,
501
.. i18n: report_sxw.report_sxw('report.sale.order', 'sale.order',
502
.. i18n: 'addons/sale/report/order.rml', parser=order, header=True)
505
.. code-block:: python
508
from report import report_sxw
510
class order(report_sxw.rml_parse):
511
def __init__(self, cr, uid, name, context):
512
super(order, self).__init__(cr, uid, name, context)
513
self.localcontext.update({
517
report_sxw.report_sxw('report.sale.order', 'sale.order',
518
'addons/sale/report/order.rml', parser=order, header=True)
520
.. i18n: The parser inherit from the **report_sxw.rml_parse** object and it add to the localcontext, the function time so it will be possible to call it in the report.
523
The parser inherit from the **report_sxw.rml_parse** object and it add to the localcontext, the function time so it will be possible to call it in the report.
525
.. i18n: After an instance of **report_sxw.report_sxw** is created with the parameters:
528
After an instance of **report_sxw.report_sxw** is created with the parameters:
530
.. i18n: * the name of the report
531
.. i18n: * the object name on which the report is defined
532
.. i18n: * the path to the rml file
533
.. i18n: * the parser to use for the report (by default rml_parse)
534
.. i18n: * a boolean to add or not the company header on the report (default True)
537
* the name of the report
538
* the object name on which the report is defined
539
* the path to the rml file
540
* the parser to use for the report (by default rml_parse)
541
* a boolean to add or not the company header on the report (default True)
543
.. i18n: The xml definition
544
.. i18n: """"""""""""""""""
550
.. i18n: To be visible from the client, the report must be declared in an xml file (generally: "module_name"_report.xml) that must be put in the **__terp__.py** file
553
To be visible from the client, the report must be declared in an xml file (generally: "module_name"_report.xml) that must be put in the **__terp__.py** file
555
.. i18n: Here is an example for the sale order report:
558
.. i18n: <?xml version="1.0"?>
562
.. i18n: id="report_sale_order"
563
.. i18n: string="Print Order"
564
.. i18n: model="sale.order"
565
.. i18n: name="sale.order"
566
.. i18n: rml="sale/report/order.rml"
567
.. i18n: auto="False"/>
568
.. i18n: header="False"/>
573
Here is an example for the sale order report:
576
<?xml version="1.0"?>
580
id="report_sale_order"
584
rml="sale/report/order.rml"
590
.. i18n: The arguments are:
595
.. i18n: * **id**: the id of the report like any xml tag in OpenERP
596
.. i18n: * **string**: the string that will be display on the Client button
597
.. i18n: * **model**: the object on which the report will run
598
.. i18n: * **name**: the name of the report without the first "report."
599
.. i18n: * **rml**: the path to the rml file
600
.. i18n: * **auto**: boolean to specify if the server must generate a default parser or not
601
.. i18n: * **header**: allows to enable or disable the report header. To edit them for a specific company, go to: Administration -> Users -> Company's structure -> Companies. There, select and edit your company: the "Header/Footer" tab allows you to edit corporate header/footer.
604
* **id**: the id of the report like any xml tag in OpenERP
605
* **string**: the string that will be display on the Client button
606
* **model**: the object on which the report will run
607
* **name**: the name of the report without the first "report."
608
* **rml**: the path to the rml file
609
* **auto**: boolean to specify if the server must generate a default parser or not
610
* **header**: allows to enable or disable the report header. To edit them for a specific company, go to: Administration -> Users -> Company's structure -> Companies. There, select and edit your company: the "Header/Footer" tab allows you to edit corporate header/footer.
612
.. i18n: .. _Python's boolean operators: http://docs.python.org/library/stdtypes.html#boolean-operations-and-or-not
615
.. _Python's boolean operators: http://docs.python.org/library/stdtypes.html#boolean-operations-and-or-not