add 2new models

This commit is contained in:
esraa 2024-12-05 14:15:54 +02:00
parent c274617c1f
commit b1af7679ad
52 changed files with 3496 additions and 0 deletions

View File

@ -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.

View 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.

View File

@ -0,0 +1,2 @@
# See LICENSE file for full copyright and licensing details.
from . import models

View File

@ -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",
}

View File

@ -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&gt;2"
msgstr ""
"<b>Uitdrukking:</b>\n"
" Als de optie “Expressie” is geselecteerd, kunt "
"u\n"
" Een aangepaste achtergrond-PDFs 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&gt;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 paginas”."
#. 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 paginas:</b>\n"
" Als de optie “Vaste paginas” 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 paginas”.\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 paginas:</b>\n"
" Als de optie “Overige paginas” is "
"geselecteerd, wordt de\n"
" “Achtergrond PDF” toegepast op\n"
" deze regel voor alle paginas 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 paginas”. 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”."

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@ -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

View File

@ -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

View File

@ -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",
)

View File

@ -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!"
)
)

View File

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_report_background_line access_report_background_line model_report_background_line base.group_user 1 0 0 0
3 access_report_background_lang_user access_report_background_lang model_report_background_lang base.group_user 1 0 0 0
4 access_report_background_line_system access_report_background_line_system model_report_background_line base.group_system 1 1 1 1
5 access_report_background_lang_admin access_report_background_lang model_report_background_lang base.group_system 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -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>

View File

@ -0,0 +1,3 @@
body {
background: transparent !important;
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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.

View File

View File

@ -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,
}

View File

View File

@ -0,0 +1,3 @@
* Laurent Mignon <laurent.mignon@acsone.eu>
* Denis Roussel <denis.roussel@acsone.eu>
* Raf Ven <raf.ven@dynapps.be>

View File

@ -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.

View File

@ -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)]
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -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&amp;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">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;product_id&quot;</span><span class="w"> </span><span class="na">domain=</span><span class="s">&quot;[('type','=','product')]&quot;</span><span class="nt">/&gt;</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 its 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 dont modify the field triggering the onchange.</li>
<li>Its 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">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;product_id_domain&quot;</span><span class="w"> </span><span class="na">invisible=</span><span class="s">&quot;1&quot;</span><span class="nt">/&gt;</span><span class="w">
</span><span class="nt">&lt;field</span><span class="w"> </span><span class="na">name=</span><span class="s">&quot;product_id&quot;</span><span class="w"> </span><span class="na">domain=</span><span class="s">&quot;product_id_domain&quot;</span><span class="nt">/&gt;</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">&quot;_compute_product_id_domain&quot;</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">&#64;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 &lt;<a class="reference external" href="mailto:laurent.mignon&#64;acsone.eu">laurent.mignon&#64;acsone.eu</a>&gt;</li>
<li>Denis Roussel &lt;<a class="reference external" href="mailto:denis.roussel&#64;acsone.eu">denis.roussel&#64;acsone.eu</a>&gt;</li>
<li>Raf Ven &lt;<a class="reference external" href="mailto:raf.ven&#64;dynapps.be">raf.ven&#64;dynapps.be</a>&gt;</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>

View File

@ -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;
});

View File

@ -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>

View File

@ -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.

View File

@ -0,0 +1,2 @@
from . import models
from .hooks import uninstall_hook

View File

@ -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",
}

View File

@ -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()

View File

@ -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"

View File

@ -0,0 +1,4 @@
from . import custom_list
from . import customer_list_view_line
from . import ir_ui_view
from . import ir_module

View File

@ -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

View File

@ -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)])

View File

@ -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

View File

@ -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]

View File

@ -0,0 +1,3 @@
* `Ooops404 <https://www.ooops404.com>`__:
* Ilyas <irazor147@gmail.com>

View File

@ -0,0 +1,2 @@
Using this module you can add new fields into list views without having deep odoo
technical knowledge.

View File

@ -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.

View File

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 clw1 clw1 model_custom_list_view base.group_system 1 1 1 1
3 clwl1 clwl1 model_custom_list_view_line base.group_system 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_customized_list_view

View File

@ -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.")

View File

@ -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>