diff --git a/odex25_base/custom_background/COPYRIGHT b/odex25_base/custom_background/COPYRIGHT new file mode 100644 index 000000000..d1ddb51af --- /dev/null +++ b/odex25_base/custom_background/COPYRIGHT @@ -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. + diff --git a/odex25_base/custom_background/LICENSE b/odex25_base/custom_background/LICENSE new file mode 100644 index 000000000..89cbe4b69 --- /dev/null +++ b/odex25_base/custom_background/LICENSE @@ -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. + diff --git a/odex25_base/custom_background/__init__.py b/odex25_base/custom_background/__init__.py new file mode 100644 index 000000000..09949f017 --- /dev/null +++ b/odex25_base/custom_background/__init__.py @@ -0,0 +1,2 @@ +# See LICENSE file for full copyright and licensing details. +from . import models diff --git a/odex25_base/custom_background/__manifest__.py b/odex25_base/custom_background/__manifest__.py new file mode 100644 index 000000000..e7217e6b9 --- /dev/null +++ b/odex25_base/custom_background/__manifest__.py @@ -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", +} diff --git a/odex25_base/custom_background/i18n/nl.po b/odex25_base/custom_background/i18n/nl.po new file mode 100644 index 000000000..728ace3b0 --- /dev/null +++ b/odex25_base/custom_background/i18n/nl.po @@ -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 \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 "" +"Expression:\n" +" If the option \"Expression\" is selected you " +"can\n" +" set custom background PDF's based on\n" +" expressions.\n" +"
\n" +" For example if you want only a background on\n" +" every second page you could do:\n" +"
\n" +" result=page%2==0\n" +"
\n" +" If you want only a background on every uneven\n" +" page (for example page 1,3 ,5) you could do:\n" +"
\n" +" result=page%2!=0\n" +"
\n" +" Or if you only want a background starting from\n" +" page 2 on for example you could do:\n" +"
\n" +" result=page>2" +msgstr "" +"Uitdrukking:\n" +" Als de optie “Expressie” is geselecteerd, kunt " +"u\n" +" Een aangepaste achtergrond-PDF’s instellen op " +"basis van\n" +" expressies.\n" +"
\n" +" Als u bijvoorbeeld alleen een achtergrond op\n" +" elke tweede pagina wil, dan kunt u bijvoorbeeld " +"dit doen:\n" +"
\n" +" result=page%2==0\n" +"
\n" +" Als u alleen een achtergrond wilt op elke " +"oneven\n" +" pagina (bijvoorbeeld pagina 1,3 ,5) kunt u " +"doen:\n" +"
\n" +" result=page%2!=0\n" +"
\n" +" Of als u alleen een achtergrond wilt vanaf\n" +" pagina 2, dan kunt u bijvoorbeeld dit doen:\n" +"
\n" +" result=page>2" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "" +"First Page:\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" +"
\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 "" +"Eerste pagina:\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" +"
\n" +" Als er geen “Achtergrond PDF” is ingesteld op " +"de “Eerste\n" +" Pagina” regel dan zal het de achtergrond PDF " +"instellen\n" +" van de “Vaste pagina”, “Expressie pagina” of\n" +" “Overige pagina’s”." + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "" +"Fixed Pages:\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" +"
\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 "" +"Vaste pagina’s:\n" +" Als de optie “Vaste pagina’s” is geselecteerd, " +"kunt u\n" +" Instellen voor welke pagina u de\n" +" “Achtergrond-PDF” wilt gebruiken.\n" +" Bijvoorbeeld: een aangepaste achtergrond-PDF " +"instellen voor\n" +" de derde pagina van een rapport door “3” in te " +"voeren in\n" +" het veld “Paginanummer”.\n" +"
\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 "" +"Last Page:\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" +"
\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" +"
" +msgstr "" +"Laatste pagina:\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" +"
\n" +" Als er geen “Achtergrond PDF” was ingesteld op " +"de laatste\n" +" pagina, dan zal het de achtergrond-PDF " +"instellen van\n" +" de “Vaste pagina”, “Expressie pagina” of\n" +" “Overige pagina’s”.\n" +"
" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "" +"Remaining Pages:\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 "" +"Overige pagina’s:\n" +" Als de optie “Overige pagina’s” is " +"geselecteerd, wordt de\n" +" “Achtergrond PDF” toegepast op\n" +" deze regel voor alle pagina’s die geen regel " +"hebben.\n" +" U kunt dit bijvoorbeeld gebruiken als u een\n" +" regel “Eerste pagina” instelt en dan een " +"tweede\n" +" regel “Overige pagina’s”. Het zou dan de\n" +" achtergrond op instellen op pagina 2, 3, .. en " +"elke andere pagina\n" +" Die u niet heeft geconfigureerd." + + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__background_ids +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "Background Configuration" +msgstr "Achtergrondconfiguratie" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "Background Configuration Per Language" +msgstr "Achtergrondconfiguratie per taal" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__custom_report_background_image +msgid "Background Image" +msgstr "Achtergrondafbeelding" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__background_pdf +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__background_pdf +#: model_terms:ir.ui.view,arch_db:custom_background.res_company_view_inherit +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "Background PDF" +msgstr "Achtergrond PDF" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__bg_per_lang_ids +#: model:ir.model.fields,field_description:custom_background.field_res_company__bg_per_lang_ids +msgid "Background Per Language" +msgstr "Achtergrond per taal" + +#. module: custom_background +#: model:ir.model,name:custom_background.model_res_company +msgid "Companies" +msgstr "Bedrijven" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__company_id +msgid "Company" +msgstr "Bedrijf" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.res_company_view_inherit +msgid "Configuration" +msgstr "Instellingen" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__create_uid +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__create_uid +msgid "Created by" +msgstr "Aangemaakt door" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__create_date +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__create_date +msgid "Created on" +msgstr "Aangemaakt op" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.res_company_view_inherit +msgid "Custom Background Image" +msgstr "Achtergrondafbeelding" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.res_company_view_inherit +msgid "Custom Background Image Per Language" +msgstr "Aangepaste achtergrondafbeelding per taal" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__custom_report_background +#: model:ir.model.fields,field_description:custom_background.field_res_company__custom_report_background_image +msgid "Custom Report Background" +msgstr "Achtergrondafbeelding layouts" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__custom_report_type +msgid "Custom Report Type" +msgstr "Afwijkende achtergrond" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "Detailed algorithm (with their priorities):" +msgstr "Gedetailleerd algoritme (met hun prioriteiten):" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__display_name +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__display_name +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__display_name +#: model:ir.model.fields,field_description:custom_background.field_res_company__display_name +msgid "Display Name" +msgstr "Schermnaam" + +#. module: custom_background +#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__expression +msgid "Expression" +msgstr "Uitdrukking" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "Extra Note" +msgstr "Extra opmerking" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__fall_back_to_company +msgid "Fall Back To Company" +msgstr "Terugvallen op het bedrijf" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__file_name +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__file_name +msgid "File Name" +msgstr "Bestandsnaam" + +#. module: custom_background +#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__first_page +msgid "First Page" +msgstr "Eerste pagina" + +#. module: custom_background +#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__fixed +msgid "Fixed Page" +msgstr "Vaste pagina" + +#. module: custom_background +#: model:ir.model.fields.selection,name:custom_background.selection__ir_actions_report__custom_report_type__company +msgid "From Company" +msgstr "Van bedrijf" + +#. module: custom_background +#: model:ir.model.fields.selection,name:custom_background.selection__ir_actions_report__custom_report_type__dynamic +msgid "From Report Dynamic" +msgstr "Van Rapport Dynamisch" + +#. module: custom_background +#: model:ir.model.fields.selection,name:custom_background.selection__ir_actions_report__custom_report_type__report +msgid "From Report Fixed" +msgstr "Van rapport opgelost" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__id +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__id +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__id +#: model:ir.model.fields,field_description:custom_background.field_res_company__id +msgid "ID" +msgstr "ID" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "" +"If there are no \"Background Configuration\" rules\n" +" added for any line it will work as a normal Odoo\n" +" report and will not apply any background to the\n" +" report." +msgstr "" +"Als er geen regels voor de “Achtergrondconfiguratie” zijn\n" +" toegevoegd voor een regel, dan zal deze werken als " +"een normaal Odoo\n" +" rapport en wordt er geen achtergrond toegevoegd op " +"de\n" +" Rappoortage." + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "Interaction with the background configuration" +msgstr "Interactie met de achtergrondconfiguratie" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report__is_bg_per_lang +#: model:ir.model.fields,field_description:custom_background.field_res_company__is_bg_per_lang +msgid "Is Background Per Language" +msgstr "Instellen achtergrond per taal" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__lang_id +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__lang_id +msgid "Language" +msgstr "Taal" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_ir_actions_report____last_update +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang____last_update +#: model:ir.model.fields,field_description:custom_background.field_report_background_line____last_update +#: model:ir.model.fields,field_description:custom_background.field_res_company____last_update +msgid "Last Modified on" +msgstr "Laatst gewijzigd op" + +#. module: custom_background +#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__last_page +msgid "Last Page" +msgstr "Laatste pagina" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__write_uid +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__write_uid +msgid "Last Updated by" +msgstr "Laatst bijgewerkt door" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__write_date +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__write_date +msgid "Last Updated on" +msgstr "Laatst bijgewerkt op" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__page_expression +msgid "Page Expression" +msgstr "Pagina expressie" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__page_number +msgid "Page Number" +msgstr "Paginanummer" + +#. module: custom_background +#: code:addons/custom_background/models/res_company.py:0 +#, python-format +msgid "" +"Please configure Custom Background Per Language beacuse 'Fall Back To Company' is set " +"in the dynamic type report level!" +msgstr "" +"Configureer a.u.b. aangepaste achtergrond per taal omdat 'Fall Back To Bedrijf' is " +"ingesteld op het rapportniveau dynamisch type!" + +#. module: custom_background +#: code:addons/custom_background/models/res_company.py:0 +#, python-format +msgid "" +"Please configure Custom Background Per Language beacuse 'From Company' type is set at " +"the Report level!" +msgstr "" +"Configureer aangepaste achtergrond per taal vanwege het type 'Van bedrijf' is ingesteld " +"op rapportniveau!" + +#. module: custom_background +#: code:addons/custom_background/models/report.py:0 +#, python-format +msgid "Please configure Custom Background Per Language for Dynamic type!" +msgstr "Configureer aangepaste achtergrond per taal voor dynamisch type!" + +#. module: custom_background +#: code:addons/custom_background/models/report.py:0 +#, python-format +msgid "Please configure Custom Background Per Language for Report type!" +msgstr "Configureer aangepaste achtergrond per taal voor rapporttype!" + +#. module: custom_background +#: code:addons/custom_background/models/report.py:0 +#, python-format +msgid "Please configure Custom Background Per Language in every company!" +msgstr "Configureer aangepaste achtergrond per taal in elk bedrijf!" + +#. module: custom_background +#: model:ir.model.fields.selection,name:custom_background.selection__report_background_line__type__remaining +msgid "Remaining pages" +msgstr "Overige pagina's" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_lang__report_id +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__report_id +msgid "Report" +msgstr "Rapport" + +#. module: custom_background +#: model:ir.model,name:custom_background.model_ir_actions_report +msgid "Report Action" +msgstr "Rapport actie" + +#. module: custom_background +#: model:ir.model,name:custom_background.model_report_background_line +msgid "Report Background Line" +msgstr "Achtergrondregel rapporteren" + +#. module: custom_background +#: model:ir.model,name:custom_background.model_report_background_lang +msgid "Report Background Line Per Language" +msgstr "Achtergrondregel per taal rapporteren" + +#. module: custom_background +#: model:ir.model.fields,field_description:custom_background.field_report_background_line__type +msgid "Type" +msgstr "Type" + +#. module: custom_background +#: code:addons/custom_background/models/report.py:0 +#, python-format +msgid "" +"Wkhtmltopdf failed (error code: %s). Memory limit too low or maximum file number of " +"subprocess reached. Message : %s" +msgstr "" +"Wkhtmltopdf fout (fout code: %s). Geheugenlimiet te laag of maximale bestandsnummer van " +"subproces bereikt. Bericht:%s" + +#. module: custom_background +#: code:addons/custom_background/models/report.py:0 +#, python-format +msgid "Wkhtmltopdf failed (error code: %s). Message: %s" +msgstr "Wkhtmltopdf fout (error code: %s). Bericht: %s" + +#. module: custom_background +#: model_terms:ir.ui.view,arch_db:custom_background.view_ir_action_inherit_form +msgid "" +"You can set the backgroud watermark in a report based on\n" +" the options set in the \"Background Configuration\" tab." +msgstr "" +"U kunt het achtergrondwatermerk in een rapport instellen op basis\n" +"van de opties die zijn ingesteld op het tabblad “Achtergrondconfiguratie”." diff --git a/odex25_base/custom_background/images/image.png b/odex25_base/custom_background/images/image.png new file mode 100644 index 000000000..18b79a7d5 Binary files /dev/null and b/odex25_base/custom_background/images/image.png differ diff --git a/odex25_base/custom_background/models/__init__.py b/odex25_base/custom_background/models/__init__.py new file mode 100644 index 000000000..d307bab92 --- /dev/null +++ b/odex25_base/custom_background/models/__init__.py @@ -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 diff --git a/odex25_base/custom_background/models/report.py b/odex25_base/custom_background/models/report.py new file mode 100644 index 000000000..30b6992fc --- /dev/null +++ b/odex25_base/custom_background/models/report.py @@ -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 diff --git a/odex25_base/custom_background/models/report_background_lang.py b/odex25_base/custom_background/models/report_background_lang.py new file mode 100644 index 000000000..c5b3b503c --- /dev/null +++ b/odex25_base/custom_background/models/report_background_lang.py @@ -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", + ) diff --git a/odex25_base/custom_background/models/res_company.py b/odex25_base/custom_background/models/res_company.py new file mode 100644 index 000000000..6cfa61b0f --- /dev/null +++ b/odex25_base/custom_background/models/res_company.py @@ -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!" + ) + ) diff --git a/odex25_base/custom_background/security/ir.model.access.csv b/odex25_base/custom_background/security/ir.model.access.csv new file mode 100644 index 000000000..8f8b55b0b --- /dev/null +++ b/odex25_base/custom_background/security/ir.model.access.csv @@ -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 diff --git a/odex25_base/custom_background/static/description/icon.png b/odex25_base/custom_background/static/description/icon.png new file mode 100644 index 000000000..9ad73d74e Binary files /dev/null and b/odex25_base/custom_background/static/description/icon.png differ diff --git a/odex25_base/custom_background/static/description/images/BackgroundConfiguration.png b/odex25_base/custom_background/static/description/images/BackgroundConfiguration.png new file mode 100644 index 000000000..b9320ded4 Binary files /dev/null and b/odex25_base/custom_background/static/description/images/BackgroundConfiguration.png differ diff --git a/odex25_base/custom_background/static/description/images/BackgroundConfigurationSelection.png b/odex25_base/custom_background/static/description/images/BackgroundConfigurationSelection.png new file mode 100644 index 000000000..976a4f4cc Binary files /dev/null and b/odex25_base/custom_background/static/description/images/BackgroundConfigurationSelection.png differ diff --git a/odex25_base/custom_background/static/description/images/CreateBackgroundConfiguration.png b/odex25_base/custom_background/static/description/images/CreateBackgroundConfiguration.png new file mode 100644 index 000000000..782304403 Binary files /dev/null and b/odex25_base/custom_background/static/description/images/CreateBackgroundConfiguration.png differ diff --git a/odex25_base/custom_background/static/description/images/FixedBackgroundBasedOnLang.png b/odex25_base/custom_background/static/description/images/FixedBackgroundBasedOnLang.png new file mode 100644 index 000000000..e0f83306f Binary files /dev/null and b/odex25_base/custom_background/static/description/images/FixedBackgroundBasedOnLang.png differ diff --git a/odex25_base/custom_background/static/description/images/logo.png b/odex25_base/custom_background/static/description/images/logo.png new file mode 100644 index 000000000..5ea55e556 Binary files /dev/null and b/odex25_base/custom_background/static/description/images/logo.png differ diff --git a/odex25_base/custom_background/static/description/index.html b/odex25_base/custom_background/static/description/index.html new file mode 100644 index 000000000..f79c2b57f --- /dev/null +++ b/odex25_base/custom_background/static/description/index.html @@ -0,0 +1,168 @@ +
+
+
+ + + +
+
+ + Custom Background + +
+
+
+
+
+ +

+ Contact With Us +

+
+ contact@bizzappdev.com + +
+
+
+
+ +
+
+
+
Module Custom Background 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). +
+
+
+
+ +
+
+
+
+ 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. +
+
+
+
+ +
+
+
+
+

There are different options for configuration

+
    +
  • Fixed background image from Company
  • +
  • Fixed background image from Report
  • +
  • Dynamic image from report
  • +
  • Background image from report based on language
  • +
+
+
+
+
+ +
+
+
+
+ You can select custom reprot type based on your requirements +
+
+
+ +
+
+
+
+ +
+
+
+
+ You can also configure Dynamic Background in your PDF as per your requirements +
+
+
+ +
+
+
+
+ +
+
+
+
+ You can configure here Dynamic Background as per your requirements +
+
+
+ +
+
+
+
+ +
+
+
+
+ You can configure Fixed Background Based on Langauge as per your requirements +
+
+
+ +
+
+
+
+ +
+
+
+
+ Report Without Background +
+
+ +
+
+
+
+
+ Report With Background +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ + + +
+ +
+
+

Need Any Help ?

+ +

Contact With Us

+ +
+
+
+
+
+
+
diff --git a/odex25_base/custom_background/static/src/scss/report_qweb_pdf.scss b/odex25_base/custom_background/static/src/scss/report_qweb_pdf.scss new file mode 100644 index 000000000..83cc2eb02 --- /dev/null +++ b/odex25_base/custom_background/static/src/scss/report_qweb_pdf.scss @@ -0,0 +1,3 @@ +body { + background: transparent !important; +} diff --git a/odex25_base/custom_background/views/ir_actions.xml b/odex25_base/custom_background/views/ir_actions.xml new file mode 100644 index 000000000..f1e9b4628 --- /dev/null +++ b/odex25_base/custom_background/views/ir_actions.xml @@ -0,0 +1,207 @@ + + + + + ir.actions.report.form + ir.actions.report + form + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ +

Interaction with the background configuration

+
+

+ You can set the backgroud watermark in a report based on + the options set in the "Background Configuration" tab. +

+

+ Detailed algorithm (with their priorities): +

    +
  1. + First Page: + If the option "First Page" is chosen it will not + look at any other configurations for the first + page of the PDF. +
    + 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". +
  2. +
  3. + Last Page: + 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. +
    + 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". +
    +
  4. +
  5. + Fixed Pages: + 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". +
    + 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. +
  6. +
  7. + Expression: + If the option "Expression" is selected you can + set custom background PDF's based on + expressions. +
    + For example if you want only a background on + every second page you could do: +
    + result=page%2==0 +
    + If you want only a background on every uneven + page (for example page 1,3 ,5) you could do: +
    + result=page%2!=0 +
    + Or if you only want a background starting from + page 2 on for example you could do: +
    + result=page>2 +
  8. +
  9. + Remaining Pages: + 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. +
  10. +
+ Extra Note +
  • + 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. +
  • +

    +
    +
    + + + +
    + + + + + + + + + +
    + + + + +
    +
    +
    +
    +
    +
    +
    diff --git a/odex25_base/custom_background/views/report_template.xml b/odex25_base/custom_background/views/report_template.xml new file mode 100644 index 000000000..827857756 --- /dev/null +++ b/odex25_base/custom_background/views/report_template.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/odex25_base/custom_background/views/res_company_view.xml b/odex25_base/custom_background/views/res_company_view.xml new file mode 100644 index 000000000..a87ee73a5 --- /dev/null +++ b/odex25_base/custom_background/views/res_company_view.xml @@ -0,0 +1,114 @@ + + + + + res.company.form + res.company + + + + + + + + + + + + + + +
    + + + + + + + + + +
    + + + + +
    +
    + + + + +
    + + + + + + + + + + + + + + + +
    + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    diff --git a/odex25_base/web_domain_field/README.rst b/odex25_base/web_domain_field/README.rst new file mode 100644 index 000000000..bf8d31a5f --- /dev/null +++ b/odex25_base/web_domain_field/README.rst @@ -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 + + + +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 + + + + +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 `_. +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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* ACSONE SA/NV + +Contributors +~~~~~~~~~~~~ + +* Laurent Mignon +* Denis Roussel +* Raf Ven + +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 `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/odex25_base/web_domain_field/__init__.py b/odex25_base/web_domain_field/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/odex25_base/web_domain_field/__manifest__.py b/odex25_base/web_domain_field/__manifest__.py new file mode 100644 index 000000000..ad868b8e4 --- /dev/null +++ b/odex25_base/web_domain_field/__manifest__.py @@ -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, +} diff --git a/odex25_base/web_domain_field/i18n/es.po b/odex25_base/web_domain_field/i18n/es.po new file mode 100644 index 000000000..e69de29bb diff --git a/odex25_base/web_domain_field/readme/CONTRIBUTORS.rst b/odex25_base/web_domain_field/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..1294ec29e --- /dev/null +++ b/odex25_base/web_domain_field/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Laurent Mignon +* Denis Roussel +* Raf Ven diff --git a/odex25_base/web_domain_field/readme/DESCRIPTION.rst b/odex25_base/web_domain_field/readme/DESCRIPTION.rst new file mode 100644 index 000000000..5aa1b96f3 --- /dev/null +++ b/odex25_base/web_domain_field/readme/DESCRIPTION.rst @@ -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. diff --git a/odex25_base/web_domain_field/readme/USAGE.rst b/odex25_base/web_domain_field/readme/USAGE.rst new file mode 100644 index 000000000..b9c04da19 --- /dev/null +++ b/odex25_base/web_domain_field/readme/USAGE.rst @@ -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 + + + +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 + + + + +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)] + ) diff --git a/odex25_base/web_domain_field/static/description/icon.png b/odex25_base/web_domain_field/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/odex25_base/web_domain_field/static/description/icon.png differ diff --git a/odex25_base/web_domain_field/static/description/index.html b/odex25_base/web_domain_field/static/description/index.html new file mode 100644 index 000000000..8ddf0a71b --- /dev/null +++ b/odex25_base/web_domain_field/static/description/index.html @@ -0,0 +1,476 @@ + + + + + + +Web Domain Field + + + +
    +

    Web Domain Field

    + + +

    Production/Stable License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

    +

    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

    + +
    +

    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.

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

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

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

    +

    Do not contact contributors directly about support or help with technical issues.

    +
    +
    +

    Credits

    +
    +

    Authors

    +
      +
    • ACSONE SA/NV
    • +
    +
    +
    +

    Contributors

    + +
    +
    +

    Maintainers

    +

    This module is maintained by the OCA.

    +Odoo Community Association +

    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 project on GitHub.

    +

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    +
    +
    +
    + + diff --git a/odex25_base/web_domain_field/static/lib/js/pyeval.js b/odex25_base/web_domain_field/static/lib/js/pyeval.js new file mode 100644 index 000000000..6f2b9f61b --- /dev/null +++ b/odex25_base/web_domain_field/static/lib/js/pyeval.js @@ -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 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; + +}); diff --git a/odex25_base/web_domain_field/views/web_domain_field.xml b/odex25_base/web_domain_field/views/web_domain_field.xml new file mode 100644 index 000000000..7102c526b --- /dev/null +++ b/odex25_base/web_domain_field/views/web_domain_field.xml @@ -0,0 +1,15 @@ + + +