Browse Source

report_py3o_fusion_server: Add support for PDF Export options of libreoffice

pull/211/head
Alexis de Lattre 7 years ago
parent
commit
d8a253de36
  1. 2
      report_py3o/README.rst
  2. 13
      report_py3o_fusion_server/README.rst
  3. 2
      report_py3o_fusion_server/__manifest__.py
  4. 11
      report_py3o_fusion_server/demo/py3o_pdf_options.xml
  5. 1
      report_py3o_fusion_server/models/__init__.py
  6. 4
      report_py3o_fusion_server/models/ir_actions_report_xml.py
  7. 316
      report_py3o_fusion_server/models/py3o_pdf_options.py
  8. 6
      report_py3o_fusion_server/models/py3o_report.py
  9. 4
      report_py3o_fusion_server/models/py3o_server.py
  10. 6
      report_py3o_fusion_server/security/ir.model.access.csv
  11. 5
      report_py3o_fusion_server/tests/test_report_py3o_fusion_server.py
  12. 1
      report_py3o_fusion_server/views/ir_report.xml
  13. 149
      report_py3o_fusion_server/views/py3o_pdf_options.xml
  14. 2
      report_py3o_fusion_server/views/py3o_server.xml

2
report_py3o/README.rst

@ -19,6 +19,8 @@ The key advantages of a Libreoffice based reporting engine are:
* If you want your users to be able to modify the document after its generation by Odoo, just configure the document with ODT output (or DOC or DOCX) and the user will be able to modify the document with Libreoffice (or Word) after its generation by Odoo.
* Easy development of spreadsheet reports in ODS format (XLS output possible).
This module *report_py3o* is the base module for the Py3o reporting engine. If used alone, it will spawn a libreoffice process for each ODT to PDF (or ODT to DOCX, ..) document conversion. This is slow and can become a problem if you have a lot of reports to convert from ODT to another format. In this case, you should consider the additionnal module *report_py3o_fusion_server* which is designed to work with a libreoffice daemon. With *report_py3o_fusion_server*, the technical environnement is more complex to setup because you have to install additionnal software components and run 2 daemons, but you have much better performances and you can configure the libreoffice PDF export options in Odoo (allows to generate PDF forms, PDF/A documents, password-protected PDFs, watermarked PDFs, etc.).
This reporting engine is an alternative to `Aeroo <https://github.com/aeroo-community/aeroo_reports>`_: these two reporting engines have similar features but their implementation is entirely different. You cannot use aeroo templates as drop in replacement though, you'll have to change a few details.
Installation

13
report_py3o_fusion_server/README.rst

@ -6,7 +6,14 @@
Py3o Report Engine - Fusion server support
==========================================
This module was written to let a py3o fusion server handle format conversion instead of local libreoffice.
This module was written to let a py3o fusion server handle format conversion instead of local libreoffice. If you install this module above the *report_py3o* module, you will have to deploy additionnal software components and run 3 daemons (libreoffice, py3o.fusion and py3o.renderserver). This additionnal complexiy comes with several advantages:
* much better performances (libreoffice runs permanently in the background, no need to spawn a new libreoffice instance upon every document conversion).
* ability to configure PDF export options in Odoo. This will allow you to generate:
* PDF forms
* password-protected PDF documents
* PDF/A documents (required by some electronic invoicing standards such as Factur-X)
* watermarked PDF documents
Installation
============
@ -54,11 +61,11 @@ At the end, with the dependencies, you should have the following py3o python lib
% pip freeze | grep py3o
py3o.formats==0.3
py3o.fusion==0.8.7
py3o.fusion==0.8.8
py3o.renderclient==0.2
py3o.renderers.juno==0.8
py3o.renderserver==0.5.1
py3o.template==0.9.11
py3o.template==0.9.12
py3o.types==0.1.1
Start the Py3o Fusion server:

2
report_py3o_fusion_server/__manifest__.py

@ -20,11 +20,13 @@
},
'demo': [
"demo/report_py3o.xml",
"demo/py3o_pdf_options.xml",
],
'data': [
"views/ir_report.xml",
'security/ir.model.access.csv',
'views/py3o_server.xml',
'views/py3o_pdf_options.xml',
],
'installable': True,
}

11
report_py3o_fusion_server/demo/py3o_pdf_options.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="py3o_pdf_options_pdfa" model="py3o.pdf.options">
<field name="name">PDF/A (for Factur-X invoices)</field>
<field name="pdfa" eval="True"/>
</record>
</odoo>

1
report_py3o_fusion_server/models/__init__.py

@ -2,5 +2,6 @@
# Copyright 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import ir_actions_report_xml
from . import py3o_pdf_options
from . import py3o_report
from . import py3o_server

4
report_py3o_fusion_server/models/ir_actions_report_xml.py

@ -39,3 +39,7 @@ class IrActionsReportXml(models.Model):
py3o_server_id = fields.Many2one(
"py3o.server",
"Fusion Server")
pdf_options_id = fields.Many2one(
'py3o.pdf.options', string='PDF Options', ondelete='restrict',
help="PDF options can be set per report, but also per Py3o Server. "
"If both are defined, the options on the report are used.")

316
report_py3o_fusion_server/models/py3o_pdf_options.py

@ -0,0 +1,316 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
import logging
logger = logging.getLogger(__name__)
class Py3oPdfOptions(models.Model):
_name = 'py3o.pdf.options'
_description = 'Define PDF export options for Libreoffice'
name = fields.Char(required=True)
# GENERAL TAB
# UseLosslessCompression (bool)
image_compression = fields.Selection([
('lossless', 'Lossless Compression'),
('jpeg', 'JPEG Compression'),
], string='Image Compression', default='jpeg')
# Quality (int)
image_jpeg_quality = fields.Integer(
string='Image JPEG Quality', default=90,
help="Enter a percentage between 0 and 100.")
# ReduceImageResolution (bool) and MaxImageResolution (int)
image_reduce_resolution = fields.Selection([
('none', 'Disable'),
('75', '75 DPI'),
('150', '150 DPI'),
('300', '300 DPI'),
('600', '600 DPI'),
('1200', '1200 DPI'),
], string='Reduce Image Resolution', default='300')
watermark = fields.Boolean('Sign With Watermark')
# Watermark (string)
watermark_text = fields.Char('WaterMark Text')
# UseTaggedPDF (bool)
tagged_pdf = fields.Boolean('Tagged PDF (add document structure)')
# SelectPdfVersion (int)
# 0 = PDF 1.4 (default selection).
# 1 = PDF/A-1 (ISO 19005-1:2005)
pdfa = fields.Boolean(
'Archive PDF/A-1a (ISO 19005-1)',
help="If you enable this option, you will not be able to "
"password-protect the document or apply other security settings.")
# ExportFormFields (bool)
pdf_form = fields.Boolean('Create PDF Form', default=True)
# FormsType (int)
pdf_form_format = fields.Selection([
('0', 'FDF'),
('1', 'PDF'),
('2', 'HTML'),
('3', 'XML'),
], string='Submit Format', default='0')
# AllowDuplicateFieldNames (bool)
pdf_form_allow_duplicate = fields.Boolean('Allow Duplicate Field Names')
# ExportBookmarks (bool)
export_bookmarks = fields.Boolean('Export Bookmarks', default=True)
# ExportPlaceholders (bool)
export_placeholders = fields.Boolean('Export Placeholders', default=True)
# ExportNotes (bool)
export_comments = fields.Boolean('Export Comments')
# ExportHiddenSlides (bool) ??
export_hidden_slides = fields.Boolean(
'Export Automatically Insered Blank Pages')
# Doesn't make sense to have the option "View PDF after export" ! :)
# INITIAL VIEW TAB
# InitialView (int)
initial_view = fields.Selection([
('0', 'Page Only'),
('1', 'Bookmarks and Page'),
('2', 'Thumnails and Page'),
], string='Panes', default='0')
# InitialPage (int)
initial_page = fields.Integer(string='Initial Page', default=1)
# Magnification (int)
magnification = fields.Selection([
('0', 'Default'),
('1', 'Fit in Window'),
('2', 'Fit Width'),
('3', 'Fit Visible'),
('4', 'Zoom'),
], string='Magnification', default='0')
# Zoom (int)
zoom = fields.Integer(
string='Zoom Factor', default=100,
help='Possible values: from 50 to 1600')
# PageLayout (int)
page_layout = fields.Selection([
('0', 'Default'),
('1', 'Single Page'),
('2', 'Continuous'),
('3', 'Continuous Facing'),
], string='Page Layout', default='0')
# USER INTERFACE TAB
# ResizeWindowToInitialPage (bool)
resize_windows_initial_page = fields.Boolean(
string='Resize Windows to Initial Page')
# CenterWindow (bool)
center_window = fields.Boolean(string='Center Window on Screen')
# OpenInFullScreenMode (bool)
open_fullscreen = fields.Boolean(string='Open in Full Screen Mode')
# DisplayPDFDocumentTitle (bool)
display_document_title = fields.Boolean(string='Display Document Title')
# HideViewerMenubar (bool)
hide_menubar = fields.Boolean(string='Hide Menubar')
# HideViewerToolbar (bool)
hide_toolbar = fields.Boolean(string='Hide Toolbar')
# HideViewerWindowControls (bool)
hide_window_controls = fields.Boolean(string='Hide Windows Controls')
# OpenBookmarkLevels (int) -1 = all (default) from 1 to 10
open_bookmark_levels = fields.Selection([
('-1', 'All Levels'),
('1', '1'),
('2', '2'),
('3', '3'),
('4', '4'),
('5', '5'),
('6', '6'),
('7', '7'),
('8', '8'),
('9', '9'),
('10', '10'),
], default='-1', string='Visible Bookmark Levels')
# LINKS TAB
# ExportBookmarksToPDFDestination (bool)
export_bookmarks_named_dest = fields.Boolean(
string='Export Bookmarks as Named Destinations')
# ConvertOOoTargetToPDFTarget (bool)
convert_doc_ref_to_pdf_target = fields.Boolean(
string='Convert Document References to PDF Targets')
# ExportLinksRelativeFsys (bool)
export_filesystem_urls = fields.Boolean(
string='Export URLs Relative to Filesystem')
# PDFViewSelection -> mnDefaultLinkAction (int)
cross_doc_link_action = fields.Selection([
('0', 'Default'),
('1', 'Open with PDF Reader Application'),
('2', 'Open with Internet Browser'),
], string='Cross-document Links', default='default')
# SECURITY TAB
# EncryptFile (bool)
encrypt = fields.Boolean('Encrypt')
# DocumentOpenPassword (char)
document_password = fields.Char(string='Document Password')
# RestrictPermissions (bool)
restrict_permissions = fields.Boolean('Restrict Permissions')
# PermissionPassword (char)
permission_password = fields.Char(string='Permission Password')
# TODO PreparedPasswords + PreparedPermissionPassword
# I don't see those fields in the LO interface !
# But they are used in the LO code...
# Printing (int)
printing = fields.Selection([
('0', 'Not Permitted'),
('1', 'Low Resolution (150 dpi)'),
('2', 'High Resolution'),
], string='Printing', default='2')
# Changes (int)
changes = fields.Selection([
('0', 'Not Permitted'),
('1', 'Inserting, Deleting and Rotating Pages'),
('2', 'Filling in Form Fields'),
('3', 'Commenting, Filling in Form Fields'),
('4', 'Any Except Extracting Pages'),
], string='Changes', default='4')
# EnableCopyingOfContent (bool)
content_copying_allowed = fields.Boolean(
string='Enable Copying of Content', default=True)
# EnableTextAccessForAccessibilityTools (bool)
text_access_accessibility_tools_allowed = fields.Boolean(
string='Enable Text Access for Accessibility Tools', default=True)
# DIGITAL SIGNATURE TAB
# This will be possible but not easy
# Because the certificate parameter is a pointer to a certificate
# already registered in LO
# On Linux LO reuses the Mozilla certificate store (on Windows the
# one from Windows)
# But there seems to be some possibilities to send this certificate via API
# It seems you can add temporary certificates during runtime:
# https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1security_1_1XCertificateContainer.html
# Here is an API to retrieve the known certificates:
# https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1xml_1_1crypto_1_1XSecurityEnvironment.html
# Thanks to 'samuel_m' on libreoffice-dev IRC chan for pointing me to this
@api.constrains(
'image_jpeg_quality', 'initial_page', 'pdfa',
'cross_doc_link_action', 'magnification', 'zoom')
def check_pdf_options(self):
for opt in self:
if opt.image_jpeg_quality > 100 or opt.image_jpeg_quality < 1:
raise ValidationError(_(
"The parameter Image JPEG Quality must be between 1 %%"
" and 100 %% (current value: %s %%)")
% opt.image_jpeg_quality)
if opt.initial_page < 1:
raise ValidationError(_(
"The initial page parameter must be strictly positive "
"(current value: %d)") % opt.initial_page)
if opt.pdfa and opt.cross_doc_link_action == '1':
raise ValidationError(_(
"The PDF/A option is not compatible with "
"'Cross-document Links' = "
"'Open with PDF Reader Application'."))
if opt.magnification == '4' and (opt.zoom < 50 or opt.zoom > 1600):
raise ValidationError(_(
"The value of the zoom factor must be between 50 and 1600 "
"(current value: %d)") % opt.zoom)
@api.onchange('encrypt')
def encrypt_change(self):
if not self.encrypt:
self.document_password = False
@api.onchange('restrict_permissions')
def restrict_permissions_change(self):
if not self.restrict_permissions:
self.permission_password = False
@api.onchange('pdfa')
def pdfa_change(self):
if self.pdfa:
self.pdf_form = False
self.encrypt = False
self.restrict_permissions = False
def odoo2libreoffice_options(self):
self.ensure_one()
options = {}
# GENERAL TAB
if self.image_compression == 'lossless':
options['UseLosslessCompression'] = True
else:
options['UseLosslessCompression'] = False
options['Quality'] = self.image_jpeg_quality
if self.image_reduce_resolution != 'none':
options['ReduceImageResolution'] = True
options['MaxImageResolution'] = int(self.image_reduce_resolution)
else:
options['ReduceImageResolution'] = False
if self.watermark and self.watermark_text:
options['Watermark'] = self.watermark_text
if self.pdfa:
options['SelectPdfVersion'] = 1
options['UseTaggedPDF'] = self.tagged_pdf
else:
options['SelectPdfVersion'] = 0
if self.pdf_form and self.pdf_form_format and not self.pdfa:
options['ExportFormFields'] = True
options['FormsType'] = int(self.pdf_form_format)
options['AllowDuplicateFieldNames'] = self.pdf_form_allow_duplicate
else:
options['ExportFormFields'] = False
options.update({
'ExportBookmarks': self.export_bookmarks,
'ExportPlaceholders': self.export_placeholders,
'ExportNotes': self.export_comments,
'ExportHiddenSlides': self.export_hidden_slides,
})
# INITIAL VIEW TAB
options.update({
'InitialView': int(self.initial_view),
'InitialPage': self.initial_page,
'Magnification': int(self.magnification),
'PageLayout': int(self.page_layout),
})
if self.magnification == '4':
options['Zoom'] = self.zoom
# USER INTERFACE TAB
options.update({
'ResizeWindowToInitialPage': self.resize_windows_initial_page,
'CenterWindow': self.center_window,
'OpenInFullScreenMode': self.open_fullscreen,
'DisplayPDFDocumentTitle': self.display_document_title,
'HideViewerMenubar': self.hide_menubar,
'HideViewerToolbar': self.hide_toolbar,
'HideViewerWindowControls': self.hide_window_controls,
})
if self.open_bookmark_levels:
options['OpenBookmarkLevels'] = int(self.open_bookmark_levels)
# LINKS TAB
options.update({
'ExportBookmarksToPDFDestination':
self.export_bookmarks_named_dest,
'ConvertOOoTargetToPDFTarget': self.convert_doc_ref_to_pdf_target,
'ExportLinksRelativeFsys': self.export_filesystem_urls,
'PDFViewSelection': int(self.cross_doc_link_action),
})
# SECURITY TAB
if not self.pdfa:
if self.encrypt and self.document_password:
options['EncryptFile'] = True
options['DocumentOpenPassword'] = self.document_password
if self.restrict_permissions and self.permission_password:
options.update({
'RestrictPermissions': True,
'PermissionPassword': self.permission_password,
'Printing': int(self.printing),
'Changes': int(self.changes),
'EnableCopyingOfContent': self.content_copying_allowed,
'EnableTextAccessForAccessibilityTools':
self.text_access_accessibility_tools_allowed,
})
logger.debug(
'Py3o PDF options ID %s converted to %s', self.id, options)
return options

6
report_py3o_fusion_server/models/py3o_report.py

@ -75,6 +75,12 @@ class Py3oReport(models.TransientModel):
}
if report_xml.py3o_is_local_fusion:
fields['skipfusion'] = '1'
if filetype == 'pdf':
options = report_xml.pdf_options_id or\
report_xml.py3o_server_id.pdf_options_id
if options:
pdf_options_dict = options.odoo2libreoffice_options()
fields['pdf_options'] = json.dumps(pdf_options_dict)
r = requests.post(
report_xml.py3o_server_id.url, data=fields, files=files)
if r.status_code != 200:

4
report_py3o_fusion_server/models/py3o_server.py

@ -13,3 +13,7 @@ class Py3oServer(models.Model):
help="If your Py3o Fusion server is on the same machine and runs "
"on the default port, the URL is http://localhost:8765/form")
is_active = fields.Boolean("Active", default=True)
pdf_options_id = fields.Many2one(
'py3o.pdf.options', string='PDF Options', ondelete='restrict',
help="PDF options can be set per Py3o Server but also per report. "
"If both are defined, the options on the report are used.")

6
report_py3o_fusion_server/security/ir.model.access.csv

@ -1,3 +1,5 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
access_py3o_server_admin,access_py3o_server_admin,model_py3o_server,base.group_no_one,1,1,1,1
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_py3o_server_admin,access_py3o_server_admin,model_py3o_server,base.group_system,1,1,1,1
access_py3o_server_user,access_py3o_server_user,model_py3o_server,base.group_user,1,0,0,0
access_py3o_pdf_options_admin,Full access to PDF options to Settings grp,model_py3o_pdf_options,base.group_system,1,1,1,1
access_py3o_pdf_options_user,Read-only access to PDF options to employees,model_py3o_pdf_options,base.group_user,1,0,0,0

5
report_py3o_fusion_server/tests/test_report_py3o_fusion_server.py

@ -36,3 +36,8 @@ class TestReportPy3oFusionServer(test_report_py3o.TestReportPy3o):
def test_reports_no_local_fusion(self):
self.report.py3o_is_local_fusion = False
self.test_reports()
def test_odoo2libreoffice_options(self):
for options in self.env['py3o.pdf.options'].search([]):
options_dict = options.odoo2libreoffice_options()
self.assertIsInstance(options_dict, dict)

1
report_py3o_fusion_server/views/ir_report.xml

@ -7,6 +7,7 @@
<field name="py3o_multi_in_one" position="after">
<field name="py3o_is_local_fusion"/>
<field name="py3o_server_id" />
<field name="pdf_options_id" attrs="{'invisible': [('py3o_filetype', '!=', 'pdf')]}"/>
</field>
</field>
</record>

149
report_py3o_fusion_server/views/py3o_pdf_options.xml

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="py3o_pdf_options_form" model="ir.ui.view">
<field name="name">py3o.pdf.options.form</field>
<field name="model">py3o.pdf.options</field>
<field name="arch" type="xml">
<form string="Py3o PDF Export Options">
<div class="oe_title">
<label for="name"/>
<h1>
<field name="name" placeholder="Give a name to the set of PDF export options"/>
</h1>
</div>
<notebook>
<page name="general" string="General">
<group name="general">
<group name="general-left" col="1">
<group name="general-image" string="Image">
<field name="image_compression" widget="radio"/>
<label for="image_jpeg_quality" attrs="{'invisible': [('image_compression', '!=', 'jpeg')]}"/>
<div name="image_jpeg_quality" attrs="{'invisible': [('image_compression', '!=', 'jpeg')]}">
<field name="image_jpeg_quality" class="oe_inline"/>
<label string=" %"/>
</div>
<field name="image_reduce_resolution"/>
</group>
<group name="general-watermark" string="Watermark">
<field name="watermark"/>
<field name="watermark_text" attrs="{'invisible': [('watermark', '!=', True)], 'required': [('watermark', '=', True)]}"/>
</group>
</group>
<group name="general-right" string="General">
<field name="pdfa"/>
<field name="tagged_pdf" attrs="{'invisible': [('pdfa', '=', True)]}"/>
<field name="pdf_form" attrs="{'invisible': [('pdfa', '=', True)]}"/>
<field name="pdf_form_format" attrs="{'invisible': [('pdf_form', '!=', True)], 'required': [('pdf_form', '=', True)]}"/>
<field name="pdf_form_allow_duplicate" attrs="{'invisible': [('pdf_form', '!=', True)]}"/>
<field name="export_bookmarks"/>
<field name="export_placeholders"/>
<field name="export_comments"/>
<field name="export_hidden_slides"/>
</group>
</group>
</page>
<page name="initial_view" string="Initial View">
<group name="initial_view">
<group name="initial_view-left" col="1">
<group name="panes" string="Panes">
<field name="initial_view" widget="radio"/>
<field name="initial_page"/>
</group>
<group name="magnification" string="Magnification">
<field name="magnification" widget="radio"/>
<field name="zoom" attrs="{'invisible': [('magnification', '!=', '4')]}"/>
</group>
</group>
<group name="initial_view-right">
<field name="page_layout" widget="radio"/>
</group>
</group>
</page>
<page name="user_intf" string="User Interface">
<group name="user_intf">
<group name="user_intf-left" col="1">
<group name="user_intf-window-options" string="Window Options">
<field name="resize_windows_initial_page"/>
<field name="center_window"/>
<field name="open_fullscreen"/>
<field name="display_document_title"/>
</group>
</group>
<group name="user_intf-right" col="1">
<group name="user_intf-options" string="User Interface Options">
<field name="hide_menubar"/>
<field name="hide_toolbar"/>
<field name="hide_window_controls"/>
</group>
<group string="Bookmarks" name="bookmarks">
<field name="open_bookmark_levels"/>
</group>
</group>
</group>
</page>
<page string="Links" name="links">
<group name="links" col="1">
<group name="links-general" string="General">
<field name="export_bookmarks_named_dest"/>
<field name="convert_doc_ref_to_pdf_target"/>
<field name="export_filesystem_urls"/>
</group>
<group name="links-cross-doc" string="Cross-document Links">
<field name="cross_doc_link_action" widget="radio"/>
</group>
</group>
</page>
<page string="Security" name="security">
<group name="security">
<group name="security-left" attrs="{'invisible': [('pdfa', '=', True)]}">
<field name="encrypt"/>
<field name="document_password" password="True" attrs="{'invisible': [('encrypt', '!=', True)], 'required': [('encrypt', '=', True)]}"/>
<field name="restrict_permissions"/>
<field name="permission_password" password="True" attrs="{'invisible': [('restrict_permissions', '!=', True)], 'required': [('restrict_permissions', '=', True)]}"/>
</group>
<group name="security-right" attrs="{'invisible': ['|', ('pdfa', '=', True), ('restrict_permissions', '=', False)]}">
<field name="printing" widget="radio"/>
<field name="changes" widget="radio"/>
<field name="content_copying_allowed"/>
<field name="text_access_accessibility_tools_allowed"/>
</group>
<group name="security-pdfa" attrs="{'invisible': [('pdfa', '=', False)]}" colspan="2">
<div><p>The security settings are incompatible with the <b>PDF/A-1a</b> option in the <em>General</em> tab.</p></div>
</group>
</group>
</page>
</notebook>
</form>
</field>
</record>
<record id="py3o_pdf_options_tree" model="ir.ui.view">
<field name="name">py3o.pdf.options.tree</field>
<field name="model">py3o.pdf.options</field>
<field name="arch" type="xml">
<tree string="Py3o PDF Export Options">
<field name="name"/>
</tree>
</field>
</record>
<record id="py3o_pdf_options_action" model="ir.actions.act_window">
<field name="name">Py3o PDF Export Options</field>
<field name="res_model">py3o.pdf.options</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="py3o_pdf_options_menu"
parent="report_py3o.py3o_config_menu"
action="py3o_pdf_options_action"
sequence="40" />
</odoo>

2
report_py3o_fusion_server/views/py3o_server.xml

@ -8,6 +8,7 @@
<form string="Py3o Server Configuration">
<group name="main">
<field name="url" widget="url"/>
<field name="pdf_options_id"/>
<field name="is_active" />
</group>
</form>
@ -20,6 +21,7 @@
<field name="arch" type="xml">
<tree string="Py3o Servers Configuration">
<field name="url" />
<field name="pdf_options_id"/>
<field name="is_active" />
</tree>
</field>

Loading…
Cancel
Save