add 2new models
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
Most of the files are
|
||||
|
||||
Copyright (c) 2018 Bizzappdev India
|
||||
|
||||
and published under the Bizzappdev License,
|
||||
as described in the LICENSE file.
|
||||
|
||||
Some files may also contain contributions from third
|
||||
parties. In this case the original copyright of
|
||||
the contributions can be traced through the
|
||||
history of the source version control system.
|
||||
|
||||
When that is not the case, the files contain a prominent
|
||||
notice stating the original copyright and applicable
|
||||
license, or come with their own dedicated COPYRIGHT
|
||||
and/or LICENSE file.
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
For copyright information, please see the COPYRIGHT file.
|
||||
|
||||
This Odoo App is published under the Bizzappdev License v1.0, as included
|
||||
below.
|
||||
|
||||
********************************************************************************
|
||||
|
||||
Bizzappdev License v1.0 Copyright (C) 2018 Bizzappdev India
|
||||
|
||||
By purchasing this Odoo App from Bizzappdev India, you agree to the
|
||||
following:
|
||||
|
||||
This license agreement (hereinafter Agreement) is an agreement
|
||||
between you (the person or company who is being licensed to use the Software or
|
||||
Documentation) and Bizzappdev India (hereinafter We/us/our). The
|
||||
Agreement applies to the Odoo App where this license is added.
|
||||
|
||||
1. By purchasing the Software you acknowledge that you have read this Agreement,
|
||||
and that you agree to the content of the Agreement and its terms, and agree to
|
||||
use the Software in compliance with this Agreement.
|
||||
|
||||
2. The Agreement comes into legal force at the moment when you order our
|
||||
Software from our site, download it from the Odoo App Store, or receive it
|
||||
through email or on data medium at the our discretion.
|
||||
|
||||
3. We are the copyright holder of the Software. The Software or a portion of it
|
||||
is a copyrightable matter and is liable to protection by the law. Any activity
|
||||
that infringes terms of this Agreement violates copyright law and will be
|
||||
prosecuted according to the current law. We reserve the right to revoke the
|
||||
license of any user who is holding an invalid license.
|
||||
|
||||
4. This Agreement gives you the right to use only one copy of the Software on
|
||||
one Odoo installation solely for your own personal or business use, subject to
|
||||
all other terms of this Agreement. A separate License should be purchased for
|
||||
each Odoo installation. Any distribution of the Software without our consent,
|
||||
including noncommercial distribution is regarded as violation of this Agreement
|
||||
and entails liability, according to the current law.
|
||||
|
||||
5. You may not use any part of the code in whole or part in any other software
|
||||
or product or website.
|
||||
|
||||
6. You may not give, sell, distribute, sub-license, rent, lease or lend any
|
||||
portion of the Software or Documentation to anyone. You may not place the
|
||||
Software on a server so that it is accessible via a public network such as the
|
||||
Internet for distribution purposes.
|
||||
|
||||
7. You are bound to preserve the copyright information intact.
|
||||
|
||||
8. We reserve the right to publish a selected list of users of our Software.
|
||||
|
||||
9. We are not liable for prosecution arising from use of the Software against
|
||||
law or for any illegal use.
|
||||
|
||||
10. If you fail to use the Software in accordance with the terms and conditions
|
||||
of this License Agreement, it constitutes a breach of the agreement, and your
|
||||
license to use the Software is revoked.
|
||||
|
||||
11. Bizzappdev reserves the right to change this license agreement at any time
|
||||
and impose its clauses at any given time.
|
||||
|
||||
12. It is allowed to change the
|
||||
software according to your needs. But only in respect of the terms of this
|
||||
license.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# See LICENSE file for full copyright and licensing details.
|
||||
from . import models
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "Custom Background",
|
||||
"version": "14.0.0.0.11",
|
||||
"author": "BizzAppDev",
|
||||
"website": "http://www.bizzappdev.com",
|
||||
"category": "GenericModules",
|
||||
"depends": ["base", "web"],
|
||||
"summary": "Custom Background",
|
||||
"images": ['images/image.png'],
|
||||
"init_xml": [],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"views/ir_actions.xml",
|
||||
"views/res_company_view.xml",
|
||||
"views/report_template.xml",
|
||||
],
|
||||
"demo_xml": [],
|
||||
"test": [],
|
||||
"installable": True,
|
||||
"application": False,
|
||||
"license": "Other proprietary",
|
||||
}
|
||||
|
|
@ -0,0 +1,511 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * custom_background
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 14.0+e\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-07-16 12:50+0000\n"
|
||||
"PO-Revision-Date: 2021-07-18 11:44+0200\n"
|
||||
"Last-Translator: Erwin van der Ploeg <erwin@odooexperts.nl>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: \n"
|
||||
"Language: nl\n"
|
||||
"X-Generator: Poedit 2.4.2\n"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid ""
|
||||
"<b>Expression:</b>\n"
|
||||
" If the option \"Expression\" is selected you "
|
||||
"can\n"
|
||||
" set custom background PDF's based on\n"
|
||||
" expressions.\n"
|
||||
" <br/>\n"
|
||||
" For example if you want only a background on\n"
|
||||
" every second page you could do:\n"
|
||||
" <br/>\n"
|
||||
" result=page%2==0\n"
|
||||
" <br/>\n"
|
||||
" If you want only a background on every uneven\n"
|
||||
" page (for example page 1,3 ,5) you could do:\n"
|
||||
" <br/>\n"
|
||||
" result=page%2!=0\n"
|
||||
" <br/>\n"
|
||||
" Or if you only want a background starting from\n"
|
||||
" page 2 on for example you could do:\n"
|
||||
" <br/>\n"
|
||||
" result=page>2"
|
||||
msgstr ""
|
||||
"<b>Uitdrukking:</b>\n"
|
||||
" Als de optie “Expressie” is geselecteerd, kunt "
|
||||
"u\n"
|
||||
" Een aangepaste achtergrond-PDF’s instellen op "
|
||||
"basis van\n"
|
||||
" expressies.\n"
|
||||
" <br/>\n"
|
||||
" Als u bijvoorbeeld alleen een achtergrond op\n"
|
||||
" elke tweede pagina wil, dan kunt u bijvoorbeeld "
|
||||
"dit doen:\n"
|
||||
" <br/>\n"
|
||||
" result=page%2==0\n"
|
||||
" <br/>\n"
|
||||
" Als u alleen een achtergrond wilt op elke "
|
||||
"oneven\n"
|
||||
" pagina (bijvoorbeeld pagina 1,3 ,5) kunt u "
|
||||
"doen:\n"
|
||||
" <br/>\n"
|
||||
" result=page%2!=0\n"
|
||||
" <br/>\n"
|
||||
" Of als u alleen een achtergrond wilt vanaf\n"
|
||||
" pagina 2, dan kunt u bijvoorbeeld dit doen:\n"
|
||||
" <br/>\n"
|
||||
" result=page>2"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid ""
|
||||
"<b>First Page:</b>\n"
|
||||
" If the option \"First Page\" is chosen it will "
|
||||
"not\n"
|
||||
" look at any other configurations for the first\n"
|
||||
" page of the PDF.\n"
|
||||
" <br/>\n"
|
||||
" If no \"Background PDF\" is set on the \"First\n"
|
||||
" Page\" rule then it will set the background "
|
||||
"PDF\n"
|
||||
" from the \"Fixed Page\", \"Expression Page\" "
|
||||
"or\n"
|
||||
" \"Remaining Pages\"."
|
||||
msgstr ""
|
||||
"<b>Eerste pagina:</b>\n"
|
||||
" Als de optie “Eerste pagina” is gekozen, dan "
|
||||
"wortd\n"
|
||||
" Niet naar andere instellingen gekeken dan "
|
||||
"deze\n"
|
||||
" Voor de eerste pagina van de pdf.\n"
|
||||
" <br/>\n"
|
||||
" Als er geen “Achtergrond PDF” is ingesteld op "
|
||||
"de “Eerste\n"
|
||||
" Pagina” regel dan zal het de achtergrond PDF "
|
||||
"instellen\n"
|
||||
" van de “Vaste pagina”, “Expressie pagina” of\n"
|
||||
" “Overige pagina’s”."
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid ""
|
||||
"<b>Fixed Pages:</b>\n"
|
||||
" If the option \"Fixed Pages\" is selected you "
|
||||
"can\n"
|
||||
" enter for which page you'd like to use the\n"
|
||||
" \"Background PDF\".\n"
|
||||
" For example: setting a custom background PDF "
|
||||
"for\n"
|
||||
" the third page of a report by entering \"3\" "
|
||||
"in\n"
|
||||
" the field \"Page Number\".\n"
|
||||
" <br/>\n"
|
||||
" If the entered page number is first and last\n"
|
||||
" then it will set the background from \"First\n"
|
||||
" Page\" and \"Last Page\" if we configured the "
|
||||
"first\n"
|
||||
" page and the last page otherwise it will use "
|
||||
"the\n"
|
||||
" \"Fixed Page\" configuration."
|
||||
msgstr ""
|
||||
"<b>Vaste pagina’s:</b>\n"
|
||||
" Als de optie “Vaste pagina’s” is geselecteerd, "
|
||||
"kunt u\n"
|
||||
" Instellen voor welke pagina u de\n"
|
||||
" “Achtergrond-PDF” wilt gebruiken.\n"
|
||||
" Bijvoorbeeld: een aangepaste achtergrond-PDF "
|
||||
"instellen voor\n"
|
||||
" de derde pagina van een rapport door “3” in te "
|
||||
"voeren in\n"
|
||||
" het veld “Paginanummer”.\n"
|
||||
" <br/>\n"
|
||||
" Als het ingevoerde paginanummer de eerste en "
|
||||
"de laatste is\n"
|
||||
" dan zal het de achtergrond instellen van "
|
||||
"“Eerste\n"
|
||||
" Pagina” en “Laatste pagina” als we de eerste "
|
||||
"hebben geconfigureerd,\n"
|
||||
" anders gebruikt het de\n"
|
||||
" Configuratie “Vaste pagina”."
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid ""
|
||||
"<b>Last Page:</b>\n"
|
||||
" If the option \"Last Page\" is selected, it "
|
||||
"will\n"
|
||||
" not check for any other configurations for the\n"
|
||||
" last page.\n"
|
||||
" It will only set the last page background PDF\n"
|
||||
" that you've applied.\n"
|
||||
" <br/>\n"
|
||||
" If there was no \"Background PDF\" set on the "
|
||||
"last\n"
|
||||
" page then it will set the background PDF from\n"
|
||||
" the \"Fixed Page\", \"Expression Page\" or\n"
|
||||
" \"Remaining Pages\".\n"
|
||||
" <br/>"
|
||||
msgstr ""
|
||||
"<b>Laatste pagina:</b>\n"
|
||||
" Als de optie “Laatste pagina” is geselecteerd, "
|
||||
"wordt\n"
|
||||
" niet gecontroleerd op andere instellingen voor "
|
||||
"de\n"
|
||||
" laatste pagina.\n"
|
||||
" Het zal alleen de laatste pagina-achtergrond "
|
||||
"PDF instellen\n"
|
||||
" die u heeft ingesteld.\n"
|
||||
" <br/>\n"
|
||||
" Als er geen “Achtergrond PDF” was ingesteld op "
|
||||
"de laatste\n"
|
||||
" pagina, dan zal het de achtergrond-PDF "
|
||||
"instellen van\n"
|
||||
" de “Vaste pagina”, “Expressie pagina” of\n"
|
||||
" “Overige pagina’s”.\n"
|
||||
" <br/>"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid ""
|
||||
"<b>Remaining Pages:</b>\n"
|
||||
" If the option \"Remaining Pages\" is selected "
|
||||
"the\n"
|
||||
" module will apply the \"Background PDF\" set "
|
||||
"on\n"
|
||||
" this rule for any pages that have no rule.\n"
|
||||
" You could use this for example if you have a\n"
|
||||
" rule \"First Page\" set and then have a second\n"
|
||||
" rule \"Remaining Pages\" it would apply this\n"
|
||||
" background on page 2, 3, .. and any other page\n"
|
||||
" you have not configured."
|
||||
msgstr ""
|
||||
"<b>Overige pagina’s:</b>\n"
|
||||
" Als de optie “Overige pagina’s” is "
|
||||
"geselecteerd, wordt de\n"
|
||||
" “Achtergrond PDF” toegepast op\n"
|
||||
" deze regel voor alle pagina’s die geen regel "
|
||||
"hebben.\n"
|
||||
" U kunt dit bijvoorbeeld gebruiken als u een\n"
|
||||
" regel “Eerste pagina” instelt en dan een "
|
||||
"tweede\n"
|
||||
" regel “Overige pagina’s”. Het zou dan de\n"
|
||||
" achtergrond op instellen op pagina 2, 3, .. en "
|
||||
"elke andere pagina\n"
|
||||
" Die u niet heeft geconfigureerd."
|
||||
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__background_ids
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid "Background Configuration"
|
||||
msgstr "Achtergrondconfiguratie"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid "Background Configuration Per Language"
|
||||
msgstr "Achtergrondconfiguratie per taal"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__custom_report_background_image
|
||||
msgid "Background Image"
|
||||
msgstr "Achtergrondafbeelding"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__background_pdf
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__background_pdf
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.res_company_view_inherit
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid "Background PDF"
|
||||
msgstr "Achtergrond PDF"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__bg_per_lang_ids
|
||||
#: model:ir.model.fields,field_description:custom_background.field_res_company__bg_per_lang_ids
|
||||
msgid "Background Per Language"
|
||||
msgstr "Achtergrond per taal"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model,name:custom_background.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Bedrijven"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__company_id
|
||||
msgid "Company"
|
||||
msgstr "Bedrijf"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.res_company_view_inherit
|
||||
msgid "Configuration"
|
||||
msgstr "Instellingen"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__create_uid
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Aangemaakt door"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__create_date
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Aangemaakt op"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.res_company_view_inherit
|
||||
msgid "Custom Background Image"
|
||||
msgstr "Achtergrondafbeelding"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.res_company_view_inherit
|
||||
msgid "Custom Background Image Per Language"
|
||||
msgstr "Aangepaste achtergrondafbeelding per taal"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__custom_report_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_res_company__custom_report_background_image
|
||||
msgid "Custom Report Background"
|
||||
msgstr "Achtergrondafbeelding layouts"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__custom_report_type
|
||||
msgid "Custom Report Type"
|
||||
msgstr "Afwijkende achtergrond"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid "Detailed algorithm (with their priorities):"
|
||||
msgstr "Gedetailleerd algoritme (met hun prioriteiten):"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__display_name
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__display_name
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__display_name
|
||||
#: model:ir.model.fields,field_description:custom_background.field_res_company__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Schermnaam"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__expression
|
||||
msgid "Expression"
|
||||
msgstr "Uitdrukking"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid "Extra Note"
|
||||
msgstr "Extra opmerking"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__fall_back_to_company
|
||||
msgid "Fall Back To Company"
|
||||
msgstr "Terugvallen op het bedrijf"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__file_name
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__file_name
|
||||
msgid "File Name"
|
||||
msgstr "Bestandsnaam"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__first_page
|
||||
msgid "First Page"
|
||||
msgstr "Eerste pagina"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__fixed
|
||||
msgid "Fixed Page"
|
||||
msgstr "Vaste pagina"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields.selection,name:custom_background.selection__ir_actions_report__custom_report_type__company
|
||||
msgid "From Company"
|
||||
msgstr "Van bedrijf"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields.selection,name:custom_background.selection__ir_actions_report__custom_report_type__dynamic
|
||||
msgid "From Report Dynamic"
|
||||
msgstr "Van Rapport Dynamisch"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields.selection,name:custom_background.selection__ir_actions_report__custom_report_type__report
|
||||
msgid "From Report Fixed"
|
||||
msgstr "Van rapport opgelost"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__id
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__id
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__id
|
||||
#: model:ir.model.fields,field_description:custom_background.field_res_company__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid ""
|
||||
"If there are no \"Background Configuration\" rules\n"
|
||||
" added for any line it will work as a normal Odoo\n"
|
||||
" report and will not apply any background to the\n"
|
||||
" report."
|
||||
msgstr ""
|
||||
"Als er geen regels voor de “Achtergrondconfiguratie” zijn\n"
|
||||
" toegevoegd voor een regel, dan zal deze werken als "
|
||||
"een normaal Odoo\n"
|
||||
" rapport en wordt er geen achtergrond toegevoegd op "
|
||||
"de\n"
|
||||
" Rappoortage."
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid "Interaction with the background configuration"
|
||||
msgstr "Interactie met de achtergrondconfiguratie"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__is_bg_per_lang
|
||||
#: model:ir.model.fields,field_description:custom_background.field_res_company__is_bg_per_lang
|
||||
msgid "Is Background Per Language"
|
||||
msgstr "Instellen achtergrond per taal"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__lang_id
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__lang_id
|
||||
msgid "Language"
|
||||
msgstr "Taal"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report____last_update
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang____last_update
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line____last_update
|
||||
#: model:ir.model.fields,field_description:custom_background.field_res_company____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr "Laatst gewijzigd op"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__last_page
|
||||
msgid "Last Page"
|
||||
msgstr "Laatste pagina"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__write_uid
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Laatst bijgewerkt door"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__write_date
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Laatst bijgewerkt op"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__page_expression
|
||||
msgid "Page Expression"
|
||||
msgstr "Pagina expressie"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__page_number
|
||||
msgid "Page Number"
|
||||
msgstr "Paginanummer"
|
||||
|
||||
#. module: custom_background
|
||||
#: code:addons/custom_background/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Please configure Custom Background Per Language beacuse 'Fall Back To Company' is set "
|
||||
"in the dynamic type report level!"
|
||||
msgstr ""
|
||||
"Configureer a.u.b. aangepaste achtergrond per taal omdat 'Fall Back To Bedrijf' is "
|
||||
"ingesteld op het rapportniveau dynamisch type!"
|
||||
|
||||
#. module: custom_background
|
||||
#: code:addons/custom_background/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Please configure Custom Background Per Language beacuse 'From Company' type is set at "
|
||||
"the Report level!"
|
||||
msgstr ""
|
||||
"Configureer aangepaste achtergrond per taal vanwege het type 'Van bedrijf' is ingesteld "
|
||||
"op rapportniveau!"
|
||||
|
||||
#. module: custom_background
|
||||
#: code:addons/custom_background/models/report.py:0
|
||||
#, python-format
|
||||
msgid "Please configure Custom Background Per Language for Dynamic type!"
|
||||
msgstr "Configureer aangepaste achtergrond per taal voor dynamisch type!"
|
||||
|
||||
#. module: custom_background
|
||||
#: code:addons/custom_background/models/report.py:0
|
||||
#, python-format
|
||||
msgid "Please configure Custom Background Per Language for Report type!"
|
||||
msgstr "Configureer aangepaste achtergrond per taal voor rapporttype!"
|
||||
|
||||
#. module: custom_background
|
||||
#: code:addons/custom_background/models/report.py:0
|
||||
#, python-format
|
||||
msgid "Please configure Custom Background Per Language in every company!"
|
||||
msgstr "Configureer aangepaste achtergrond per taal in elk bedrijf!"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__remaining
|
||||
msgid "Remaining pages"
|
||||
msgstr "Overige pagina's"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__report_id
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__report_id
|
||||
msgid "Report"
|
||||
msgstr "Rapport"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model,name:custom_background.model_ir_actions_report
|
||||
msgid "Report Action"
|
||||
msgstr "Rapport actie"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model,name:custom_background.model_report_background_line
|
||||
msgid "Report Background Line"
|
||||
msgstr "Achtergrondregel rapporteren"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model,name:custom_background.model_report_background_lang
|
||||
msgid "Report Background Line Per Language"
|
||||
msgstr "Achtergrondregel per taal rapporteren"
|
||||
|
||||
#. module: custom_background
|
||||
#: model:ir.model.fields,field_description:custom_background.field_report_background_line__type
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
|
||||
#. module: custom_background
|
||||
#: code:addons/custom_background/models/report.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Wkhtmltopdf failed (error code: %s). Memory limit too low or maximum file number of "
|
||||
"subprocess reached. Message : %s"
|
||||
msgstr ""
|
||||
"Wkhtmltopdf fout (fout code: %s). Geheugenlimiet te laag of maximale bestandsnummer van "
|
||||
"subproces bereikt. Bericht:%s"
|
||||
|
||||
#. module: custom_background
|
||||
#: code:addons/custom_background/models/report.py:0
|
||||
#, python-format
|
||||
msgid "Wkhtmltopdf failed (error code: %s). Message: %s"
|
||||
msgstr "Wkhtmltopdf fout (error code: %s). Bericht: %s"
|
||||
|
||||
#. module: custom_background
|
||||
#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form
|
||||
msgid ""
|
||||
"You can set the backgroud watermark in a report based on\n"
|
||||
" the options set in the \"Background Configuration\" tab."
|
||||
msgstr ""
|
||||
"U kunt het achtergrondwatermerk in een rapport instellen op basis\n"
|
||||
"van de opties die zijn ingesteld op het tabblad “Achtergrondconfiguratie”."
|
||||
|
After Width: | Height: | Size: 194 KiB |
|
|
@ -0,0 +1,4 @@
|
|||
# See LICENSE file for full copyright and licensing details.
|
||||
from . import report
|
||||
from . import res_company
|
||||
from . import report_background_lang
|
||||
|
|
@ -0,0 +1,649 @@
|
|||
# See LICENSE file for full copyright and licensing details.
|
||||
import base64
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from contextlib import closing
|
||||
|
||||
from PyPDF2 import PdfFileReader, PdfFileWriter
|
||||
from reportlab.graphics.barcode import createBarcodeDrawing
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools.misc import find_in_path
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo.tools.translate import _
|
||||
|
||||
try:
|
||||
createBarcodeDrawing(
|
||||
"Code128",
|
||||
value="foo",
|
||||
format="png",
|
||||
width=100,
|
||||
height=100,
|
||||
humanReadable=1,
|
||||
).asString("png")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# --------------------------------------------------------------------------
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_wkhtmltopdf_bin():
|
||||
return find_in_path("wkhtmltopdf")
|
||||
|
||||
|
||||
class ReportBackgroundLine(models.Model):
|
||||
_name = "report.background.line"
|
||||
_description = "Report Background Line"
|
||||
|
||||
page_number = fields.Integer()
|
||||
type = fields.Selection(
|
||||
[
|
||||
("fixed", "Fixed Page"),
|
||||
("expression", "Expression"),
|
||||
("first_page", "First Page"),
|
||||
("last_page", "Last Page"),
|
||||
("remaining", "Remaining pages"),
|
||||
]
|
||||
)
|
||||
background_pdf = fields.Binary(string="Background PDF")
|
||||
# New field. #22260
|
||||
file_name = fields.Char(string="File Name")
|
||||
report_id = fields.Many2one("ir.actions.report", string="Report")
|
||||
page_expression = fields.Char()
|
||||
fall_back_to_company = fields.Boolean()
|
||||
# New fields. #22260
|
||||
lang_id = fields.Many2one(
|
||||
"res.lang",
|
||||
string="Language",
|
||||
)
|
||||
# Added new field #T5211
|
||||
company_id = fields.Many2one(comodel_name="res.company", string="Company")
|
||||
|
||||
|
||||
class IrActionsReport(models.Model):
|
||||
_inherit = "ir.actions.report"
|
||||
|
||||
custom_report_background = fields.Boolean(string="Custom Report Background")
|
||||
custom_report_background_image = fields.Binary(string="Background Image")
|
||||
custom_report_type = fields.Selection(
|
||||
[
|
||||
("company", "From Company"),
|
||||
("report", "From Report Fixed"),
|
||||
("dynamic", "From Report Dynamic"),
|
||||
]
|
||||
)
|
||||
|
||||
background_ids = fields.One2many(
|
||||
"report.background.line", "report_id", "Background Configuration"
|
||||
)
|
||||
# New fields. #22260
|
||||
bg_per_lang_ids = fields.One2many(
|
||||
"report.background.lang",
|
||||
"report_id",
|
||||
string="Background Per Language",
|
||||
)
|
||||
is_bg_per_lang = fields.Boolean(
|
||||
string="Is Background Per Language",
|
||||
)
|
||||
|
||||
def get_company_without_custom_bg(self):
|
||||
"""New method for search and get company in which custom bg per language is not
|
||||
set. #22260"""
|
||||
res_company_env = self.env["res.company"].search([])
|
||||
# Filtered company in which is_bg_per_lang is not set and
|
||||
# attachment is not set.
|
||||
company = res_company_env.filtered(
|
||||
lambda c: not c.is_bg_per_lang or not c.bg_per_lang_ids
|
||||
)
|
||||
return company
|
||||
|
||||
@api.constrains(
|
||||
"is_bg_per_lang", "bg_per_lang_ids", "custom_report_type", "background_ids"
|
||||
)
|
||||
def _check_report_custom_bg_config(self):
|
||||
"""New constrains method for check custom bg per company is set or not when for
|
||||
'report' & 'dynamic' type. #22260"""
|
||||
# If is_bg_per_lang is false then return.
|
||||
if not self.is_bg_per_lang:
|
||||
return
|
||||
# If type is 'report' and custom bg per lang is not set then raise warning.
|
||||
if self.custom_report_type == "report" and not self.bg_per_lang_ids:
|
||||
raise UserError(
|
||||
_("Please configure Custom Background Per Language for Report type!")
|
||||
)
|
||||
# If type is 'dynamic' and custom bg per lang is not set then raise warning.
|
||||
elif self.custom_report_type == "dynamic" and not self.background_ids:
|
||||
raise UserError(
|
||||
_("Please configure Custom Background Per Language for Dynamic type!")
|
||||
)
|
||||
# Check type is dynamic and background_ids is set or not.
|
||||
elif self.custom_report_type == "dynamic" and self.background_ids:
|
||||
# Filter fall_back_to_company true records.
|
||||
fbc = self.background_ids.filtered(lambda bg: bg.fall_back_to_company)
|
||||
# If fbc and custom bg not set at company level then raise warning.
|
||||
if fbc:
|
||||
company = self.get_company_without_custom_bg()
|
||||
# If any attachment not set in the any company then raise warning.
|
||||
if company:
|
||||
raise UserError(
|
||||
_(
|
||||
"Please configure Custom Background Per Language in every "
|
||||
"company!"
|
||||
)
|
||||
)
|
||||
# If type is 'company' or type is not set then search
|
||||
# configuration in all company.
|
||||
elif self.custom_report_type == "company" or not self.custom_report_type:
|
||||
company = self.get_company_without_custom_bg()
|
||||
# If any attachment not set in the any company then raise warning.
|
||||
if company:
|
||||
raise UserError(
|
||||
_(
|
||||
"Please configure Custom Background Per Language in every "
|
||||
"company!"
|
||||
)
|
||||
)
|
||||
|
||||
def _render_qweb_pdf(self, res_ids=None, data=None):
|
||||
Model = self.env[self.model]
|
||||
record_ids = Model.browse(res_ids)
|
||||
company_id = False
|
||||
if record_ids[:1]._name == "res.company":
|
||||
company_id = record_ids[:1]
|
||||
# Fix test cases error. #22107
|
||||
elif hasattr(record_ids[:1], "company_id"):
|
||||
# If in record company is not set then consider current log in
|
||||
# user's company. #22476
|
||||
company_id = record_ids[:1].company_id or self.env.user.company_id
|
||||
else:
|
||||
company_id = self.env.company
|
||||
|
||||
# Add custom_bg_res_ids in context. #22260
|
||||
return super(
|
||||
IrActionsReport,
|
||||
self.with_context(custom_bg_res_ids=res_ids, background_company=company_id),
|
||||
)._render_qweb_pdf(res_ids=res_ids, data=data)
|
||||
|
||||
def add_pdf_watermarks(self, custom_background_data, page):
|
||||
"""create a temp file and set datas and added in report page. #T4209"""
|
||||
temp_back_id, temp_back_path = tempfile.mkstemp(
|
||||
suffix=".pdf", prefix="back_report.tmp."
|
||||
)
|
||||
back_data = base64.b64decode(custom_background_data)
|
||||
with closing(os.fdopen(temp_back_id, "wb")) as back_file:
|
||||
back_file.write(back_data)
|
||||
pdf_reader_watermark = PdfFileReader(temp_back_path, "rb")
|
||||
watermark_page = pdf_reader_watermark.getPage(0)
|
||||
watermark_page.mergePage(page)
|
||||
return watermark_page
|
||||
|
||||
def get_lang(self):
|
||||
"""New method for return language, if partner_id is available in model and
|
||||
partner is set in that model, else set current logged in user's language.
|
||||
#22260"""
|
||||
res_record_ids = self._context.get("custom_bg_res_ids")
|
||||
model = self.env[self.model]
|
||||
record_ids = model.browse(res_record_ids)
|
||||
lang_code = False
|
||||
# If partner_id field in the model and partner is set in the model the consider
|
||||
# partner's language.
|
||||
# NOTE: Used "record_ids[-1]" to avoid loop, if use loop then always set last
|
||||
# record partner's language.
|
||||
if "partner_id" in model._fields and record_ids[-1].partner_id:
|
||||
partner_lang = record_ids[-1].partner_id.lang
|
||||
lang_code = partner_lang if partner_lang else "en_US"
|
||||
else:
|
||||
# If partner_id field is not in model or partner_id is not set then consider
|
||||
# current user's language.
|
||||
lang_code = self._context.get("lang")
|
||||
return lang_code
|
||||
|
||||
def get_bg_per_lang(self):
|
||||
"""New method for get custom background based on the partner languages for
|
||||
report type and company type. #22260"""
|
||||
company_background = self._context.get("background_company")
|
||||
lang_code = self.get_lang()
|
||||
# If custom_report_type is dynamic then set language related domains.
|
||||
if self.custom_report_type == "dynamic":
|
||||
# If is_bg_per_lang true then set lang_code related domain.
|
||||
if self.is_bg_per_lang:
|
||||
lang_domain = [
|
||||
("lang_id.code", "=", lang_code),
|
||||
]
|
||||
else:
|
||||
# If is_bg_per_lang false then set lang_id related domain.
|
||||
lang_domain = [
|
||||
("lang_id", "=", False),
|
||||
]
|
||||
return lang_domain
|
||||
|
||||
# If custom_report_type is report then set report(self) id.
|
||||
if self.custom_report_type == "report":
|
||||
custom_bg_from = self
|
||||
# If custom_report_type is company then set current company id from context.
|
||||
if self.custom_report_type == "company" or not self.custom_report_type:
|
||||
custom_bg_from = company_background
|
||||
|
||||
# Filter records from report_background_lang model based on the languages.
|
||||
# custom_bg_from: company_id or report_id(self).
|
||||
custom_bg_lang = custom_bg_from.bg_per_lang_ids.filtered(
|
||||
lambda l: l.lang_id.code == lang_code
|
||||
)
|
||||
|
||||
# Set 1st custom background.
|
||||
custom_background = custom_bg_lang[:1].background_pdf
|
||||
return custom_background
|
||||
|
||||
@api.model
|
||||
def _run_wkhtmltopdf(
|
||||
self,
|
||||
bodies,
|
||||
header=None,
|
||||
footer=None,
|
||||
landscape=False,
|
||||
specific_paperformat_args=None,
|
||||
set_viewport_size=False,
|
||||
):
|
||||
"""Execute wkhtmltopdf as a subprocess in order to convert html given
|
||||
in input into a pdf document.
|
||||
|
||||
:param bodies: The html bodies of the report, one per page.
|
||||
:param header: The html header of the report containing all headers.
|
||||
:param footer: The html footer of the report containing all footers.
|
||||
:param landscape: Force the pdf to be rendered under a landscape
|
||||
format.
|
||||
:param specific_paperformat_args: dict of prioritized paperformat
|
||||
arguments.
|
||||
:param set_viewport_size: Enable a viewport sized '1024x1280' or
|
||||
'1280x1024' depending of landscape arg.
|
||||
:return: Content of the pdf as a string
|
||||
"""
|
||||
|
||||
# call default odoo standard function of paperformat #19896
|
||||
# https://github.com/odoo/odoo/blob/13.0/odoo/addons/base/models
|
||||
# /ir_actions_report.py#L243
|
||||
paperformat_id = self.get_paperformat()
|
||||
|
||||
# Build the base command args for wkhtmltopdf bin
|
||||
command_args = self._build_wkhtmltopdf_args(
|
||||
paperformat_id,
|
||||
landscape,
|
||||
specific_paperformat_args=specific_paperformat_args,
|
||||
set_viewport_size=set_viewport_size,
|
||||
)
|
||||
|
||||
files_command_args = []
|
||||
temporary_files = []
|
||||
if header:
|
||||
head_file_fd, head_file_path = tempfile.mkstemp(
|
||||
suffix=".html", prefix="report.header.tmp."
|
||||
)
|
||||
with closing(os.fdopen(head_file_fd, "wb")) as head_file:
|
||||
head_file.write(header)
|
||||
temporary_files.append(head_file_path)
|
||||
files_command_args.extend(["--header-html", head_file_path])
|
||||
if footer:
|
||||
foot_file_fd, foot_file_path = tempfile.mkstemp(
|
||||
suffix=".html", prefix="report.footer.tmp."
|
||||
)
|
||||
with closing(os.fdopen(foot_file_fd, "wb")) as foot_file:
|
||||
foot_file.write(footer)
|
||||
temporary_files.append(foot_file_path)
|
||||
files_command_args.extend(["--footer-html", foot_file_path])
|
||||
|
||||
paths = []
|
||||
for i, body in enumerate(bodies):
|
||||
prefix = "%s%d." % ("report.body.tmp.", i)
|
||||
body_file_fd, body_file_path = tempfile.mkstemp(
|
||||
suffix=".html", prefix=prefix
|
||||
)
|
||||
with closing(os.fdopen(body_file_fd, "wb")) as body_file:
|
||||
body_file.write(body)
|
||||
paths.append(body_file_path)
|
||||
temporary_files.append(body_file_path)
|
||||
|
||||
pdf_report_fd, pdf_report_path = tempfile.mkstemp(
|
||||
suffix=".pdf", prefix="report.tmp."
|
||||
)
|
||||
os.close(pdf_report_fd)
|
||||
temporary_files.append(pdf_report_path)
|
||||
try:
|
||||
wkhtmltopdf = (
|
||||
[_get_wkhtmltopdf_bin()]
|
||||
+ command_args
|
||||
+ files_command_args
|
||||
+ paths
|
||||
+ [pdf_report_path]
|
||||
)
|
||||
process = subprocess.Popen(
|
||||
wkhtmltopdf, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
out, err = process.communicate()
|
||||
|
||||
if process.returncode not in [0, 1]:
|
||||
if process.returncode == -11:
|
||||
message = _(
|
||||
"Wkhtmltopdf failed (error code: %s). Memory limit too low or "
|
||||
"maximum file number of subprocess reached. Message : %s"
|
||||
)
|
||||
else:
|
||||
message = _("Wkhtmltopdf failed (error code: %s). Message: %s")
|
||||
_logger.warning(message, process.returncode, err[-1000:])
|
||||
raise UserError(message % (str(process.returncode), err[-1000:]))
|
||||
else:
|
||||
if err:
|
||||
_logger.warning("wkhtmltopdf: %s" % err)
|
||||
# Dynamic Type.
|
||||
if (
|
||||
self
|
||||
and self.custom_report_background
|
||||
and self.custom_report_type == "dynamic"
|
||||
):
|
||||
temp_report_id, temp_report_path = tempfile.mkstemp(
|
||||
suffix=".pdf", prefix="with_back_report.tmp."
|
||||
)
|
||||
output = PdfFileWriter()
|
||||
pdf_reader_content = PdfFileReader(pdf_report_path, "rb")
|
||||
|
||||
# Call method for get domain related to the languages. #22260
|
||||
lang_domain = self.get_bg_per_lang()
|
||||
|
||||
# Added lang_domain in all search methods. #22260
|
||||
first_page = self.background_ids.search(
|
||||
lang_domain
|
||||
+ [
|
||||
("type", "=", "first_page"),
|
||||
("report_id", "=", self.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
last_page = self.background_ids.search(
|
||||
lang_domain
|
||||
+ [
|
||||
("type", "=", "last_page"),
|
||||
("report_id", "=", self.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
fixed_pages = self.background_ids.search(
|
||||
lang_domain
|
||||
+ [
|
||||
("type", "=", "fixed"),
|
||||
("report_id", "=", self.id),
|
||||
]
|
||||
)
|
||||
remaining_pages = self.background_ids.search(
|
||||
lang_domain
|
||||
+ [
|
||||
("type", "=", "remaining"),
|
||||
("report_id", "=", self.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
expression = self.background_ids.search(
|
||||
lang_domain
|
||||
+ [
|
||||
("type", "=", "expression"),
|
||||
("report_id", "=", self.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
|
||||
company_background = self._context.get("background_company")
|
||||
company_background_img = (
|
||||
company_background.custom_report_background_image
|
||||
)
|
||||
company_background_dynamic = company_background.background_ids
|
||||
# Start. #22260
|
||||
if self.is_bg_per_lang:
|
||||
lang_code = self.get_lang()
|
||||
custom_bg_lang = company_background.bg_per_lang_ids.filtered(
|
||||
lambda l: l.lang_id.code == lang_code
|
||||
)
|
||||
# End. #22260
|
||||
for i in range(pdf_reader_content.getNumPages()):
|
||||
watermark = ""
|
||||
if first_page and i == 0:
|
||||
if first_page.fall_back_to_company and company_background:
|
||||
# Start. #22260
|
||||
# If is_bg_per_lang then get custom bg from the company.
|
||||
if self.is_bg_per_lang:
|
||||
watermark = custom_bg_lang[:1].background_pdf
|
||||
else:
|
||||
company_watermark = company_background_dynamic.filtered(
|
||||
lambda a: a.type == "first_page"
|
||||
)
|
||||
if company_watermark:
|
||||
watermark = company_watermark.background_pdf
|
||||
else:
|
||||
watermark = company_background_img
|
||||
# End. #22260
|
||||
# Fix page 1st issue. #22260
|
||||
elif first_page.background_pdf:
|
||||
watermark = first_page.background_pdf
|
||||
elif last_page and i == pdf_reader_content.getNumPages() - 1:
|
||||
if last_page.fall_back_to_company and company_background:
|
||||
# Start. #22260
|
||||
# If is_bg_per_lang then get custom bg from the company.
|
||||
if self.is_bg_per_lang:
|
||||
watermark = custom_bg_lang[:1].background_pdf
|
||||
else:
|
||||
company_watermark = company_background_dynamic.filtered(
|
||||
lambda a: a.type == "last_page"
|
||||
)
|
||||
if company_watermark:
|
||||
watermark = company_watermark.background_pdf
|
||||
else:
|
||||
watermark = company_background_img
|
||||
# End. #22260
|
||||
elif last_page.background_pdf:
|
||||
watermark = last_page.background_pdf
|
||||
elif i + 1 in fixed_pages.mapped("page_number"):
|
||||
fixed_page = fixed_pages.search(
|
||||
[
|
||||
("page_number", "=", i + 1),
|
||||
("report_id", "=", self.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
if (
|
||||
fixed_page
|
||||
and fixed_page.fall_back_to_company
|
||||
and company_background
|
||||
):
|
||||
# Start. #22260
|
||||
# If is_bg_per_lang then get custom bg from the company.
|
||||
if self.is_bg_per_lang:
|
||||
watermark = custom_bg_lang[:1].background_pdf
|
||||
else:
|
||||
company_watermark = company_background_dynamic.filtered(
|
||||
lambda a: a.type == "fixed"
|
||||
and a.page_number == i + 1
|
||||
)
|
||||
if company_watermark:
|
||||
watermark = company_watermark.background_pdf
|
||||
else:
|
||||
watermark = company_background_img
|
||||
# End. #22260
|
||||
elif fixed_page and fixed_page.background_pdf:
|
||||
watermark = fixed_page.background_pdf
|
||||
elif expression and expression.page_expression:
|
||||
eval_dict = {"page": i + 1}
|
||||
safe_eval(
|
||||
expression.page_expression,
|
||||
eval_dict,
|
||||
mode="exec",
|
||||
nocopy=True,
|
||||
)
|
||||
if (
|
||||
expression.fall_back_to_company
|
||||
and company_background
|
||||
and eval_dict.get("result", False)
|
||||
):
|
||||
# Start. #22260
|
||||
# If is_bg_per_lang then get custom bg from the company.
|
||||
if self.is_bg_per_lang:
|
||||
watermark = custom_bg_lang[:1].background_pdf
|
||||
else:
|
||||
company_watermark = company_background_dynamic.filtered(
|
||||
lambda a: a.type == "expression"
|
||||
and a.page_expression
|
||||
)
|
||||
if company_watermark:
|
||||
company_eval_dict = {"page": i + 1}
|
||||
safe_eval(
|
||||
company_watermark.page_expression,
|
||||
company_eval_dict,
|
||||
mode="exec",
|
||||
nocopy=True,
|
||||
)
|
||||
if company_eval_dict.get("result", False):
|
||||
watermark = company_watermark.background_pdf
|
||||
else:
|
||||
watermark = company_background_img
|
||||
else:
|
||||
watermark = company_background_img
|
||||
# End. #22260
|
||||
elif (
|
||||
eval_dict.get("result", False) and expression.background_pdf
|
||||
):
|
||||
watermark = expression.background_pdf
|
||||
else:
|
||||
if remaining_pages:
|
||||
if (
|
||||
remaining_pages.fall_back_to_company
|
||||
and company_background
|
||||
):
|
||||
# Start. #22260
|
||||
# If is_bg_per_lang then get custom bg from
|
||||
# the company.
|
||||
if self.is_bg_per_lang:
|
||||
watermark = custom_bg_lang[:1].background_pdf
|
||||
else:
|
||||
company_watermark = (
|
||||
company_background_dynamic.filtered(
|
||||
lambda a: a.type == "remaining"
|
||||
)
|
||||
)
|
||||
if company_watermark:
|
||||
watermark = company_watermark.background_pdf
|
||||
else:
|
||||
watermark = company_background_img
|
||||
# End. #22260
|
||||
elif remaining_pages.background_pdf:
|
||||
watermark = remaining_pages.background_pdf
|
||||
else:
|
||||
if remaining_pages:
|
||||
if (
|
||||
remaining_pages.fall_back_to_company
|
||||
and company_background
|
||||
):
|
||||
# Start. #22260
|
||||
# If is_bg_per_lang then get custom bg from the company.
|
||||
if self.is_bg_per_lang:
|
||||
watermark = custom_bg_lang[:1].background_pdf
|
||||
else:
|
||||
company_watermark = (
|
||||
company_background_dynamic.filtered(
|
||||
lambda a: a.type == "remaining"
|
||||
)
|
||||
)
|
||||
if company_watermark:
|
||||
watermark = company_watermark.background_pdf
|
||||
else:
|
||||
watermark = company_background_img
|
||||
# End. #22260
|
||||
elif remaining_pages.background_pdf:
|
||||
watermark = remaining_pages.background_pdf
|
||||
if watermark:
|
||||
page = self.add_pdf_watermarks(
|
||||
watermark,
|
||||
pdf_reader_content.getPage(i),
|
||||
)
|
||||
else:
|
||||
page = pdf_reader_content.getPage(i)
|
||||
output.addPage(page)
|
||||
output.write(open(temp_report_path, "wb"))
|
||||
pdf_report_path = temp_report_path
|
||||
os.close(temp_report_id)
|
||||
elif self.custom_report_background:
|
||||
temp_back_id, temp_back_path = tempfile.mkstemp(
|
||||
suffix=".pdf", prefix="back_report.tmp."
|
||||
)
|
||||
custom_background = False
|
||||
# From Report Type.
|
||||
if (
|
||||
self
|
||||
and self.custom_report_background
|
||||
and self.custom_report_type == "report"
|
||||
):
|
||||
# 222760 Starts.If background per lang is True then call method for
|
||||
# get custom background based on different languages.
|
||||
if self.is_bg_per_lang:
|
||||
custom_background = self.get_bg_per_lang()
|
||||
# 222760 Ends.
|
||||
else:
|
||||
custom_background = self.custom_report_background_image
|
||||
# 222760 Ends.
|
||||
# From Company Type.
|
||||
if (
|
||||
self.custom_report_background
|
||||
and not custom_background
|
||||
and (
|
||||
self.custom_report_type == "company"
|
||||
or not self.custom_report_type
|
||||
)
|
||||
and self._context.get("background_company") # #19896
|
||||
):
|
||||
# report background will be displayed based on the current
|
||||
# company #19896
|
||||
company_id = self._context.get("background_company")
|
||||
# 222760 Starts. If background per lang is True then call method for
|
||||
# get custom background from company based on different languages.
|
||||
if self.is_bg_per_lang:
|
||||
custom_background = self.get_bg_per_lang()
|
||||
# 222760 Ends.
|
||||
else:
|
||||
custom_background = company_id.custom_report_background_image
|
||||
# If background found from any type then set that to the report.
|
||||
if custom_background:
|
||||
back_data = base64.b64decode(custom_background)
|
||||
with closing(os.fdopen(temp_back_id, "wb")) as back_file:
|
||||
back_file.write(back_data)
|
||||
temp_report_id, temp_report_path = tempfile.mkstemp(
|
||||
suffix=".pdf", prefix="with_back_report.tmp."
|
||||
)
|
||||
output = PdfFileWriter()
|
||||
pdf_reader_content = PdfFileReader(pdf_report_path, "rb")
|
||||
|
||||
for i in range(pdf_reader_content.getNumPages()):
|
||||
page = pdf_reader_content.getPage(i)
|
||||
pdf_reader_watermark = PdfFileReader(temp_back_path, "rb")
|
||||
watermark = pdf_reader_watermark.getPage(0)
|
||||
watermark.mergePage(page)
|
||||
output.addPage(watermark)
|
||||
output.write(open(temp_report_path, "wb"))
|
||||
pdf_report_path = temp_report_path
|
||||
os.close(temp_report_id)
|
||||
except Exception as ex:
|
||||
logging.info("Error while PDF Background %s" % ex)
|
||||
raise
|
||||
|
||||
with open(pdf_report_path, "rb") as pdf_document:
|
||||
pdf_content = pdf_document.read()
|
||||
|
||||
# Manual cleanup of the temporary files
|
||||
for temporary_file in temporary_files:
|
||||
try:
|
||||
os.unlink(temporary_file)
|
||||
except (OSError, IOError):
|
||||
_logger.error("Error when trying to remove file %s" % temporary_file)
|
||||
|
||||
return pdf_content
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from odoo import fields, models
|
||||
|
||||
|
||||
class ReportBackgroundLang(models.Model):
|
||||
_name = "report.background.lang"
|
||||
_description = "Report Background Line Per Language"
|
||||
|
||||
# New fields. #22260
|
||||
lang_id = fields.Many2one(
|
||||
"res.lang",
|
||||
required=True,
|
||||
string="Language",
|
||||
)
|
||||
background_pdf = fields.Binary(string="Background PDF", required=True)
|
||||
file_name = fields.Char(string="File Name")
|
||||
report_id = fields.Many2one("ir.actions.report", string="Report")
|
||||
company_id = fields.Many2one(
|
||||
"res.company",
|
||||
string="Company",
|
||||
)
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
# See LICENSE file for full copyright and licensing details.
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
custom_report_background_image = fields.Binary(string="Custom Report Background")
|
||||
# New field. #22260
|
||||
is_bg_per_lang = fields.Boolean(
|
||||
string="Is Background Per Language",
|
||||
)
|
||||
bg_per_lang_ids = fields.One2many(
|
||||
"report.background.lang",
|
||||
"company_id",
|
||||
string="Background Per Language",
|
||||
)
|
||||
|
||||
# Added new field #T5211
|
||||
is_dynamic_background = fields.Boolean(
|
||||
string="Is Dynamic Background",
|
||||
)
|
||||
background_ids = fields.One2many(
|
||||
"report.background.line", "company_id", "Background Configuration"
|
||||
)
|
||||
|
||||
@api.constrains(
|
||||
"is_bg_per_lang", "bg_per_lang_ids", "is_dynamic_background", "background_ids"
|
||||
)
|
||||
def _check_company_custom_bg_config(self):
|
||||
"""New constrains method for check custom bg per company is set or not when
|
||||
'From Company' type is set at ir_actions_report level. #22260"""
|
||||
# Env.
|
||||
report_env = self.env["ir.actions.report"]
|
||||
# Search report based on the 'company' type and 'is_bg_per_lang' boolean.
|
||||
report_ids = report_env.search(
|
||||
[
|
||||
("custom_report_type", "in", ["company", False]),
|
||||
("is_bg_per_lang", "=", True),
|
||||
]
|
||||
)
|
||||
# Search dynamic reoprt.
|
||||
dynamic_report_ids = report_env.search(
|
||||
[
|
||||
("custom_report_type", "=", "dynamic"),
|
||||
("is_bg_per_lang", "=", True),
|
||||
]
|
||||
)
|
||||
is_fall_back_to_company = False
|
||||
if dynamic_report_ids:
|
||||
# Get report in which Fall back to company is true.
|
||||
is_fall_back_to_company = dynamic_report_ids.mapped(
|
||||
"background_ids"
|
||||
).filtered(lambda r: r.fall_back_to_company)
|
||||
# If fall_back_to_company and custom bg per lang is not set then raise warning.
|
||||
if is_fall_back_to_company and not (
|
||||
self.is_bg_per_lang and self.bg_per_lang_ids
|
||||
):
|
||||
raise UserError(
|
||||
_(
|
||||
"Please configure Custom Background Per Language beacuse "
|
||||
"'Fall Back To Company' is set in the dynamic type report level!"
|
||||
)
|
||||
)
|
||||
# If any report with company type and custom bg per lang is not set at
|
||||
# res_company level then raise warning.
|
||||
if report_ids and not (self.is_bg_per_lang and self.bg_per_lang_ids):
|
||||
raise UserError(
|
||||
_(
|
||||
"Please configure Custom Background Per Language beacuse "
|
||||
"'From Company' type is set at the Report level!"
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_report_background_line,access_report_background_line,model_report_background_line,base.group_user,1,0,0,0
|
||||
access_report_background_lang_user,access_report_background_lang,model_report_background_lang,base.group_user,1,0,0,0
|
||||
access_report_background_line_system,access_report_background_line_system,model_report_background_line,base.group_system,1,1,1,1
|
||||
access_report_background_lang_admin,access_report_background_lang,model_report_background_lang,base.group_system,1,1,1,1
|
||||
|
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 261 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
<section class="container pt-0" style="font-family:sans-serif;background-color: #5d5d59;color: white;">
|
||||
<div class="row mt-1 text-center">
|
||||
<div class="col-sm-3 mt-sm-4 text-decoration-none">
|
||||
<span href="https://www.bizzappdev.com">
|
||||
<img class="float-sm-right col-sm-12" src="images/logo.png">
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-6 mt-3">
|
||||
<span class="oe_slogan" style="color: #d6df00; font-size: calc(1.2rem + 1.1vw);">
|
||||
<b>Custom Background</b>
|
||||
</span>
|
||||
<br/>
|
||||
<hr style="border: 1px solid #ffffff;"/>
|
||||
</div>
|
||||
<div class="col-sm-3 mt-sm-4">
|
||||
<div class="float-sm-left text-decoration-none">
|
||||
<span href="https://www.bizzappdev.com/#contact">
|
||||
<h3 style="color: white;">
|
||||
<span style="color: #d6df00;">Contact</span> With Us
|
||||
</h3>
|
||||
</span>
|
||||
<a href="mailto:contact@bizzappdev.com?subject=OdooAppStore: Custom Background" style="color: #ffffff;">contact@bizzappdev.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container mt-3" style="font-family: sans-serif;">
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-12">
|
||||
<div class="h3" style="color: #383835;">Module <i>Custom Background</i> facilitates is to set the Header and footer or watermark in the report based on predifined PDF configured. the predefined PDF will be used as background PDF in all the page of the report(QWeb-PDF).
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container mt-3" style="font-family: sans-serif;">
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-12">
|
||||
<div class="h4 p-3" style="color: #383835;">
|
||||
<b>At level of the report you can configure that background pdf. You can configure based on company, language, or specifically for the report only. You can configure different PDF for First and last page as well as for page range.</b>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container mt-3" style="font-family: sans-serif;">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="ml-5" style="color: #383835;">
|
||||
<b class="oe_mt32"><h4>There are different options for configuration</h4></b>
|
||||
<ul style="color: #383835;">
|
||||
<li>Fixed background image from Company</li>
|
||||
<li>Fixed background image from Report</li>
|
||||
<li>Dynamic image from report</li>
|
||||
<li>Background image from report based on language</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container mt-5" style="font-family: sans-serif;">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 h3 text-center">
|
||||
<div style="color: #5d5d59;">
|
||||
<b>You can select custom reprot type based on your requirements</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 text-center">
|
||||
<img class="img img-fluid col-md-11 my-3 shadow-lg bg-white rounded px-0" src="images/BackgroundConfigurationSelection.png"/>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container mt-5" style="font-family: sans-serif;">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 h3 text-center">
|
||||
<div style="color: #5d5d59;">
|
||||
<b>You can also configure Dynamic Background in your PDF as per your requirements</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 text-center">
|
||||
<img class="img img-fluid col-md-11 my-3 shadow-lg bg-white rounded px-0" src="images/BackgroundConfiguration.png"/>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container mt-5" style="font-family: sans-serif;">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 h3 text-center">
|
||||
<div style="color: #5d5d59;">
|
||||
<b>You can configure here Dynamic Background as per your requirements</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 text-center">
|
||||
<img class="img img-fluid col-md-11 my-3 shadow-lg bg-white rounded px-0" src="images/CreateBackgroundConfiguration.png"/>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container mt-5" style="font-family: sans-serif;">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 h3 text-center">
|
||||
<div style="color: #5d5d59;">
|
||||
<b>You can configure Fixed Background Based on Langauge as per your requirements</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 text-center">
|
||||
<img class="img img-fluid col-md-11 my-3 shadow-lg bg-white rounded px-0" src="images/FixedBackgroundBasedOnLang.png"/>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container mt-5" style="font-family: sans-serif;">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 h3 text-center">
|
||||
<div style="color: #5d5d59;">
|
||||
<b>Report Without Background</b>
|
||||
</div>
|
||||
<div>
|
||||
<img src="https://user-images.githubusercontent.com/4273984/133068606-a8085840-36c4-4e67-8fcd-6b3896a88c33.png" class="img img-fluid col-sm-11 my-3 ml-md-5 shadow-lg bg-white rounded px-0" />
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 h3 text-center">
|
||||
<div style="color: #5d5d59;">
|
||||
<b>Report With Background</b>
|
||||
</div>
|
||||
<div>
|
||||
<img src="https://user-images.githubusercontent.com/4273984/133068633-5c7f0b97-4ede-46f0-8be8-8b326bbe3bbe.png" class="img img-fluid col-sm-11 my-3 mr-md-5 shadow-lg bg-white rounded px-0" />
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container pt-3 pb-2" style="font-family:sans-serif;background-color: #5d5d59;color: white;">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="col-sm-8 m-auto">
|
||||
<div class="col-sm-6 float-left text-decoration-none">
|
||||
<span href="https://www.bizzappdev.com">
|
||||
<img class="float-md-right col-md-12 mb-3 mt-3" src="images/logo.png">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 float-md-left text-md-left text-center">
|
||||
<div>
|
||||
<h4 style="color: white;">Need Any Help ?</h4>
|
||||
<span href="https://www.bizzappdev.com/#contact">
|
||||
<h1 style="color: white;"><span style="color:#d6df00">Contact</span> With Us</h1>
|
||||
<div>
|
||||
<span class="fa fa-envelope" style="color: white;"></span>
|
||||
<a href="mailto:contact@bizzappdev.com?subject=OdooAppStore: Custom Background" style="color: #ffffff;">contact@bizzappdev.com</a>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
body {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="view_ir_action_inherit_form" model="ir.ui.view">
|
||||
<field name="name">ir.actions.report.form</field>
|
||||
<field name="model">ir.actions.report</field>
|
||||
<field name="type">form</field>
|
||||
<field name="inherit_id" ref="base.act_report_xml_view" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='report_type']" position="after">
|
||||
<field name="custom_report_background" select="1" />
|
||||
<field
|
||||
name="custom_report_type"
|
||||
attrs="{'invisible':
|
||||
[('custom_report_background', '!=', True)]}"
|
||||
/>
|
||||
<!-- Added is_bg_per_lang related domain. #22260 -->
|
||||
<field
|
||||
name="custom_report_background_image"
|
||||
attrs="{'invisible': ['|', '|', ('custom_report_type','!=',
|
||||
'report'), ('custom_report_background', '!=', True), ('is_bg_per_lang', '=', True)]}"
|
||||
/>
|
||||
<!-- New field. #22260 -->
|
||||
<field
|
||||
name="is_bg_per_lang"
|
||||
attrs="{'invisible': [('custom_report_background', '=', False)]}"
|
||||
/>
|
||||
</xpath>
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page
|
||||
string="Background Configuration"
|
||||
attrs="{'invisible': ['|', ('custom_report_type','!=','dynamic'),
|
||||
('custom_report_background', '=', False)]}"
|
||||
>
|
||||
<field name="background_ids">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="type" />
|
||||
<field name="fall_back_to_company" />
|
||||
<field
|
||||
name="page_number"
|
||||
attrs="{'invisible': [('type', '!=', 'fixed')]}"
|
||||
/>
|
||||
<field
|
||||
name="page_expression"
|
||||
attrs="{'invisible': [('type', '!=', 'expression')]}"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<field
|
||||
name="background_pdf"
|
||||
filename="file_name"
|
||||
/>
|
||||
<!-- New field. #22260 -->
|
||||
<field name="file_name" invisible="1" />
|
||||
<!-- New field. #22260 -->
|
||||
<field
|
||||
name="lang_id"
|
||||
attrs="{'readonly': [('parent.is_bg_per_lang', '!=', True)], 'required': [('parent.is_bg_per_lang', '=', True)]}"
|
||||
/>
|
||||
<field name="report_id" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
<tree>
|
||||
<field name="type" />
|
||||
<field name="page_number" invisible="1" />
|
||||
<field name="page_expression" invisible="1" />
|
||||
<!-- New field. #22260 -->
|
||||
<field name="file_name" string="Background PDF" />
|
||||
<!-- New field. #22260 -->
|
||||
<field name="lang_id" />
|
||||
<field name="report_id" invisible="1" />
|
||||
</tree>
|
||||
</field>
|
||||
<i
|
||||
class="fa fa-info fa-3x text-info float-left"
|
||||
role="img"
|
||||
aria-label="Info"
|
||||
title="Info"
|
||||
/>
|
||||
<h3>Interaction with the background configuration</h3>
|
||||
<div>
|
||||
<p>
|
||||
You can set the backgroud watermark in a report based on
|
||||
the options set in the "Background Configuration" tab.
|
||||
</p>
|
||||
<p>
|
||||
Detailed algorithm (with their priorities):
|
||||
<ol>
|
||||
<li>
|
||||
<b>First Page:</b>
|
||||
If the option "First Page" is chosen it will not
|
||||
look at any other configurations for the first
|
||||
page of the PDF.
|
||||
<br />
|
||||
If no "Background PDF" is set on the "First
|
||||
Page" rule then it will set the background PDF
|
||||
from the "Fixed Page", "Expression Page" or
|
||||
"Remaining Pages".
|
||||
</li>
|
||||
<li>
|
||||
<b>Last Page:</b>
|
||||
If the option "Last Page" is selected, it will
|
||||
not check for any other configurations for the
|
||||
last page.
|
||||
It will only set the last page background PDF
|
||||
that you've applied.
|
||||
<br />
|
||||
If there was no "Background PDF" set on the last
|
||||
page then it will set the background PDF from
|
||||
the "Fixed Page", "Expression Page" or
|
||||
"Remaining Pages".
|
||||
<br />
|
||||
</li>
|
||||
<li>
|
||||
<b>Fixed Pages:</b>
|
||||
If the option "Fixed Pages" is selected you can
|
||||
enter for which page you'd like to use the
|
||||
"Background PDF".
|
||||
For example: setting a custom background PDF for
|
||||
the third page of a report by entering "3" in
|
||||
the field "Page Number".
|
||||
<br />
|
||||
If the entered page number is first and last
|
||||
then it will set the background from "First
|
||||
Page" and "Last Page" if we configured the first
|
||||
page and the last page otherwise it will use the
|
||||
"Fixed Page" configuration.
|
||||
</li>
|
||||
<li>
|
||||
<b>Expression:</b>
|
||||
If the option "Expression" is selected you can
|
||||
set custom background PDF's based on
|
||||
expressions.
|
||||
<br />
|
||||
For example if you want only a background on
|
||||
every second page you could do:
|
||||
<br />
|
||||
result=page%2==0
|
||||
<br />
|
||||
If you want only a background on every uneven
|
||||
page (for example page 1,3 ,5) you could do:
|
||||
<br />
|
||||
result=page%2!=0
|
||||
<br />
|
||||
Or if you only want a background starting from
|
||||
page 2 on for example you could do:
|
||||
<br />
|
||||
result=page>2
|
||||
</li>
|
||||
<li>
|
||||
<b>Remaining Pages:</b>
|
||||
If the option "Remaining Pages" is selected the
|
||||
module will apply the "Background PDF" set on
|
||||
this rule for any pages that have no rule.
|
||||
You could use this for example if you have a
|
||||
rule "First Page" set and then have a second
|
||||
rule "Remaining Pages" it would apply this
|
||||
background on page 2, 3, .. and any other page
|
||||
you have not configured.
|
||||
</li>
|
||||
</ol>
|
||||
Extra Note
|
||||
<li>
|
||||
If there are no "Background Configuration" rules
|
||||
added for any line it will work as a normal Odoo
|
||||
report and will not apply any background to the
|
||||
report.
|
||||
</li>
|
||||
</p>
|
||||
</div>
|
||||
</page>
|
||||
<!-- New Page for configure custom BG per Language. #22260 -->
|
||||
<page
|
||||
string="Background Configuration Per Language"
|
||||
attrs="{'invisible': ['|', '|', ('is_bg_per_lang', '!=', True), ('custom_report_background', '!=', True), ('custom_report_type', '!=', 'report')]}"
|
||||
>
|
||||
<field name="bg_per_lang_ids">
|
||||
<form>
|
||||
<group>
|
||||
<group>
|
||||
<field name="lang_id" />
|
||||
</group>
|
||||
<group>
|
||||
<field
|
||||
name="background_pdf"
|
||||
filename="file_name"
|
||||
/>
|
||||
<field name="file_name" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
<tree>
|
||||
<field name="lang_id" />
|
||||
<field name="file_name" string="Background PDF" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Inherit template for set the transparent backgrounf in PFD #T4818 -->
|
||||
<template id="report_assets_pdf_inherited" inherit_id="web.report_assets_pdf">
|
||||
<link position="after">
|
||||
<link
|
||||
href="/custom_background/static/src/scss/report_qweb_pdf.scss"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</link>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="res_company_view_inherit" model="ir.ui.view">
|
||||
<field name="name">res.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="inherit_id" ref="base.view_company_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page name="configuration" string="Configuration">
|
||||
<group>
|
||||
<field name="is_dynamic_background" />
|
||||
<field name="is_bg_per_lang" />
|
||||
<separator
|
||||
string="Custom Background Image"
|
||||
colspan="2"
|
||||
attrs="{'invisible': ['|', ('is_bg_per_lang', '=', True), ('is_dynamic_background', '=', True)]}"
|
||||
/>
|
||||
<field
|
||||
name="custom_report_background_image"
|
||||
attrs="{'invisible': ['|', ('is_bg_per_lang', '=', True), ('is_dynamic_background', '=', True)]}"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<!-- New o2m field for configure custom bg based on different languages. #22260 -->
|
||||
<separator
|
||||
string="Custom Background Image Per Language"
|
||||
colspan="2"
|
||||
attrs="{'invisible': ['|', ('is_bg_per_lang', '!=', True), ('is_dynamic_background', '=', True)]}"
|
||||
/>
|
||||
<field
|
||||
name="bg_per_lang_ids"
|
||||
attrs="{'invisible': ['|', ('is_bg_per_lang', '!=', True), ('is_dynamic_background', '=', True)]}"
|
||||
widget="one2many"
|
||||
nolabel="1"
|
||||
>
|
||||
<form>
|
||||
<group>
|
||||
<group>
|
||||
<field name="lang_id" />
|
||||
</group>
|
||||
<group>
|
||||
<field
|
||||
name="background_pdf"
|
||||
filename="file_name"
|
||||
/>
|
||||
<field name="file_name" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
<tree>
|
||||
<field name="lang_id" />
|
||||
<field name="file_name" string="Background PDF" />
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<!-- Added new fields for configurations of background report #T5211 -->
|
||||
<group>
|
||||
<separator
|
||||
string="Custom Background Configuration"
|
||||
colspan="2"
|
||||
attrs="{'invisible': [('is_dynamic_background', '!=', True)]}"
|
||||
/>
|
||||
<field
|
||||
name="background_ids"
|
||||
attrs="{'invisible': [('is_dynamic_background', '!=', True)]}"
|
||||
widget="one2many"
|
||||
nolabel="1"
|
||||
>
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="type" />
|
||||
<field
|
||||
name="page_number"
|
||||
attrs="{'invisible': [('type', '!=', 'fixed')]}"
|
||||
/>
|
||||
<field
|
||||
name="page_expression"
|
||||
attrs="{'invisible': [('type', '!=', 'expression')]}"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<field
|
||||
name="background_pdf"
|
||||
filename="file_name"
|
||||
/>
|
||||
<field name="file_name" invisible="1" />
|
||||
<field
|
||||
name="lang_id"
|
||||
attrs="{'readonly': [('parent.is_bg_per_lang', '!=', True)], 'required': [('parent.is_bg_per_lang', '=', True)]}"
|
||||
/>
|
||||
<field name="report_id" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
<tree>
|
||||
<field name="type" />
|
||||
<field name="page_number" invisible="1" />
|
||||
<field name="page_expression" invisible="1" />
|
||||
<field name="file_name" string="Background PDF" />
|
||||
<field name="lang_id" />
|
||||
<field name="report_id" invisible="1" />
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
================
|
||||
Web Domain Field
|
||||
================
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:dc8abc1ad57856bda62ec4d3f0ff74f3029cf9ebb32ba41c8ae449867393ea65
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Production/Stable
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/web/tree/14.0/web_domain_field
|
||||
:alt: OCA/web
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/web-14-0/web-14-0-web_domain_field
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||
:target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=14.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
When you define a view you can specify on the relational fields a domain
|
||||
attribute. This attribute is evaluated as filter to apply when displaying
|
||||
existing records for selection.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
When you define a view you can specify on the relational fields a domain
|
||||
attribute. This attribute is evaluated as filter to apply when displaying
|
||||
existing records for selection.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<field name="product_id" domain="[('type','=','product')]"/>
|
||||
|
||||
The value provided for the domain attribute must be a string representing a
|
||||
valid Odoo domain. This string is evaluated on the client side in a
|
||||
restricted context where we can reference as right operand the values of
|
||||
fields present into the form and a limited set of functions.
|
||||
|
||||
In this context it's hard to build complex domain and we are facing to some
|
||||
limitations as:
|
||||
|
||||
* The syntax to include in your domain a criteria involving values from a
|
||||
x2many field is complex.
|
||||
* The right side of domain in case of x2many can involve huge amount of ids
|
||||
(performance problem).
|
||||
* Domains computed by an onchange on an other field are not recomputed when
|
||||
you modify the form and don't modify the field triggering the onchange.
|
||||
* It's not possible to extend an existing domain. You must completely redefine
|
||||
the domain in your specialized addon
|
||||
* etc...
|
||||
|
||||
In order to mitigate these limitations this new addon allows you to use the
|
||||
value of a field as domain of an other field in the xml definition of your
|
||||
view.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<field name="product_id_domain" invisible="1"/>
|
||||
<field name="product_id" domain="product_id_domain"/>
|
||||
|
||||
The field used as domain must provide the domain as a JSON encoded string.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
product_id_domain = fields.Char(
|
||||
compute="_compute_product_id_domain",
|
||||
readonly=True,
|
||||
store=False,
|
||||
)
|
||||
|
||||
@api.depends('name')
|
||||
def _compute_product_id_domain(self):
|
||||
for rec in self:
|
||||
rec.product_id_domain = json.dumps(
|
||||
[('type', '=', 'product'), ('name', 'like', rec.name)]
|
||||
)
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_domain_field%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
* ACSONE SA/NV
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* Laurent Mignon <laurent.mignon@acsone.eu>
|
||||
* Denis Roussel <denis.roussel@acsone.eu>
|
||||
* Raf Ven <raf.ven@dynapps.be>
|
||||
|
||||
Maintainers
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
This module is part of the `OCA/web <https://github.com/OCA/web/tree/14.0/web_domain_field>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright 2017 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
{
|
||||
"name": "Web Domain Field",
|
||||
"summary": """
|
||||
Use computed field as domain""",
|
||||
"version": "14.0.1.0.2",
|
||||
"development_status": "Production/Stable",
|
||||
"license": "AGPL-3",
|
||||
"author": "ACSONE SA/NV,Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/web",
|
||||
"depends": ["web"],
|
||||
"data": ["views/web_domain_field.xml"],
|
||||
"installable": True,
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
* Laurent Mignon <laurent.mignon@acsone.eu>
|
||||
* Denis Roussel <denis.roussel@acsone.eu>
|
||||
* Raf Ven <raf.ven@dynapps.be>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
When you define a view you can specify on the relational fields a domain
|
||||
attribute. This attribute is evaluated as filter to apply when displaying
|
||||
existing records for selection.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
When you define a view you can specify on the relational fields a domain
|
||||
attribute. This attribute is evaluated as filter to apply when displaying
|
||||
existing records for selection.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<field name="product_id" domain="[('type','=','product')]"/>
|
||||
|
||||
The value provided for the domain attribute must be a string representing a
|
||||
valid Odoo domain. This string is evaluated on the client side in a
|
||||
restricted context where we can reference as right operand the values of
|
||||
fields present into the form and a limited set of functions.
|
||||
|
||||
In this context it's hard to build complex domain and we are facing to some
|
||||
limitations as:
|
||||
|
||||
* The syntax to include in your domain a criteria involving values from a
|
||||
x2many field is complex.
|
||||
* The right side of domain in case of x2many can involve huge amount of ids
|
||||
(performance problem).
|
||||
* Domains computed by an onchange on an other field are not recomputed when
|
||||
you modify the form and don't modify the field triggering the onchange.
|
||||
* It's not possible to extend an existing domain. You must completely redefine
|
||||
the domain in your specialized addon
|
||||
* etc...
|
||||
|
||||
In order to mitigate these limitations this new addon allows you to use the
|
||||
value of a field as domain of an other field in the xml definition of your
|
||||
view.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<field name="product_id_domain" invisible="1"/>
|
||||
<field name="product_id" domain="product_id_domain"/>
|
||||
|
||||
The field used as domain must provide the domain as a JSON encoded string.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
product_id_domain = fields.Char(
|
||||
compute="_compute_product_id_domain",
|
||||
readonly=True,
|
||||
store=False,
|
||||
)
|
||||
|
||||
@api.depends('name')
|
||||
def _compute_product_id_domain(self):
|
||||
for rec in self:
|
||||
rec.product_id_domain = json.dumps(
|
||||
[('type', '=', 'product'), ('name', 'like', rec.name)]
|
||||
)
|
||||
|
After Width: | Height: | Size: 9.2 KiB |
|
|
@ -0,0 +1,476 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
|
||||
<title>Web Domain Field</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
|
||||
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
.subscript {
|
||||
vertical-align: sub;
|
||||
font-size: smaller }
|
||||
|
||||
.superscript {
|
||||
vertical-align: super;
|
||||
font-size: smaller }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title, .code .error {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: inherit }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
.align-top {
|
||||
vertical-align: top }
|
||||
|
||||
.align-middle {
|
||||
vertical-align: middle }
|
||||
|
||||
.align-bottom {
|
||||
vertical-align: bottom }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
pre.code .ln { color: grey; } /* line numbers */
|
||||
pre.code, code { background-color: #eeeeee }
|
||||
pre.code .comment, code .comment { color: #5C6576 }
|
||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
/* "booktabs" style (no vertical lines) */
|
||||
table.docutils.booktabs {
|
||||
border: 0px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.docutils.booktabs * {
|
||||
border: 0px;
|
||||
}
|
||||
table.docutils.booktabs th {
|
||||
border-bottom: thin solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="web-domain-field">
|
||||
<h1 class="title">Web Domain Field</h1>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:dc8abc1ad57856bda62ec4d3f0ff74f3029cf9ebb32ba41c8ae449867393ea65
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/web/tree/14.0/web_domain_field"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/web-14-0/web-14-0-web_domain_field"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=14.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||
<p>When you define a view you can specify on the relational fields a domain
|
||||
attribute. This attribute is evaluated as filter to apply when displaying
|
||||
existing records for selection.</p>
|
||||
<p><strong>Table of contents</strong></p>
|
||||
<div class="contents local topic" id="contents">
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a></li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="usage">
|
||||
<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
|
||||
<p>When you define a view you can specify on the relational fields a domain
|
||||
attribute. This attribute is evaluated as filter to apply when displaying
|
||||
existing records for selection.</p>
|
||||
<pre class="code xml literal-block">
|
||||
<span class="nt"><field</span><span class="w"> </span><span class="na">name=</span><span class="s">"product_id"</span><span class="w"> </span><span class="na">domain=</span><span class="s">"[('type','=','product')]"</span><span class="nt">/></span>
|
||||
</pre>
|
||||
<p>The value provided for the domain attribute must be a string representing a
|
||||
valid Odoo domain. This string is evaluated on the client side in a
|
||||
restricted context where we can reference as right operand the values of
|
||||
fields present into the form and a limited set of functions.</p>
|
||||
<p>In this context it’s hard to build complex domain and we are facing to some
|
||||
limitations as:</p>
|
||||
<blockquote>
|
||||
<ul class="simple">
|
||||
<li>The syntax to include in your domain a criteria involving values from a
|
||||
x2many field is complex.</li>
|
||||
<li>The right side of domain in case of x2many can involve huge amount of ids
|
||||
(performance problem).</li>
|
||||
<li>Domains computed by an onchange on an other field are not recomputed when
|
||||
you modify the form and don’t modify the field triggering the onchange.</li>
|
||||
<li>It’s not possible to extend an existing domain. You must completely redefine
|
||||
the domain in your specialized addon</li>
|
||||
<li>etc…</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p>In order to mitigate these limitations this new addon allows you to use the
|
||||
value of a field as domain of an other field in the xml definition of your
|
||||
view.</p>
|
||||
<pre class="code xml literal-block">
|
||||
<span class="nt"><field</span><span class="w"> </span><span class="na">name=</span><span class="s">"product_id_domain"</span><span class="w"> </span><span class="na">invisible=</span><span class="s">"1"</span><span class="nt">/></span><span class="w">
|
||||
</span><span class="nt"><field</span><span class="w"> </span><span class="na">name=</span><span class="s">"product_id"</span><span class="w"> </span><span class="na">domain=</span><span class="s">"product_id_domain"</span><span class="nt">/></span>
|
||||
</pre>
|
||||
<p>The field used as domain must provide the domain as a JSON encoded string.</p>
|
||||
<pre class="code python literal-block">
|
||||
<span class="n">product_id_domain</span> <span class="o">=</span> <span class="n">fields</span><span class="o">.</span><span class="n">Char</span><span class="p">(</span><span class="w">
|
||||
</span> <span class="n">compute</span><span class="o">=</span><span class="s2">"_compute_product_id_domain"</span><span class="p">,</span><span class="w">
|
||||
</span> <span class="n">readonly</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span><span class="w">
|
||||
</span> <span class="n">store</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span><span class="w">
|
||||
</span><span class="p">)</span><span class="w">
|
||||
|
||||
</span><span class="nd">@api</span><span class="o">.</span><span class="n">depends</span><span class="p">(</span><span class="s1">'name'</span><span class="p">)</span><span class="w">
|
||||
</span><span class="k">def</span> <span class="nf">_compute_product_id_domain</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><span class="w">
|
||||
</span> <span class="k">for</span> <span class="n">rec</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">:</span><span class="w">
|
||||
</span> <span class="n">rec</span><span class="o">.</span><span class="n">product_id_domain</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="w">
|
||||
</span> <span class="p">[(</span><span class="s1">'type'</span><span class="p">,</span> <span class="s1">'='</span><span class="p">,</span> <span class="s1">'product'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'name'</span><span class="p">,</span> <span class="s1">'like'</span><span class="p">,</span> <span class="n">rec</span><span class="o">.</span><span class="n">name</span><span class="p">)]</span><span class="w">
|
||||
</span> <span class="p">)</span>
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_domain_field%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
|
||||
<div class="section" id="authors">
|
||||
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>ACSONE SA/NV</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Laurent Mignon <<a class="reference external" href="mailto:laurent.mignon@acsone.eu">laurent.mignon@acsone.eu</a>></li>
|
||||
<li>Denis Roussel <<a class="reference external" href="mailto:denis.roussel@acsone.eu">denis.roussel@acsone.eu</a>></li>
|
||||
<li>Raf Ven <<a class="reference external" href="mailto:raf.ven@dynapps.be">raf.ven@dynapps.be</a>></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
|
||||
<p>This module is maintained by the OCA.</p>
|
||||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
|
||||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/14.0/web_domain_field">OCA/web</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
odoo.define('web.domain_field', function (require) {
|
||||
"use strict";
|
||||
|
||||
var py_utils = require('web.py_utils');
|
||||
var session = require('web.session');
|
||||
|
||||
|
||||
var original_pyeval = py_utils.eval;
|
||||
var py = window.py;
|
||||
|
||||
/** Copied from py_utils and not modified but required since not publicly
|
||||
exposed by web.py_utils**/
|
||||
|
||||
// recursively wraps JS objects passed into the context to attributedicts
|
||||
// which jsonify back to JS objects
|
||||
function wrap(value) {
|
||||
if (value === null) { return py.None; }
|
||||
|
||||
switch (typeof value) {
|
||||
case 'undefined': throw new Error("No conversion for undefined");
|
||||
case 'boolean': return py.bool.fromJSON(value);
|
||||
case 'number': return py.float.fromJSON(value);
|
||||
case 'string': return py.str.fromJSON(value);
|
||||
}
|
||||
|
||||
switch(value.constructor) {
|
||||
case Object: return wrapping_dict.fromJSON(value);
|
||||
case Array: return wrapping_list.fromJSON(value);
|
||||
}
|
||||
|
||||
throw new Error("ValueError: unable to wrap " + value);
|
||||
}
|
||||
|
||||
var wrapping_dict = py.type('wrapping_dict', null, {
|
||||
__init__: function () {
|
||||
this._store = {};
|
||||
},
|
||||
__getitem__: function (key) {
|
||||
var k = key.toJSON();
|
||||
if (!(k in this._store)) {
|
||||
throw new Error("KeyError: '" + k + "'");
|
||||
}
|
||||
return wrap(this._store[k]);
|
||||
},
|
||||
__getattr__: function (key) {
|
||||
return this.__getitem__(py.str.fromJSON(key));
|
||||
},
|
||||
__len__: function () {
|
||||
return Object.keys(this._store).length;
|
||||
},
|
||||
__nonzero__: function () {
|
||||
return py.PY_size(this) > 0 ? py.True : py.False;
|
||||
},
|
||||
get: function () {
|
||||
var args = py.PY_parseArgs(arguments, ['k', ['d', py.None]]);
|
||||
|
||||
if (!(args.k.toJSON() in this._store)) { return args.d; }
|
||||
return this.__getitem__(args.k);
|
||||
},
|
||||
fromJSON: function (d) {
|
||||
var instance = py.PY_call(wrapping_dict);
|
||||
instance._store = d;
|
||||
return instance;
|
||||
},
|
||||
toJSON: function () {
|
||||
return this._store;
|
||||
},
|
||||
});
|
||||
|
||||
var wrapping_list = py.type('wrapping_list', null, {
|
||||
__init__: function () {
|
||||
this._store = [];
|
||||
},
|
||||
__getitem__: function (index) {
|
||||
return wrap(this._store[index.toJSON()]);
|
||||
},
|
||||
__len__: function () {
|
||||
return this._store.length;
|
||||
},
|
||||
__nonzero__: function () {
|
||||
return py.PY_size(this) > 0 ? py.True : py.False;
|
||||
},
|
||||
fromJSON: function (ar) {
|
||||
var instance = py.PY_call(wrapping_list);
|
||||
instance._store = ar;
|
||||
return instance;
|
||||
},
|
||||
toJSON: function () {
|
||||
return this._store;
|
||||
},
|
||||
});
|
||||
|
||||
function wrap_context(context) {
|
||||
for (var k in context) {
|
||||
if (!context.hasOwnProperty(k)) { continue; }
|
||||
var val = context[k];
|
||||
// Don't add a test case like ``val === undefined``
|
||||
// this is intended to prevent letting crap pass
|
||||
// on the context without even knowing it.
|
||||
// If you face an issue from here, try to sanitize
|
||||
// the context upstream instead
|
||||
if (val === null) { continue; }
|
||||
if (val.constructor === Array) {
|
||||
context[k] = wrapping_list.fromJSON(val);
|
||||
} else if (val.constructor === Object
|
||||
&& !py.PY_isInstance(val, py.object)) {
|
||||
context[k] = wrapping_dict.fromJSON(val);
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
function ensure_evaluated(args, kwargs) {
|
||||
for (var i=0; i<args.length; ++i) {
|
||||
args[i] = eval_arg(args[i]);
|
||||
}
|
||||
for (var k in kwargs) {
|
||||
if (!kwargs.hasOwnProperty(k)) { continue; }
|
||||
kwargs[k] = eval_arg(kwargs[k]);
|
||||
}
|
||||
}
|
||||
/** End of unmodified methods copied from pyeval **/
|
||||
|
||||
// We need to override the original method to be able to call our
|
||||
// Specialized version of pyeval for domain fields
|
||||
function eval_arg (arg) {
|
||||
if (typeof arg !== 'object' || !arg.__ref) {
|
||||
return arg;
|
||||
}
|
||||
switch (arg.__ref) {
|
||||
case 'domain': case 'compound_domain':
|
||||
return domain_field_pyeval('domains', [arg]);
|
||||
case 'context': case 'compound_context':
|
||||
return original_pyeval('contexts', [arg]);
|
||||
default:
|
||||
throw new Error(_t("Unknown nonliteral type ") + ' ' + arg.__ref);
|
||||
}
|
||||
}
|
||||
|
||||
// Override eval_domains to add 3 lines in order to be able to use a field
|
||||
// value as domain
|
||||
function eval_domains(domains, evaluation_context) {
|
||||
evaluation_context = _.extend(py_utils.context(), evaluation_context || {});
|
||||
var result_domain = [];
|
||||
// Normalize only if the first domain is the array ["|"] or ["!"]
|
||||
var need_normalization = (
|
||||
domains &&
|
||||
domains.length > 0 &&
|
||||
domains[0].length === 1 &&
|
||||
(domains[0][0] === "|" || domains[0][0] === "!")
|
||||
);
|
||||
_(domains).each(function (domain) {
|
||||
if (_.isString(domain)) {
|
||||
// Modified part or the original method
|
||||
if (domain in evaluation_context) {
|
||||
result_domain.push.apply(
|
||||
result_domain, $.parseJSON(evaluation_context[domain]));
|
||||
return;
|
||||
}
|
||||
// End of modifications
|
||||
|
||||
// wrap raw strings in domain
|
||||
domain = { __ref: 'domain', __debug: domain };
|
||||
}
|
||||
var domain_array_to_combine;
|
||||
switch(domain.__ref) {
|
||||
case 'domain':
|
||||
evaluation_context.context = evaluation_context;
|
||||
domain_array_to_combine = py.eval(domain.__debug, wrap_context(evaluation_context));
|
||||
break;
|
||||
default:
|
||||
domain_array_to_combine = domain;
|
||||
}
|
||||
if (need_normalization) {
|
||||
domain_array_to_combine = get_normalized_domain(domain_array_to_combine);
|
||||
}
|
||||
result_domain.push.apply(result_domain, domain_array_to_combine);
|
||||
});
|
||||
return result_domain;
|
||||
}
|
||||
|
||||
|
||||
// Override pyeval in order to call our specialized implementation of
|
||||
// eval_domains
|
||||
function domain_field_pyeval (type, object, context, options) {
|
||||
switch (type) {
|
||||
case 'domain':
|
||||
case 'domains':
|
||||
if (type === 'domain') {
|
||||
object = [object];
|
||||
}
|
||||
return eval_domains(object, context);
|
||||
default:
|
||||
return original_pyeval(type, object, context, options);
|
||||
}
|
||||
}
|
||||
|
||||
function eval_domains_and_contexts(source) {
|
||||
// see Session.eval_context in Python
|
||||
return {
|
||||
context: domain_field_pyeval('contexts', source.contexts || [], source.eval_context),
|
||||
domain: domain_field_pyeval('domains', source.domains, source.eval_context),
|
||||
group_by: domain_field_pyeval('groupbys', source.group_by_seq || [], source.eval_context),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
py_utils.eval = domain_field_pyeval;
|
||||
py_utils.ensure_evaluated = ensure_evaluated;
|
||||
py_utils.eval_domains_and_contexts = eval_domains_and_contexts;
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<template
|
||||
id="assets_backend"
|
||||
name="web_domain_field assets"
|
||||
inherit_id="web.assets_backend"
|
||||
>
|
||||
<xpath expr="script[last()]" position="after">
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="/web_domain_field/static/lib/js/pyeval.js"
|
||||
/>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
====================
|
||||
Customized List View
|
||||
====================
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:3a84bbcf7504991f8ea35e9c11270c99debf913dad0f53c5bc0c402e10372871
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--ux-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/server-ux/tree/14.0/customized_list_view
|
||||
:alt: OCA/server-ux
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/server-ux-14-0/server-ux-14-0-customized_list_view
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-ux&target_branch=14.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
Using this module you can add new fields into list views without having deep odoo
|
||||
technical knowledge.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
* Enable debug mode.
|
||||
* Go to Settings.
|
||||
* Click Customized List Views in the top menu.
|
||||
* Create new record.
|
||||
* Select model and list view.
|
||||
* Create new line. Select which field you want to add, before or after each field.
|
||||
* Select widget and optional if required. Optional can be 'show' or 'hide'.
|
||||
* Save record and click Apply.
|
||||
* Open list view you modified. You will see new field there.
|
||||
* You can click Restore Original to restore original view.
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-ux/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/server-ux/issues/new?body=module:%20customized_list_view%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
* Ilyas
|
||||
* Ooops
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* `Ooops404 <https://www.ooops404.com>`__:
|
||||
|
||||
* Ilyas <irazor147@gmail.com>
|
||||
|
||||
Maintainers
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
.. |maintainer-ilyasProgrammer| image:: https://github.com/ilyasProgrammer.png?size=40px
|
||||
:target: https://github.com/ilyasProgrammer
|
||||
:alt: ilyasProgrammer
|
||||
|
||||
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|
||||
|
||||
|maintainer-ilyasProgrammer|
|
||||
|
||||
This module is part of the `OCA/server-ux <https://github.com/OCA/server-ux/tree/14.0/customized_list_view>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
from . import models
|
||||
from .hooks import uninstall_hook
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "Customized List View",
|
||||
"summary": "Add fields into list view",
|
||||
"version": "14.0.1.0.0",
|
||||
"category": "Usability",
|
||||
"website": "https://github.com/OCA/server-ux",
|
||||
"author": "Ilyas, Ooops, Odoo Community Association (OCA)",
|
||||
"maintainers": ["ilyasProgrammer"],
|
||||
"data": ["views/views.xml", "security/ir.model.access.csv"],
|
||||
"depends": ["web_domain_field"],
|
||||
"license": "AGPL-3",
|
||||
"installable": True,
|
||||
"uninstall_hook": "uninstall_hook",
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import SUPERUSER_ID, api
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
# Restore all views
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
customizations = env["custom.list.view"].with_context(active_test=False).search([])
|
||||
for cust in customizations:
|
||||
cust.button_roll_back()
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * web_tree_customized_field_list
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 14.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2024-05-03 19:36+0000\n"
|
||||
"Last-Translator: Francesco Foresti <francesco.foresti@ooops404.com>\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.17\n"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__active
|
||||
msgid "Active"
|
||||
msgstr "Attivo"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model_terms:ir.ui.view,arch_db:web_tree_customized_field_list.view_custom_list_view_form
|
||||
msgid "Apply"
|
||||
msgstr "Applica"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__create_uid
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Creato da"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__create_date
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Creato il"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model,name:web_tree_customized_field_list.model_custom_list_view
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__custom_list_view_id
|
||||
msgid "Custom List View"
|
||||
msgstr "Vista lista personalizzata"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model,name:web_tree_customized_field_list.model_custom_list_view_line
|
||||
msgid "Custom List View Line"
|
||||
msgstr "Riga lista vista personalizzata"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model_terms:ir.ui.view,arch_db:web_tree_customized_field_list.view_custom_list_view_form
|
||||
msgid "Customized List View"
|
||||
msgstr "Vista lista personalizzata"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.actions.act_window,name:web_tree_customized_field_list.action_custom_list_view_tree
|
||||
#: model:ir.ui.menu,name:web_tree_customized_field_list.menu_custom_list_view_tree
|
||||
msgid "Customized List Views"
|
||||
msgstr "Viste lista personalizzate"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__display_name
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__display_name
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_module_module__display_name
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_ui_view__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Nome visualizzato"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__fields_domain
|
||||
msgid "Fields Domain"
|
||||
msgstr "Dominio campi"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_module_module__id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_ui_view__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__label
|
||||
msgid "Label"
|
||||
msgstr "Etichetta"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__archive_uid
|
||||
msgid "Last Archived by"
|
||||
msgstr "Ultima archiviazione di"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__archive_date
|
||||
msgid "Last Archived on"
|
||||
msgstr "Ultima archiviazione il"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view____last_update
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line____last_update
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_module_module____last_update
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_ui_view____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr "Ultima modifica il"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__write_uid
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Ultimo aggiornamento di"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__write_date
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Ultimo aggiornamento il"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__line_ids
|
||||
msgid "Line"
|
||||
msgstr "Riga"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__list_view_id
|
||||
msgid "List View"
|
||||
msgstr "Vista lista"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__model_id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__model_name
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__model_id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__model_name
|
||||
msgid "Model"
|
||||
msgstr "Modello"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model,name:web_tree_customized_field_list.model_ir_module_module
|
||||
msgid "Module"
|
||||
msgstr "Modulo"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__name
|
||||
msgid "Name"
|
||||
msgstr "Nome"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__field_id
|
||||
msgid "New Field"
|
||||
msgstr "Nuovo campo"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.constraint,message:web_tree_customized_field_list.constraint_custom_list_view_unique_model_list_view_rec
|
||||
msgid ""
|
||||
"Only one record per view is allowed. Please modify existing record instead."
|
||||
msgstr ""
|
||||
"Solo un record per vista è permesso. Modifica invece il record esistente."
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__optional
|
||||
msgid "Optional"
|
||||
msgstr "Opzionale"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__original_arch
|
||||
msgid "Original Arch"
|
||||
msgstr "Arch originale"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__after
|
||||
msgid "Place After"
|
||||
msgstr "Inserisci dopo di"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__before
|
||||
msgid "Place Before"
|
||||
msgstr "Inserisci prima di"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model_terms:ir.ui.view,arch_db:web_tree_customized_field_list.view_custom_list_view_form
|
||||
msgid "Restore Original"
|
||||
msgstr "Ripristina originale"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model,name:web_tree_customized_field_list.model_ir_ui_view
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__use_widget
|
||||
msgid "Widget"
|
||||
msgstr "Widget"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__use_widget__color
|
||||
msgid "color"
|
||||
msgstr "color"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__optional__hide
|
||||
msgid "hide"
|
||||
msgstr "hide"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__use_widget__many2many_tags
|
||||
msgid "many2many_tags"
|
||||
msgstr "many2many_tags"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__use_widget__monetary
|
||||
msgid "monetary"
|
||||
msgstr "monetary"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__optional__show
|
||||
msgid "show"
|
||||
msgstr "show"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
from . import custom_list
|
||||
from . import customer_list_view_line
|
||||
from . import ir_ui_view
|
||||
from . import ir_module
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class CustomListView(models.Model):
|
||||
_name = "custom.list.view"
|
||||
_description = "Custom List View"
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
name = fields.Char(required=True)
|
||||
model_id = fields.Many2one("ir.model", required=True, ondelete="cascade")
|
||||
model_name = fields.Char(related="model_id.model", store=True)
|
||||
list_view_id = fields.Many2one("ir.ui.view", required=True)
|
||||
line_ids = fields.One2many("custom.list.view.line", "custom_list_view_id")
|
||||
original_arch = fields.Text(readonly=True)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
"unique_model_list_view_rec",
|
||||
"UNIQUE(list_view_id)",
|
||||
"Only one record per view is allowed. "
|
||||
"Please modify existing record instead.",
|
||||
)
|
||||
]
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
recs = super().create(vals_list)
|
||||
for rec in recs:
|
||||
rec.original_arch = rec.list_view_id.arch
|
||||
return recs
|
||||
|
||||
def button_apply_changes(self):
|
||||
self.ensure_one()
|
||||
doc = etree.XML(self.original_arch)
|
||||
for mod_line in self.line_ids:
|
||||
target = mod_line.before and mod_line.before.name or mod_line.after.name
|
||||
for node in doc.xpath("/tree/field[@name='%s']" % target):
|
||||
node_string = (
|
||||
"<field name='%s' widget='%s' optional='%s' string='%s'/>"
|
||||
% (
|
||||
mod_line.field_id.name,
|
||||
mod_line.use_widget or "",
|
||||
mod_line.optional or "",
|
||||
mod_line.label or mod_line.field_id.field_description,
|
||||
)
|
||||
)
|
||||
new_node = etree.fromstring(node_string)
|
||||
if mod_line.before:
|
||||
node.addprevious(new_node)
|
||||
else:
|
||||
node.addnext(new_node)
|
||||
new_arch = etree.tostring(doc, encoding="unicode").replace("\t", "")
|
||||
self.list_view_id.arch = new_arch
|
||||
|
||||
def button_roll_back(self):
|
||||
self.ensure_one()
|
||||
doc = etree.XML(self.original_arch)
|
||||
new_arch = etree.tostring(doc, encoding="unicode").replace("\t", "")
|
||||
self.list_view_id.arch = new_arch
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
import json
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class CustomListViewLine(models.Model):
|
||||
_name = "custom.list.view.line"
|
||||
_description = "Custom List View Line"
|
||||
|
||||
custom_list_view_id = fields.Many2one("custom.list.view")
|
||||
model_id = fields.Many2one(related="custom_list_view_id.model_id")
|
||||
model_name = fields.Char(related="custom_list_view_id.model_name")
|
||||
field_id = fields.Many2one("ir.model.fields", "New Field")
|
||||
after = fields.Many2one("ir.model.fields", "Place After")
|
||||
before = fields.Many2one("ir.model.fields", "Place Before")
|
||||
optional = fields.Selection([("show", "show"), ("hide", "hide")])
|
||||
label = fields.Char()
|
||||
use_widget = fields.Selection(
|
||||
[
|
||||
("many2many_tags", "many2many_tags"),
|
||||
("color", "color"),
|
||||
("monetary", "monetary"),
|
||||
],
|
||||
string="Widget",
|
||||
default=False,
|
||||
)
|
||||
fields_domain = fields.Char(
|
||||
compute="_compute_fields_domain",
|
||||
readonly=True,
|
||||
store=False,
|
||||
)
|
||||
|
||||
@api.depends("custom_list_view_id.list_view_id")
|
||||
def _compute_fields_domain(self):
|
||||
for rec in self:
|
||||
arch = (
|
||||
rec.custom_list_view_id.original_arch
|
||||
or rec.custom_list_view_id.list_view_id.arch
|
||||
)
|
||||
doc = etree.XML(arch)
|
||||
nodes = doc.xpath("//tree//field")
|
||||
field_names = []
|
||||
for item in nodes:
|
||||
field_names.append(item.attrib["name"])
|
||||
field_ids = self.env["ir.model.fields"].search(
|
||||
[
|
||||
("model", "=", rec.custom_list_view_id.model_name),
|
||||
("name", "in", field_names),
|
||||
]
|
||||
)
|
||||
rec.fields_domain = json.dumps([("id", "in", field_ids.ids)])
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import models
|
||||
|
||||
|
||||
class Module(models.Model):
|
||||
_inherit = "ir.module.module"
|
||||
|
||||
def _button_immediate_function(self, function):
|
||||
res = super(Module, self)._button_immediate_function(function)
|
||||
clv_model = self.env["ir.model"]._get("custom.list.view")
|
||||
if not clv_model:
|
||||
# case when customized_list_view was uninstalled
|
||||
# views will be restored in the uninstall_hook
|
||||
return res
|
||||
line_mods = self.env["custom.list.view.line"].search([("field_id", "=", False)])
|
||||
if line_mods:
|
||||
# fields was deleted during modules operation
|
||||
custom_views = line_mods.mapped("custom_list_view_id")
|
||||
line_mods.unlink()
|
||||
custom_views.button_apply_changes()
|
||||
return res
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import models
|
||||
|
||||
|
||||
class IrUiView(models.Model):
|
||||
_inherit = "ir.ui.view"
|
||||
|
||||
def name_get(self):
|
||||
return [(rec.id, rec.xml_id) for rec in self]
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
* `Ooops404 <https://www.ooops404.com>`__:
|
||||
|
||||
* Ilyas <irazor147@gmail.com>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Using this module you can add new fields into list views without having deep odoo
|
||||
technical knowledge.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
* Enable debug mode.
|
||||
* Go to Settings.
|
||||
* Click Customized List Views in the top menu.
|
||||
* Create new record.
|
||||
* Select model and list view.
|
||||
* Create new line. Select which field you want to add, before or after each field.
|
||||
* Select widget and optional if required. Optional can be 'show' or 'hide'.
|
||||
* Save record and click Apply.
|
||||
* Open list view you modified. You will see new field there.
|
||||
* You can click Restore Original to restore original view.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
clw1,clw1,model_custom_list_view,base.group_system,1,1,1,1
|
||||
clwl1,clwl1,model_custom_list_view_line,base.group_system,1,1,1,1
|
||||
|
|
After Width: | Height: | Size: 9.2 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from . import test_customized_list_view
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestCustomizedListView(common.SavepointCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestCustomizedListView, cls).setUpClass()
|
||||
cls.view_model = cls.env["ir.ui.view"].sudo()
|
||||
cls.users_model = cls.env["ir.model"]._get("res.users")
|
||||
cls.view_users_tree = cls.env.ref("base.view_users_tree")
|
||||
|
||||
def test_all_customized_list_view(self):
|
||||
new_view = self.view_users_tree.copy()
|
||||
# Lets create some custom.list.view records
|
||||
login_field = self.env["ir.model.fields"].search(
|
||||
[
|
||||
("model_id", "=", self.users_model.id),
|
||||
("name", "=", "login"),
|
||||
]
|
||||
)
|
||||
date_field = self.env["ir.model.fields"].search(
|
||||
[
|
||||
("model_id", "=", self.users_model.id),
|
||||
("name", "=", "create_date"),
|
||||
]
|
||||
)
|
||||
write_field = self.env["ir.model.fields"].search(
|
||||
[
|
||||
("model_id", "=", self.users_model.id),
|
||||
("name", "=", "write_date"),
|
||||
]
|
||||
)
|
||||
custom = self.env["custom.list.view"].create(
|
||||
{
|
||||
"name": "Test View Mod",
|
||||
"model_id": self.users_model.id,
|
||||
"list_view_id": self.view_users_tree.id,
|
||||
}
|
||||
)
|
||||
self.env["custom.list.view.line"].create(
|
||||
[
|
||||
{
|
||||
"custom_list_view_id": custom.id,
|
||||
"field_id": date_field.id,
|
||||
"after": login_field.id,
|
||||
"label": "Custom Label 1",
|
||||
},
|
||||
{
|
||||
"custom_list_view_id": custom.id,
|
||||
"field_id": date_field.id,
|
||||
"before": write_field.id,
|
||||
"label": "Custom Label 2",
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
# New field should not be in the view before button_apply_changes is clicked
|
||||
users_form = self.view_users_tree.arch
|
||||
self.assertNotIn(
|
||||
"create_date", users_form, msg="create_date should not be in the view."
|
||||
)
|
||||
|
||||
# Apply changes. Now new field should be there
|
||||
custom.button_apply_changes()
|
||||
users_form = self.view_users_tree.arch
|
||||
self.assertIn(
|
||||
"create_date", users_form, msg="create_date should be in the view."
|
||||
)
|
||||
|
||||
# Try to roll back. New field should not be in the view.
|
||||
custom.button_roll_back()
|
||||
users_form = self.view_users_tree.arch
|
||||
self.assertNotIn(
|
||||
"create_date", users_form, msg="Roll back feature does not work."
|
||||
)
|
||||
|
||||
# Trigger _compute_fields_domain with read(). No domain there.
|
||||
custom.list_view_id = new_view
|
||||
fields_domain = custom.line_ids[0].read(["fields_domain"])
|
||||
self.assertIsNotNone(fields_domain, msg="fields_domain should be empty.")
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_custom_list_view_form" model="ir.ui.view">
|
||||
<field name="name">custom.list.view.form.view</field>
|
||||
<field name="model">custom.list.view</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Customized List View">
|
||||
<header>
|
||||
<button
|
||||
name="button_apply_changes"
|
||||
type="object"
|
||||
string="Apply"
|
||||
groups="base.group_system"
|
||||
class="btn-primary"
|
||||
/>
|
||||
<button
|
||||
name="button_roll_back"
|
||||
type="object"
|
||||
string="Restore Original"
|
||||
groups="base.group_system"
|
||||
class="btn-secondary"
|
||||
/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" />
|
||||
<field name="model_id" />
|
||||
<field name="model_name" />
|
||||
<field
|
||||
name="list_view_id"
|
||||
domain="[('model', '=', model_name), ('type', '=', 'tree'), ('mode', '=', 'primary')]"
|
||||
attrs="{'readonly': [('model_name', '=', False)]}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="line_ids">
|
||||
<tree editable="top">
|
||||
<field name="model_id" invisible="1" />
|
||||
<field name="custom_list_view_id" invisible="1" />
|
||||
<field name="model_name" />
|
||||
<field
|
||||
name="field_id"
|
||||
domain="[('model_id', '=', model_id)]"
|
||||
/>
|
||||
<field name="label" />
|
||||
<field name="use_widget" />
|
||||
<field name="optional" />
|
||||
<field name="fields_domain" invisible="1" />
|
||||
<field
|
||||
name="before"
|
||||
attrs="{'required': [('after', '=', False)],
|
||||
'readonly': [('after', '!=', False)]}"
|
||||
domain="fields_domain"
|
||||
/>
|
||||
<field
|
||||
name="after"
|
||||
domain="fields_domain"
|
||||
attrs="{'required': [('before', '=', False)],
|
||||
'readonly': [('before', '!=', False)]}"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_custom_list_view_tree" model="ir.ui.view">
|
||||
<field name="name">custom.list.view.tree.view</field>
|
||||
<field name="model">custom.list.view</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="model_id" />
|
||||
<field name="list_view_id" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_custom_list_view_tree" model="ir.actions.act_window">
|
||||
<field name="name">Customized List Views</field>
|
||||
<field name="res_model">custom.list.view</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="target">current</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_custom_list_view_tree"
|
||||
name="Customized List Views"
|
||||
action="action_custom_list_view_tree"
|
||||
parent="base.menu_administration"
|
||||
sequence="20"
|
||||
/>
|
||||
|
||||
</odoo>
|
||||