From 0bd839caa31726165fc3048753e480b56b091910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Sainl=C3=A9ger?= Date: Thu, 19 May 2022 12:21:24 +0200 Subject: [PATCH] [NEW] Addons account_budget_forecast creation --- account_budget_forecast/__init__.py | 3 + account_budget_forecast/__manifest__.py | 100 ++++ .../i18n/account_budget_forecast.pot | 445 ++++++++++++++++++ account_budget_forecast/i18n/fr.po | 445 ++++++++++++++++++ account_budget_forecast/models/__init__.py | 11 + .../models/account_analytic_account.py | 271 +++++++++++ .../models/account_analytic_line.py | 19 + .../models/account_move.py | 14 + .../models/budget_coefficient.py | 22 + .../models/budget_forecast.py | 388 +++++++++++++++ account_budget_forecast/models/crm_lead.py | 30 ++ account_budget_forecast/models/product.py | 17 + account_budget_forecast/models/project.py | 24 + account_budget_forecast/models/sale_order.py | 30 ++ .../security/ir.model.access.csv | 4 + ...ection_category_and_note_fields_backend.js | 217 +++++++++ .../section_category_and_note_backend.scss | 22 + .../views/account_analytic_account.xml | 102 ++++ .../account_analytic_account_categories.xml | 148 ++++++ .../views/account_invoice.xml | 13 + account_budget_forecast/views/actions.xml | 8 + account_budget_forecast/views/assets.xml | 9 + .../views/budget_coefficient.xml | 18 + .../views/budget_coefficient_model.xml | 14 + .../views/budget_forecast.xml | 98 ++++ account_budget_forecast/views/crm_lead.xml | 18 + account_budget_forecast/views/hr_employee.xml | 16 + account_budget_forecast/views/menu.xml | 8 + .../views/product_template_form.xml | 13 + account_budget_forecast/views/sale_order.xml | 17 + 30 files changed, 2544 insertions(+) create mode 100644 account_budget_forecast/__init__.py create mode 100644 account_budget_forecast/__manifest__.py create mode 100644 account_budget_forecast/i18n/account_budget_forecast.pot create mode 100644 account_budget_forecast/i18n/fr.po create mode 100644 account_budget_forecast/models/__init__.py create mode 100644 account_budget_forecast/models/account_analytic_account.py create mode 100644 account_budget_forecast/models/account_analytic_line.py create mode 100644 account_budget_forecast/models/account_move.py create mode 100644 account_budget_forecast/models/budget_coefficient.py create mode 100644 account_budget_forecast/models/budget_forecast.py create mode 100644 account_budget_forecast/models/crm_lead.py create mode 100644 account_budget_forecast/models/product.py create mode 100644 account_budget_forecast/models/project.py create mode 100644 account_budget_forecast/models/sale_order.py create mode 100644 account_budget_forecast/security/ir.model.access.csv create mode 100644 account_budget_forecast/static/src/js/section_category_and_note_fields_backend.js create mode 100644 account_budget_forecast/static/src/scss/section_category_and_note_backend.scss create mode 100644 account_budget_forecast/views/account_analytic_account.xml create mode 100644 account_budget_forecast/views/account_analytic_account_categories.xml create mode 100644 account_budget_forecast/views/account_invoice.xml create mode 100644 account_budget_forecast/views/actions.xml create mode 100644 account_budget_forecast/views/assets.xml create mode 100644 account_budget_forecast/views/budget_coefficient.xml create mode 100644 account_budget_forecast/views/budget_coefficient_model.xml create mode 100644 account_budget_forecast/views/budget_forecast.xml create mode 100644 account_budget_forecast/views/crm_lead.xml create mode 100644 account_budget_forecast/views/hr_employee.xml create mode 100644 account_budget_forecast/views/menu.xml create mode 100644 account_budget_forecast/views/product_template_form.xml create mode 100644 account_budget_forecast/views/sale_order.xml diff --git a/account_budget_forecast/__init__.py b/account_budget_forecast/__init__.py new file mode 100644 index 0000000..cde864b --- /dev/null +++ b/account_budget_forecast/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/account_budget_forecast/__manifest__.py b/account_budget_forecast/__manifest__.py new file mode 100644 index 0000000..e8034dd --- /dev/null +++ b/account_budget_forecast/__manifest__.py @@ -0,0 +1,100 @@ +# Copyright 2021 Elabore () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "account_budget_forecast", + "version": "13.0.1.0.0", + "author": "Elabore", + "maintainer": "False", + "website": "False", + "license": "AGPL-3", + "category": "False", + "summary": "Project Forcast Budget to plan the costings and expenses of your projects", + "description": """ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +======================= +Account Budget Forecast +======================= +This module provides budget forecast functionnalities. + +Installation +============ +Just install account_budget_forecast, all dependencies will be installed by default. + +Known issues / Roadmap +====================== + +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 smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ +* Elabore: `Icon `_. + +Contributors +------------ +* Stéphan Sainléger + +Funders +------- +The development of this module has been financially supported by: +* Elabore (https://elabore.coop) +* Amaco (https://amaco.org) + +Maintainer +---------- +This module is maintained by ELABORE. + +""", + # any module necessary for this one to work correctly + "depends": [ + "account", + "analytic", + "base", + "crm", + "hr_timesheet", + "product", + "project", + "sale", + "stock", + ], + "external_dependencies": { + "python": [], + }, + # always loaded + "data": [ + "security/ir.model.access.csv", + "views/account_analytic_account.xml", + "views/account_analytic_account_categories.xml", + "views/account_invoice.xml", + "views/sale_order.xml", + "views/budget_forecast.xml", + "views/budget_coefficient.xml", + "views/budget_coefficient_model.xml", + "views/product_template_form.xml", + "views/hr_employee.xml", + "views/actions.xml", + "views/assets.xml", + "views/menu.xml", + "views/crm_lead.xml", + ], + # only loaded in demonstration mode + "demo": [], + "js": [], + "css": [], + "qweb": [], + "installable": True, + # Install this module automatically if all dependency have been previously + # and independently installed. Used for synergetic or glue modules. + "auto_install": False, + "application": False, +} diff --git a/account_budget_forecast/i18n/account_budget_forecast.pot b/account_budget_forecast/i18n/account_budget_forecast.pot new file mode 100644 index 0000000..b6f75cf --- /dev/null +++ b/account_budget_forecast/i18n/account_budget_forecast.pot @@ -0,0 +1,445 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_budget_forecast +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-07 08:46+0000\n" +"PO-Revision-Date: 2021-11-07 08:46+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__actual_amount +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__actual_amount +msgid "Actual Amount" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__actual_qty +msgid "Actual Quantity" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_account_analytic_account +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__analytic_id +#: model:ir.model.fields,field_description:account_budget_forecast.field_crm_lead__analytic_account +msgid "Analytic Account" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_account_analytic_line +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__analytic_line_ids +msgid "Analytic Line" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Analytic Lines" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__budget_forecast__display_type__line_article +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__product_template__budget_level__article +msgid "Article" +msgstr "" + +#. module: account_budget_forecast +#: code:addons/account_budget_forecast/models/account_analytic_account.py:0 +#: code:addons/account_budget_forecast/models/account_analytic_account.py:0 +#: code:addons/account_budget_forecast/models/project.py:0 +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.crm_lead_analytic_account_view_form +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.product_category_form_view_inherit +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_account_analytic_account_form +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_order_form +#, python-format +msgid "Budget" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_category__budget_category_id +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_product__budget_category_id +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_template__budget_category_id +#: model:ir.ui.menu,name:account_budget_forecast.menu_budget_forecast_category +msgid "Budget Category" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__budget_coefficients_ids +msgid "Budget Coefficients" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__budget_forecast_ids +msgid "Budget Forecast" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.actions.act_window,name:account_budget_forecast.act_budget_forecast +#: model:ir.ui.menu,name:account_budget_forecast.menu_budget_forecast +msgid "Budget Forecast lines" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_product__budget_level +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_template__budget_level +msgid "Budget level" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__product_template__budget_level__category +msgid "Category" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__child_ids +msgid "Child" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Childs" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__code +msgid "Code" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.constraint,message:account_budget_forecast.constraint_budget_forecast_category_uk_code +msgid "Code must be unique!" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Coeff" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__coeff +msgid "Coefficient" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_budget_coefficient +msgid "Coefficients for the order line price calculation" +msgstr "" + +#. module: account_budget_forecast +#. openerp-web +#: code:addons/account_budget_forecast/static/src/js/section_category_and_note_fields_backend.js:0 +#, python-format +msgid "Configure a product" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__create_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__create_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__create_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__create_uid +msgid "Created by" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__create_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__create_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__create_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__create_date +msgid "Created on" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__description +msgid "Description" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__display_actual_amounts +msgid "Display Actual Amounts" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Display Actual amounts" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__display_name +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__display_name +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__display_name +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__display_name +msgid "Display Name" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__display_type +msgid "Display Type" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_hr_employee +msgid "Employee" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__field_name +msgid "Field Name" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__global_coeff +msgid "Global coefficient" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Hide Actual amounts" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__id +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__id +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__id +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__id +msgid "ID" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient____last_update +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast____last_update +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category____last_update +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard____last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__write_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__write_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__write_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__write_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__write_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__write_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__write_date +msgid "Last Updated on" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_crm_lead +msgid "Lead/Opportunity" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__message +msgid "Message" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__name +msgid "Name" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.constraint,message:account_budget_forecast.constraint_budget_forecast_category_uk_name +msgid "Name must be unique!" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__name +msgid "Nom" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__note +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__budget_forecast__display_type__line_note +msgid "Note" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Notes" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.budget_forecast_message_wizard_form +msgid "Ok" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__parent_id +msgid "Parent" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__plan_amount_without_coeff +msgid "Plan Amount" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__plan_amount_with_coeff +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__plan_amount_with_coeff +#: model:ir.model.fields,field_description:account_budget_forecast.field_crm_lead__plan_amount_with_coeff +#: model:ir.model.fields,field_description:account_budget_forecast.field_sale_order__plan_amount_with_coeff +msgid "Plan Amount with coeff" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__plan_amount_without_coeff +msgid "Plan Amount without coeff" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__plan_price +msgid "Plan Price" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__plan_qty +msgid "Plan Quantity" +msgstr "" + +#. module: account_budget_forecast +#: code:addons/account_budget_forecast/models/sale_order.py:0 +#, python-format +msgid "Please set the analytic account" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__product_id +msgid "Product" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_product_category +msgid "Product Category" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_product_template +msgid "Product Template" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_project_project +msgid "Project" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Project Budget" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__project_section_budget_ids +msgid "Project Section Budget" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__project_managers +msgid "Project managers" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Quantities" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Quantity" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Refresh" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__budget_forecast__display_type__line_section +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__product_template__budget_level__section +msgid "Section" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__sequence +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__sequence +msgid "Sequence" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_budget_forecast_message_wizard +msgid "Show Message" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__budget_forecast__display_type__line_subsection +msgid "Sub-Section" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Summary" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,help:account_budget_forecast.field_budget_forecast__display_type +msgid "Technical field for UX purpose." +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_hr_employee__timesheet_product_id +msgid "Timesheet Product" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Total" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Totals" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__product_uom_id +msgid "Unit of Measure" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Unit prices" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__view_id +msgid "View" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_budget_forecast +msgid "budget.forecast" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_tree +msgid "total" +msgstr "" diff --git a/account_budget_forecast/i18n/fr.po b/account_budget_forecast/i18n/fr.po new file mode 100644 index 0000000..474398b --- /dev/null +++ b/account_budget_forecast/i18n/fr.po @@ -0,0 +1,445 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_budget_forecast +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-07 08:48+0000\n" +"PO-Revision-Date: 2021-11-07 08:48+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__actual_amount +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__actual_amount +msgid "Actual Amount" +msgstr "Montant actuel" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__actual_qty +msgid "Actual Quantity" +msgstr "Quantité actuelle" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_account_analytic_account +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__analytic_id +#: model:ir.model.fields,field_description:account_budget_forecast.field_crm_lead__analytic_account +msgid "Analytic Account" +msgstr "Compte analytique" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_account_analytic_line +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__analytic_line_ids +msgid "Analytic Line" +msgstr "Ligne analytique" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Analytic Lines" +msgstr "Lignes analytiques" + +#. module: account_budget_forecast +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__budget_forecast__display_type__line_article +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__product_template__budget_level__article +msgid "Article" +msgstr "Article" + +#. module: account_budget_forecast +#: code:addons/account_budget_forecast/models/account_analytic_account.py:0 +#: code:addons/account_budget_forecast/models/account_analytic_account.py:0 +#: code:addons/account_budget_forecast/models/project.py:0 +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.crm_lead_analytic_account_view_form +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.product_category_form_view_inherit +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_account_analytic_account_form +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_order_form +#, python-format +msgid "Budget" +msgstr "Chiffrage" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_category__budget_category_id +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_product__budget_category_id +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_template__budget_category_id +#: model:ir.ui.menu,name:account_budget_forecast.menu_budget_forecast_category +msgid "Budget Category" +msgstr "Catégorie de chiffrage" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__budget_coefficients_ids +msgid "Budget Coefficients" +msgstr "Coefficients de chiffrage" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__budget_forecast_ids +msgid "Budget Forecast" +msgstr "Chiffrage" + +#. module: account_budget_forecast +#: model:ir.actions.act_window,name:account_budget_forecast.act_budget_forecast +#: model:ir.ui.menu,name:account_budget_forecast.menu_budget_forecast +msgid "Budget Forecast lines" +msgstr "Lignes de chiffrage" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_product__budget_level +#: model:ir.model.fields,field_description:account_budget_forecast.field_product_template__budget_level +msgid "Budget level" +msgstr "Niveau de chiffrage" + +#. module: account_budget_forecast +#: model:ir.model.fields.selection,name:account_budget_forecast.category +msgid "Category" +msgstr "Sous-section" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__child_ids +msgid "Child" +msgstr "Enfant" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Childs" +msgstr "Enfants" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__code +msgid "Code" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.constraint,message:account_budget_forecast.constraint_budget_forecast_category_uk_code +msgid "Code must be unique!" +msgstr "Le code doit être unique !" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Coeff" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__coeff +msgid "Coefficient" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_budget_coefficient +msgid "Coefficients for the order line price calculation" +msgstr "Coefficiens pour le calcul des montants de chiffrages" + +#. module: account_budget_forecast +#. openerp-web +#: code:addons/account_budget_forecast/static/src/js/section_category_and_note_fields_backend.js:0 +#, python-format +msgid "Configure a product" +msgstr "Configurer un produit" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__create_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__create_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__create_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__create_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__create_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__create_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__description +msgid "Description" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__display_actual_amounts +msgid "Display Actual Amounts" +msgstr "Afficher les montants actuels" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Display Actual amounts" +msgstr "Afficher les montants actuels" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__display_name +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__display_name +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__display_name +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__display_name +msgid "Display Name" +msgstr "Nom affiché" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__display_type +msgid "Display Type" +msgstr "Type de ligne" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_hr_employee +msgid "Employee" +msgstr "Employé" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__field_name +msgid "Field Name" +msgstr "Nom de champ" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__global_coeff +msgid "Global coefficient" +msgstr "Coefficient global" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Hide Actual amounts" +msgstr "Cacher les montants actuels" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__id +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__id +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__id +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__id +msgid "ID" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient____last_update +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast____last_update +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category____last_update +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard____last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__write_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__write_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__write_uid +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__write_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__write_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__write_date +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_crm_lead +msgid "Lead/Opportunity" +msgstr "Piste/opportunité" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_message_wizard__message +msgid "Message" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__name +msgid "Name" +msgstr "Nom" + +#. module: account_budget_forecast +#: model:ir.model.constraint,message:account_budget_forecast.constraint_budget_forecast_category_uk_name +msgid "Name must be unique!" +msgstr "Le nom doit être unique !" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_coefficient__name +msgid "Name" +msgstr "Nom" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__note +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__budget_forecast__display_type__line_note +msgid "Note" +msgstr "Note" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Notes" +msgstr "Notes" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.budget_forecast_message_wizard_form +msgid "Ok" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__parent_id +msgid "Parent" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__plan_amount_without_coeff +msgid "Plan Amount" +msgstr "Montant prévu" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__plan_amount_with_coeff +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__plan_amount_with_coeff +#: model:ir.model.fields,field_description:account_budget_forecast.field_crm_lead__plan_amount_with_coeff +#: model:ir.model.fields,field_description:account_budget_forecast.field_sale_order__plan_amount_with_coeff +msgid "Plan Amount with coeff" +msgstr "Montant prévu avec coeff" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__plan_amount_without_coeff +msgid "Plan Amount without coeff" +msgstr "Montant prévu sans coeff" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__plan_price +msgid "Plan Price" +msgstr "Prix prévu" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__plan_qty +msgid "Plan Quantity" +msgstr "Quantité prévue" + +#. module: account_budget_forecast +#: code:addons/account_budget_forecast/models/sale_order.py:0 +#, python-format +msgid "Please set the analytic account" +msgstr "Renseignez le compte analytique svp" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__product_id +msgid "Product" +msgstr "Produit" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_product_category +msgid "Product Category" +msgstr "Catégorie d'article" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_product_template +msgid "Product Template" +msgstr "Modèle d'article" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_project_project +msgid "Project" +msgstr "Projet" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Project Budget" +msgstr "Chiffrage de projet" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__project_section_budget_ids +msgid "Project Section Budget" +msgstr "Lignes de section" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_account_analytic_account__project_managers +msgid "Project managers" +msgstr "Managers de projet" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Quantities" +msgstr "Quantités" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Quantity" +msgstr "Quantité" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Refresh" +msgstr "Actualiser" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_sale_order +msgid "Sales Order" +msgstr "Bon de commande" + +#. module: account_budget_forecast +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__budget_forecast__display_type__line_section +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__product_template__budget_level__section +msgid "Section" +msgstr "" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__sequence +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__sequence +msgid "Sequence" +msgstr "Séquence" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_budget_forecast_message_wizard +msgid "Show Message" +msgstr "Montrer le message" + +#. module: account_budget_forecast +#: model:ir.model.fields.selection,name:account_budget_forecast.selection__budget_forecast__display_type__line_subsection +msgid "Sub-Section" +msgstr "Sous-section" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Summary" +msgstr "Résumé" + +#. module: account_budget_forecast +#: model:ir.model.fields,help:account_budget_forecast.field_budget_forecast__display_type +msgid "Technical field for UX purpose." +msgstr "Champ technique pour le design" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_hr_employee__timesheet_product_id +msgid "Timesheet Product" +msgstr "Ligne de temps" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_analytic_budget_forecast +msgid "Total" +msgstr "Total" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Totals" +msgstr "Totaux" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast__product_uom_id +msgid "Unit of Measure" +msgstr "Unité de mesure" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +msgid "Unit prices" +msgstr "Prix unitaires" + +#. module: account_budget_forecast +#: model:ir.model.fields,field_description:account_budget_forecast.field_budget_forecast_category__view_id +msgid "View" +msgstr "Vue" + +#. module: account_budget_forecast +#: model:ir.model,name:account_budget_forecast.model_budget_forecast +msgid "budget.forecast" +msgstr "" + +#. module: account_budget_forecast +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_form +#: model_terms:ir.ui.view,arch_db:account_budget_forecast.view_budget_forecast_tree +msgid "total" +msgstr "total" diff --git a/account_budget_forecast/models/__init__.py b/account_budget_forecast/models/__init__.py new file mode 100644 index 0000000..8863cd4 --- /dev/null +++ b/account_budget_forecast/models/__init__.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from . import account_analytic_account +from . import account_analytic_line +from . import account_move +from . import budget_forecast +from . import sale_order +from . import product +from . import project +from . import budget_coefficient +from . import crm_lead diff --git a/account_budget_forecast/models/account_analytic_account.py b/account_budget_forecast/models/account_analytic_account.py new file mode 100644 index 0000000..2dfbbc1 --- /dev/null +++ b/account_budget_forecast/models/account_analytic_account.py @@ -0,0 +1,271 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + + +class AccountAnalyticAccount(models.Model): + _inherit = "account.analytic.account" + + budget_forecast_ids = fields.One2many("budget.forecast", "analytic_id", copy=True) + budget_forecast_manpower_ids = fields.One2many( + "budget.forecast", + "analytic_id", + domain=[("budget_category", "=", "manpower")], + copy=True, + ) + budget_forecast_material_ids = fields.One2many( + "budget.forecast", + "analytic_id", + domain=[("budget_category", "=", "material")], + copy=True, + ) + budget_forecast_equipment_ids = fields.One2many( + "budget.forecast", + "analytic_id", + domain=[("budget_category", "=", "equipment")], + copy=True, + ) + budget_forecast_subcontractors_ids = fields.One2many( + "budget.forecast", + "analytic_id", + domain=[("budget_category", "=", "subcontractors")], + copy=True, + ) + budget_forecast_miscellaneous_ids = fields.One2many( + "budget.forecast", + "analytic_id", + domain=[("budget_category", "=", "miscellaneous")], + copy=True, + ) + project_section_budget_ids = fields.One2many( + "budget.forecast", + "analytic_id", + domain=[ + ("display_type", "in", ["line_section", "line_subsection"]), + ("is_summary", "=", True), + ], + copy=False, + ) + budget_coefficients_ids = fields.One2many( + "budget.coefficient", + "budget_forecast", + copy=True, + ) + global_coeff = fields.Float("Global coefficient", compute="_calc_global_coeff") + plan_amount_without_coeff = fields.Float( + "Plan Amount without coeff", compute="_calc_budget_amount" + ) + plan_amount_with_coeff = fields.Float( + "Plan Amount with coeff", compute="_calc_budget_amount" + ) + total_expenses = fields.Float("Total expenses", compute="_calc_budget_amount") + remaining_budget = fields.Float("Remaining budget", compute="_calc_budget_amount") + total_incomes = fields.Float("Total incomes", compute="_calc_budget_amount") + project_balance = fields.Float("Project Balance", compute="_calc_budget_amount") + + display_actual_amounts = fields.Boolean( + string="Display Actual Amounts", default=False + ) + project_managers = fields.Many2many( + "res.users", + string="Project managers", + domain=lambda self: [("groups_id", "in", self.env.ref("base.group_user").id)], + ) + opportunity = fields.Many2one( + "crm.lead", + string="Opportunity", + compute="_compute_opportunity", + ) + + def default_budget_coefficients_ids(self): + for coeff in self.budget_coefficients_ids: + coeff.unlink() + coeff_models = self.env["budget.coefficient.model"].search([]) + for model in coeff_models: + vals = { + "name": model.name, + "coeff": model.coeff, + "note": model.note, + "budget_forecast": self.id, + } + self.env["budget.coefficient"].create(vals) + + def _compute_opportunity(self): + lead = self.env["crm.lead"].search( + [("analytic_account", "=", self.id)], limit=1 + ) + if lead: + self.opportunity = lead.id + + @api.model + def create(self, values): + record = super(AccountAnalyticAccount, self).create(values) + if not record.project_managers: + record.project_managers = self.env["res.users"].browse(self.env.user.id) + record.default_budget_coefficients_ids() + return record + + @api.depends( + "budget_forecast_ids.plan_amount_without_coeff", + "budget_forecast_ids.plan_amount_with_coeff", + "budget_forecast_ids.actual_amount", + ) + def _calc_budget_amount(self): + for record in self: + line_ids = record.mapped("budget_forecast_ids").filtered( + lambda line: (line.display_type == "line_section") + and (line.is_summary == True) + ) + # Planned amounts + record.plan_amount_without_coeff = sum( + line_ids.mapped("plan_amount_without_coeff") + ) + record.plan_amount_with_coeff = sum( + line_ids.mapped("plan_amount_with_coeff") + ) + # Expenses + record.total_expenses = sum(line_ids.mapped("actual_amount")) + record.remaining_budget = ( + record.plan_amount_with_coeff - record.total_expenses + ) + # Incomes + record.total_incomes = 0 + domain = [ + ("analytic_account_id", "=", record.id), + ("parent_state", "in", ["draft", "posted"]), + ("move_id.type", "in", ["out_invoice", "out_refund"]), + ] + invoice_lines = self.env["account.move.line"].search(domain) + for invoice_line in invoice_lines: + if invoice_line.move_id.type == "out_invoice": + record.total_incomes = ( + record.total_incomes + invoice_line.price_subtotal + ) + elif invoice_line.move_id.type == "out_refund": + record.total_incomes = ( + record.total_incomes - invoice_line.price_subtotal + ) + # Balance + record.project_balance = record.total_incomes - record.total_expenses + + def _calc_global_coeff(self): + for record in self: + line_ids = record.mapped("budget_coefficients_ids") + record.global_coeff = sum(line_ids.mapped("coeff")) + + def action_budget_forecast(self): + # Access only if the connected user is the project manager or an Odoo administrator + if not ( + self.env.uid in self.project_managers.ids + or self.env.user.has_group("base.group_system") + ): + raise UserError( + _( + "You are not manager of this project.\nPlease contact the project manager or your Odoo administrator." + ) + ) + return { + "type": "ir.actions.act_window", + "name": _("Budget"), + "res_model": self._name, + "res_id": self.id, + "view_mode": "form", + "view_type": "form", + "views": [ + ( + self.env.ref( + "account_budget_forecast.view_analytic_budget_forecast" + ).id, + "form", + ) + ], + "context": {"budget_forecast": True}, + } + + def name_get(self): + if self._context.get("budget_forecast"): + res = [] + for analytic in self: + res.append((analytic.id, _("Budget"))) + return res + return super(AccountAnalyticAccount, self).name_get() + + def displayActualAmounts(self): + for record in self: + if record.display_actual_amounts: + record.display_actual_amounts = False + else: + record.display_actual_amounts = True + + def _update_summary(self): + for record in self: + summary_line_ids = ( + record.mapped("budget_forecast_ids") + .filtered(lambda r: r.is_summary) + .sorted(key=lambda r: r.sequence, reverse=True) + ) + for summary_line in summary_line_ids: + # find corresponding category section/sub_section lines + lines = record.mapped("budget_forecast_ids").filtered( + lambda x: ( + (not x.is_summary) and (x.summary_id.id == summary_line.id) + ) + ) + # Calculate the total amounts + summary_line.plan_amount_without_coeff = 0 + summary_line.plan_amount_with_coeff = 0 + summary_line.actual_amount = 0 + for line in lines: + summary_line.plan_amount_without_coeff += ( + line.plan_amount_without_coeff + ) + summary_line.plan_amount_with_coeff += line.plan_amount_with_coeff + summary_line.actual_amount += line.actual_amount + + def action_refresh(self): + for record in self: + line_ids = ( + record.mapped("budget_forecast_ids") + .filtered(lambda r: not r.is_summary) + .sorted(key=lambda r: r.sequence, reverse=True) + ) + for line in line_ids: + line.refresh() + record._update_summary() + record._calc_budget_amount() + + def action_create_quotation(self): + quotation = self.env["sale.order"].create( + { + "company_id": self.company_id.id, + "partner_id": self.env.user.partner_id.id, + } + ) + for section in self.budget_forecast_ids.filtered( + lambda s: (s.display_type == "line_section") and (s.is_summary == True) + ): + values = { + "order_id": quotation.id, + "product_id": section.product_id.id, + "name": section.product_id.name, + "product_uom_qty": 1.0, + "price_unit": section.plan_amount_with_coeff, + } + self.env["sale.order.line"].create(values) + quotation.analytic_account_id = self.id + quotation.opportunity_id = self.opportunity.id + return { + "type": "ir.actions.act_window", + "name": _("Quotation"), + "res_model": "sale.order", + "res_id": quotation.id, + "view_mode": "form", + "view_type": "form", + "views": [ + ( + self.env.ref("sale.view_order_form").id, + "form", + ) + ], + } diff --git a/account_budget_forecast/models/account_analytic_line.py b/account_budget_forecast/models/account_analytic_line.py new file mode 100644 index 0000000..2ec1c9f --- /dev/null +++ b/account_budget_forecast/models/account_analytic_line.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api + + +class AccountAnalyticLine(models.Model): + _inherit = "account.analytic.line" + + budget_forecast_id = fields.Many2one("budget.forecast") + + # def _timesheet_preprocess(self, vals): + # vals = super(AccountAnalyticLine, self)._timesheet_preprocess(vals) + # if vals.get("so_line") and not vals.get("product_id"): + # so_line = self.env["sale.order.line"].browse(vals["so_line"]) + # vals["product_id"] = so_line.product_id.id + # if vals.get("employee_id") and not vals.get("product_id"): + # employee = self.env["hr.employee"].browse(vals["employee_id"]) + # vals["product_id"] = employee.timesheet_product_id.id + # return vals diff --git a/account_budget_forecast/models/account_move.py b/account_budget_forecast/models/account_move.py new file mode 100644 index 0000000..edf731d --- /dev/null +++ b/account_budget_forecast/models/account_move.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + budget_forecast_id = fields.Many2one("budget.forecast") + + @api.depends("budget_forecast_id") + def _transfer_budget_forecast_line(self): + for record in self: + record.analytic_line_ids.budget_forecast_id = record.budget_forecast_id.id diff --git a/account_budget_forecast/models/budget_coefficient.py b/account_budget_forecast/models/budget_coefficient.py new file mode 100644 index 0000000..97f6573 --- /dev/null +++ b/account_budget_forecast/models/budget_coefficient.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from odoo import api, models, fields + + +class BudgetCoefficientModel(models.Model): + _name = "budget.coefficient.model" + description = "Coefficient Models for the order line price calculation" + + name = fields.Char(string="Name", required=True) + coeff = fields.Float(string="Coefficient", required=True, default=0.00) + note = fields.Text(string="Description") + + +class BudgetCoefficient(models.Model): + _name = "budget.coefficient" + _description = "Coefficients for the order line price calculation" + + name = fields.Char(string="Name", required=True) + coeff = fields.Float(string="Coefficient", required=True, default=0.00) + budget_forecast = fields.Many2one("account.analytic.account", string="Budget") + note = fields.Text(string="Description") diff --git a/account_budget_forecast/models/budget_forecast.py b/account_budget_forecast/models/budget_forecast.py new file mode 100644 index 0000000..12c2083 --- /dev/null +++ b/account_budget_forecast/models/budget_forecast.py @@ -0,0 +1,388 @@ +# -*- coding: utf-8 -*- +import logging +from odoo import models, fields, api, _ + +_logger = logging.getLogger(__name__) + + +class BudgetForecast(models.Model): + _name = "budget.forecast" + _description = _name + + name = fields.Char("Name", store=True) + description = fields.Char("Description", copy=True) + sequence = fields.Integer() + analytic_id = fields.Many2one( + "account.analytic.account", + "Analytic Account", + required=True, + ondelete="restrict", + index=True, + copy=True, + ) + + budget_category = fields.Selection( + [ + ("manpower", "Manpower"), + ("material", "Material"), + ("equipment", "Equipment"), + ("subcontractors", "Subcontractors"), + ("miscellaneous", "Miscellaneous"), + ], + string=_("Budget Category"), + ) + is_summary = fields.Boolean(copy=False, default=False, store=True) + summary_id = fields.Many2one("budget.forecast", store=True) + display_type = fields.Selection( + [ + ("line_section", "Section"), + ("line_subsection", "Sub-Section"), + ("line_article", "Article"), + ("line_note", "Note"), + ], + copy=True, + store=True, + ) + + product_id = fields.Many2one( + "product.product", copy=True, domain="[('budget_level', '=', display_type)]" + ) + + plan_qty = fields.Float("Plan Quantity", copy=False) + plan_price = fields.Float("Plan Price", default=0.00, copy=False) + plan_amount_without_coeff = fields.Float( + "Plan Amount", compute="_calc_plan_amount_without_coeff", store=True, copy=False + ) + plan_amount_with_coeff = fields.Float( + "Plan Amount with coeff", + compute="_calc_plan_amount_with_coeff", + store=True, + copy=False, + ) + + analytic_line_ids = fields.One2many( + "account.analytic.line", "budget_forecast_id", copy=False + ) + actual_qty = fields.Float( + "Actual Quantity", + compute="_calc_actual", + store=True, + compute_sudo=True, + copy=False, + ) + actual_amount = fields.Float( + "Expenses", + compute="_calc_actual", + store=True, + compute_sudo=True, + copy=False, + ) + diff_expenses = fields.Float( + "Diff", compute="_calc_actual", store=True, compute_sudo=True, copy=False + ) + incomes = fields.Float( + "Incomes", + compute="_calc_actual", + store=True, + compute_sudo=True, + copy=False, + ) + balance = fields.Float( + "Balance", compute="_calc_actual", store=True, compute_sudo=True, copy=False + ) + parent_id = fields.Many2one( + "budget.forecast", store=True, compute_sudo=True, compute="_calc_parent_id" + ) + child_ids = fields.One2many("budget.forecast", "parent_id", copy=False) + + note = fields.Text(string="Note") + + ################################################################################### + # Budget lines management + ################################################################################### + + @api.model_create_multi + @api.returns("self", lambda value: value.id) + def create(self, vals_list): + records = super(BudgetForecast, self).create(vals_list) + for record in records: + if not record.display_type: + record.display_type = "line_article" + elif record.is_summary and record.display_type in [ + "line_section", + "line_subsection", + ]: + record._create_category_sections() + return records + + def _create_category_sections(self): + categories = dict(self._fields["budget_category"].selection) + for category in categories: + # Create other category lines + values = { + "budget_category": category, + "summary_id": self.id, + "is_summary": False, + } + self.copy(values) + + def write(self, vals, one_more_loop=True): + res = super(BudgetForecast, self).write(vals) + if one_more_loop: + self._sync_sections_data() + return res + + def unlink(self, child_unlink=False): + parent_ids = self.mapped("parent_id") + if not child_unlink: + for record in self: + if not record.is_summary and ( + record.display_type + in [ + "line_section", + "line_subsection", + ] + ): + # find similar section/sub_section lines + lines = record.env["budget.forecast"].search( + [ + "|", + ("summary_id", "=", record.summary_id.id), + ("id", "=", record.summary_id.id), + ] + ) + for line in lines: + line.unlink(True) + res = super(BudgetForecast, self).unlink() + parent_ids.exists()._calc_plan() + return res + + @api.onchange("product_id") + def _onchange_product_id(self): + if self.product_id: + self.description = self.product_id.name + if self.display_type == "line_article": + self.plan_price = self.product_id.standard_price + else: + self._calc_plan_price() + else: + self.description = "" + self.plan_price = 0 + if self.display_type != "line_article": + self._calc_plan_price() + + def _get_budget_category_label(self): + categories = dict(self._fields["budget_category"].selection) + for key, val in categories.items(): + if key == self.budget_category: + return val + return "" + + @api.onchange("description", "product_id") + def _compute_name(self): + for record in self: + if record.product_id: + name = ( + record.description + + " - " + + record.product_id.name + + " - " + + record._get_budget_category_label() + + " - " + + record.analytic_id.name + ) + values = { + "name": name, + } + record.write(values, False) + + def _sync_sections_data(self): + for record in self: + if record.display_type in ["line_section", "line_subsection"]: + if not record.is_summary: + # find corresponding line summary + summary_line = self.env["budget.forecast"].browse( + record.summary_id.id + ) + values = { + "product_id": record.product_id.id, + "description": record.description, + } + summary_line.write(values, False) + summary_line._compute_name() + + # find similar category section/sub_section lines + domain = [ + ("is_summary", "=", False), + ("id", "!=", record.id), + ] + if not record.is_summary: + domain.extend([("summary_id", "=", record.summary_id.id)]) + else: + domain.extend([("summary_id", "=", record.id)]) + lines = self.env["budget.forecast"].search(domain) + for line in lines: + values = { + "product_id": record.product_id.id, + "description": record.description, + } + line.write(values, False) + line._compute_name() + + @api.depends( + "analytic_id.budget_forecast_ids", "sequence", "parent_id", "child_ids" + ) + def _calc_parent_id(self): + for record in self: + if record.display_type == "line_section": + # A Section is the top of the line hierarchy => no parent + record.parent_id = False + continue + found = False + parent_id = False + for line in record.analytic_id.budget_forecast_ids.search( + [ + ("analytic_id", "=", record.analytic_id.id), + ("budget_category", "=", record.budget_category), + ] + ).sorted(key=lambda r: r.sequence, reverse=True): + if not found and line != record: + continue + if line == record: + found = True + continue + if line.display_type in ["line_article", "line_note"]: + continue + elif line.display_type == "line_subsection": + if record.display_type in ["line_article", "line_note"]: + parent_id = line + break + else: + continue + elif line.display_type == "line_section": + parent_id = line + break + record.parent_id = parent_id + + def refresh(self): + self._calc_parent_id() + self._calc_plan() + self._calc_actual() + + ################################################################################### + # Amounts calculation + ################################################################################### + + def _calc_plan(self): + self._calc_plan_qty() + self._calc_plan_price() + self._calc_plan_amount_without_coeff() + self._calc_plan_amount_with_coeff() + + @api.depends("plan_qty", "plan_price", "child_ids") + def _calc_plan_amount_without_coeff(self): + for record in self: + if record.child_ids: + record.plan_amount_without_coeff = sum( + record.mapped("child_ids.plan_amount_without_coeff") + ) + else: + record.plan_amount_without_coeff = record.plan_qty * record.plan_price + + @api.depends("plan_qty", "plan_price", "child_ids") + def _calc_plan_amount_with_coeff(self): + for record in self: + record.plan_amount_with_coeff = record.plan_amount_without_coeff * ( + 1 + record.analytic_id.global_coeff + ) + + @api.depends("child_ids") + def _calc_plan_qty(self): + for record in self: + if record.child_ids: + record.plan_qty = sum(record.mapped("child_ids.plan_qty")) + + @api.depends("child_ids") + def _calc_plan_price(self): + for record in self: + if record.display_type in ["line_section", "line_subsection"]: + if record.child_ids: + lst = record.mapped("child_ids.plan_price") + if lst and (sum(lst) > 0): + record.plan_price = lst and sum(lst) + else: + record.plan_price = record.product_id.standard_price + else: + record.plan_price = record.product_id.standard_price + elif record.display_type == "line_note": + record.plan_price = 0.00 + + @api.depends("analytic_id.line_ids.amount") + def _calc_actual(self): + for record in self: + if record.display_type in ["line_section", "line_subsection"]: + if record.child_ids: + # Actual expenses are calculated with the child lines + record.actual_qty = sum(record.mapped("child_ids.actual_qty")) + record.actual_amount = sum(record.mapped("child_ids.actual_amount")) + # Incomes are calculated with the analytic lines + line_ids = record.analytic_line_ids.filtered( + lambda x: x.move_id.move_id.type + in ["out_invoice", "out_refund"] + ) + record.incomes = sum(line_ids.mapped("amount")) + + # Add Invoice lines ids + domain = [ + ("analytic_account_id", "=", record.analytic_id.id), + ("parent_state", "in", ["draft", "posted"]), + ("budget_forecast_id", "=", record.id), + ("move_id.type", "in", ["out_invoice", "out_refund"]), + ] + invoice_lines = self.env["account.move.line"].search(domain) + for invoice_line in invoice_lines: + if invoice_line.move_id.type == "out_invoice": + record.incomes = ( + record.incomes + invoice_line.price_subtotal + ) + elif invoice_line.move_id.type == "out_refund": + record.incomes = ( + record.incomes - invoice_line.price_subtotal + ) + record.balance = record.incomes - record.actual_amount + + elif record.display_type == "line_note": + record.actual_qty = 0 + record.actual_amount = 0.00 + else: + line_ids = record.analytic_line_ids.filtered( + lambda x: x.move_id.move_id.type + not in ["out_invoice", "out_refund"] + ) + record.actual_qty = abs(sum(line_ids.mapped("unit_amount"))) + record.actual_amount = -sum(line_ids.mapped("amount")) + + # Add Invoice lines ids + domain = [ + ("analytic_account_id", "=", record.analytic_id.id), + ("parent_state", "in", ["draft", "posted"]), + ("budget_forecast_id", "=", record.id), + ("move_id.type", "in", ["in_invoice", "in_refund"]), + ] + invoice_lines = self.env["account.move.line"].search(domain) + for invoice_line in invoice_lines: + if invoice_line.move_id.type == "in_invoice": + record.actual_qty = record.actual_qty + invoice_line.quantity + record.actual_amount = ( + record.actual_amount + invoice_line.price_subtotal + ) + elif invoice_line.move_id.type == "in_refund": + record.actual_qty = record.actual_qty - invoice_line.quantity + record.actual_amount = ( + record.actual_amount - invoice_line.price_subtotal + ) + + record.incomes = None + record.balance = None + + record.diff_expenses = record.plan_amount_with_coeff - record.actual_amount diff --git a/account_budget_forecast/models/crm_lead.py b/account_budget_forecast/models/crm_lead.py new file mode 100644 index 0000000..99620e9 --- /dev/null +++ b/account_budget_forecast/models/crm_lead.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from odoo import _, models, fields, api +from odoo.exceptions import UserError + + +class Lead(models.Model): + _inherit = "crm.lead" + + analytic_account = fields.Many2one( + "account.analytic.account", "Analytic Account", required=False, index=True + ) + plan_amount_with_coeff = fields.Float( + related="analytic_account.plan_amount_with_coeff" + ) + + def action_budget_forecast(self): + if not self.analytic_account: + raise UserError( + _( + "You must add an analytic account to build/access the budget forecast screen." + ) + ) + return self.analytic_account.action_budget_forecast() + + def action_new_quotation(self): + action = super(Lead, self).action_new_quotation() + if self.analytic_account: + action["context"]["default_analytic_account_id"] = self.analytic_account.id + return action diff --git a/account_budget_forecast/models/product.py b/account_budget_forecast/models/product.py new file mode 100644 index 0000000..b84dbf1 --- /dev/null +++ b/account_budget_forecast/models/product.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, _ + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + budget_level = fields.Selection( + [ + ("line_section", "Section"), + ("line_subsection", "Sub-Section"), + ("line_article", "Article"), + ], + string="Budget level", + default="line_article", + ) diff --git a/account_budget_forecast/models/project.py b/account_budget_forecast/models/project.py new file mode 100644 index 0000000..5485896 --- /dev/null +++ b/account_budget_forecast/models/project.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from odoo import models, _ +import json + + +class Project(models.Model): + _inherit = "project.project" + + def _plan_get_stat_button(self): + res = super(Project, self)._plan_get_stat_button() + action = self.analytic_account_id.action_budget_forecast() + res.append( + { + "name": _("Budget"), + "icon": "fa-usd", + "action": { + "data-model": action["res_model"], + "data-views": json.dumps(action["views"]), + "data-res-id": action["res_id"], + }, + } + ) + return res diff --git a/account_budget_forecast/models/sale_order.py b/account_budget_forecast/models/sale_order.py new file mode 100644 index 0000000..5974943 --- /dev/null +++ b/account_budget_forecast/models/sale_order.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, _, api +from odoo.exceptions import Warning + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + plan_amount_with_coeff = fields.Float( + related="analytic_account_id.plan_amount_with_coeff" + ) + + def action_budget_forecast(self): + if not self.analytic_account_id: + raise Warning(_("Please set the analytic account")) + return self.analytic_account_id.action_budget_forecast() + + @api.returns("self", lambda value: value.id) + def copy(self, default=None): + record = super(SaleOrder, self).copy(default=default) + if self.analytic_account_id.budget_forecast_ids: + if self.name in self.analytic_account_id.name: + name = self.analytic_account_id.name.replace(self.name, record.name) + else: + name = "%s: %s" % (self.analytic_account_id.name, record.name) + record.analytic_account_id = self.analytic_account_id.copy( + default=dict(name=name) + ) + return record diff --git a/account_budget_forecast/security/ir.model.access.csv b/account_budget_forecast/security/ir.model.access.csv new file mode 100644 index 0000000..60a888d --- /dev/null +++ b/account_budget_forecast/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_budget_forecast_project_user,access_budget_forecast_project_user,model_budget_forecast,project.group_project_user,1,1,1,1 +access_budget_coefficient,budget_coefficient,model_budget_coefficient,base.group_user,1,1,1,1 +access_budget_coefficient_model,budget_coefficient_model,model_budget_coefficient_model,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/account_budget_forecast/static/src/js/section_category_and_note_fields_backend.js b/account_budget_forecast/static/src/js/section_category_and_note_fields_backend.js new file mode 100644 index 0000000..1cc6a69 --- /dev/null +++ b/account_budget_forecast/static/src/js/section_category_and_note_fields_backend.js @@ -0,0 +1,217 @@ + +odoo.define('account_budget_forecast.section_category_and_note_backend', function (require) { +// The goal of this file is to contain JS hacks related to allowing +// section, category and note on budget forecast. + +"use strict"; +var pyUtils = require('web.py_utils'); +var core = require('web.core'); +var _t = core._t; +var FieldChar = require('web.basic_fields').FieldChar; +var FieldOne2Many = require('web.relational_fields').FieldOne2Many; +var fieldRegistry = require('web.field_registry'); +var FieldText = require('web.basic_fields').FieldText; +var ListRenderer = require('web.ListRenderer'); + +var SectionCategoryAndNoteListRenderer = ListRenderer.extend({ + /** + * We want section and note to take the whole line (except handle and trash) + * to look better and to hide the unnecessary fields. + * + * @override + */ + _renderBodyCell: function (record, node, index, options) { + var $cell = this._super.apply(this, arguments); + + var isNote = record.data.display_type === 'line_note'; + + if (isNote) { + if (node.attrs.widget === "handle") { + return $cell; + } else if (node.attrs.name === "description") { + var nbrColumns = this._getNumberOfCols(); + if (this.handleField) { + nbrColumns--; + } + if (this.addTrashIcon) { + nbrColumns--; + } + $cell.attr('colspan', nbrColumns); + } else { + return $cell.addClass('o_hidden'); + } + } + + return $cell; + }, + /** + * We add the o_is_{display_type} class to allow custom behaviour both in JS and CSS. + * + * @override + */ + _renderRow: function (record, index) { + var $row = this._super.apply(this, arguments); + + if (record.data.display_type) { + $row.removeClass('table-striped'); + $row.addClass('o_is_budget_' + record.data.display_type); + } + + return $row; + }, + /** + * We want to add .o_section_category_and_note_list_view on the table to have stronger CSS. + * + * @override + * @private + */ + _renderView: function () { + var def = this._super(); + this.$el.find('> table').addClass('o_section_category_and_note_list_view'); + this.$el.find('> table').removeClass('table-striped'); + return def; + }, + _renderView: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + self.$('.o_list_table').addClass('o_section_category_and_note_list_view'); + self.$('.o_list_table').removeClass('table-striped'); + }); + }, + /** + * Add support for product configurator + * + * @override + * @private + */ + _onAddRecord: function (ev) { + // we don't want the browser to navigate to a the # url + ev.preventDefault(); + + // we don't want the click to cause other effects, such as unselecting + // the row that we are creating, because it counts as a click on a tr + ev.stopPropagation(); + + // but we do want to unselect current row + var self = this; + this.unselectRow().then(function () { + var context = ev.currentTarget.dataset.context; + + var pricelistId = self._getPricelistId(); + if (context && pyUtils.py_eval(context).open_product_configurator){ + self._rpc({ + model: 'ir.model.data', + method: 'xmlid_to_res_id', + kwargs: {xmlid: 'sale.sale_product_configurator_view_form'}, + }).then(function (res_id) { + self.do_action({ + name: _t('Configure a product'), + type: 'ir.actions.act_window', + res_model: 'sale.product.configurator', + views: [[res_id, 'form']], + target: 'new', + context: { + 'default_pricelist_id': pricelistId + } + }, { + on_close: function (products) { + if (products && products !== 'special'){ + self.trigger_up('add_record', { + context: self._productsToRecords(products), + forceEditable: "bottom" , + allowWarning: true, + onSuccess: function (){ + self.unselectRow(); + } + }); + } + } + }); + }); + } else { + self.trigger_up('add_record', {context: context && [context]}); // TODO write a test, the deferred was not considered + } + }); + }, + + /** + * Will try to get the pricelist_id value from the parent sale_order form + * + * @private + * @returns {integer} pricelist_id's id + */ + _getPricelistId: function () { + var saleOrderForm = this.getParent() && this.getParent().getParent(); + var stateData = saleOrderForm && saleOrderForm.state && saleOrderForm.state.data; + var pricelist_id = stateData.pricelist_id && stateData.pricelist_id.data && stateData.pricelist_id.data.id; + + return pricelist_id; + }, + + /** + * Will map the products to appropriate record objects that are + * ready for the default_get + * + * @private + * @param {Array} products The products to transform into records + */ + _productsToRecords: function (products) { + var records = []; + _.each(products, function (product){ + var record = { + default_product_id: product.product_id, + default_product_uom_qty: product.quantity + }; + + if (product.no_variant_attribute_values) { + var default_product_no_variant_attribute_values = []; + _.each(product.no_variant_attribute_values, function (attribute_value) { + default_product_no_variant_attribute_values.push( + [4, parseInt(attribute_value.value)] + ); + }); + record['default_product_no_variant_attribute_value_ids'] + = default_product_no_variant_attribute_values; + } + + if (product.product_custom_attribute_values) { + var default_custom_attribute_values = []; + _.each(product.product_custom_attribute_values, function (attribute_value) { + default_custom_attribute_values.push( + [0, 0, { + attribute_value_id: attribute_value.attribute_value_id, + custom_value: attribute_value.custom_value + }] + ); + }); + record['default_product_custom_attribute_value_ids'] + = default_custom_attribute_values; + } + + records.push(record); + }); + + return records; + } +}); + +// We create a custom widget because this is the cleanest way to do it: +// to be sure this custom code will only impact selected fields having the widget +// and not applied to any other existing ListRenderer. +var SectionCategoryAndNoteFieldOne2Many = FieldOne2Many.extend({ + /** + * We want to use our custom renderer for the list. + * + * @override + */ + _getRenderer: function () { + if (this.view.arch.tag === 'tree') { + return SectionCategoryAndNoteListRenderer; + } + return this._super.apply(this, arguments); + }, +}); + +fieldRegistry.add('section_category_and_note_one2many', SectionCategoryAndNoteFieldOne2Many); + +}); diff --git a/account_budget_forecast/static/src/scss/section_category_and_note_backend.scss b/account_budget_forecast/static/src/scss/section_category_and_note_backend.scss new file mode 100644 index 0000000..a29d6dc --- /dev/null +++ b/account_budget_forecast/static/src/scss/section_category_and_note_backend.scss @@ -0,0 +1,22 @@ + +// The goal of this file is to contain CSS hacks related to allowing +// section, category and note on budget forecast. + +tr.o_data_row.o_is_budget_line_note, +tr.o_data_row.o_is_budget_line_note textarea[name="description"], +div.oe_kanban_card.o_is_budget_line_note { + font-style: italic; +} +tr.o_data_row.o_is_budget_line_section, +div.oe_kanban_card.o_is_budget_line_section { + font-weight: bold; + background-color: #b59898; +} +tr.o_data_row.o_is_budget_line_subsection, +div.oe_kanban_card.o_is_budget_line_subsection { + font-style: italic; + background-color: #dbd1d1; +} +tr.o_data_row.o_is_budget_line_note { + word-break: break-all; +} diff --git a/account_budget_forecast/views/account_analytic_account.xml b/account_budget_forecast/views/account_analytic_account.xml new file mode 100644 index 0000000..a849024 --- /dev/null +++ b/account_budget_forecast/views/account_analytic_account.xml @@ -0,0 +1,102 @@ + + + + + analytic.analytic.account.budget.forecast + account.analytic.account + 100 + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
\ No newline at end of file diff --git a/account_budget_forecast/views/account_analytic_account_categories.xml b/account_budget_forecast/views/account_analytic_account_categories.xml new file mode 100644 index 0000000..afd9a20 --- /dev/null +++ b/account_budget_forecast/views/account_analytic_account_categories.xml @@ -0,0 +1,148 @@ + + + + budget.forecast.category.miscellaneous + account.analytic.account + + 9999 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/account_budget_forecast/views/account_invoice.xml b/account_budget_forecast/views/account_invoice.xml new file mode 100644 index 0000000..45c83bf --- /dev/null +++ b/account_budget_forecast/views/account_invoice.xml @@ -0,0 +1,13 @@ + + + + account.invoice.budget.form + account.move + + + + + + + + \ No newline at end of file diff --git a/account_budget_forecast/views/actions.xml b/account_budget_forecast/views/actions.xml new file mode 100644 index 0000000..674e09b --- /dev/null +++ b/account_budget_forecast/views/actions.xml @@ -0,0 +1,8 @@ + + + + Budget coefficient models + budget.coefficient.model + tree + + \ No newline at end of file diff --git a/account_budget_forecast/views/assets.xml b/account_budget_forecast/views/assets.xml new file mode 100644 index 0000000..b9695a3 --- /dev/null +++ b/account_budget_forecast/views/assets.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/account_budget_forecast/views/budget_coefficient.xml b/account_budget_forecast/views/budget_coefficient.xml new file mode 100644 index 0000000..87013e4 --- /dev/null +++ b/account_budget_forecast/views/budget_coefficient.xml @@ -0,0 +1,18 @@ + + + + budget.coefficient.form + budget.coefficient + +
+ + + + + + + +
+
+
+
\ No newline at end of file diff --git a/account_budget_forecast/views/budget_coefficient_model.xml b/account_budget_forecast/views/budget_coefficient_model.xml new file mode 100644 index 0000000..5f86983 --- /dev/null +++ b/account_budget_forecast/views/budget_coefficient_model.xml @@ -0,0 +1,14 @@ + + + + budget.coefficient.tree + budget.coefficient.model + + + + + + + + + \ No newline at end of file diff --git a/account_budget_forecast/views/budget_forecast.xml b/account_budget_forecast/views/budget_forecast.xml new file mode 100644 index 0000000..c2159c9 --- /dev/null +++ b/account_budget_forecast/views/budget_forecast.xml @@ -0,0 +1,98 @@ + + + + + budget.forecast.form + budget.forecast + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + budget.forecast.tree + budget.forecast + + + + + + + + + + + + + + + + + + + Budget Forecast lines + budget.forecast + tree,form + +
\ No newline at end of file diff --git a/account_budget_forecast/views/crm_lead.xml b/account_budget_forecast/views/crm_lead.xml new file mode 100644 index 0000000..dec01eb --- /dev/null +++ b/account_budget_forecast/views/crm_lead.xml @@ -0,0 +1,18 @@ + + + + crm.lead.analytic_account.form + crm.lead + + +
+ +
+ + + +
+
+
\ No newline at end of file diff --git a/account_budget_forecast/views/hr_employee.xml b/account_budget_forecast/views/hr_employee.xml new file mode 100644 index 0000000..b5b6c61 --- /dev/null +++ b/account_budget_forecast/views/hr_employee.xml @@ -0,0 +1,16 @@ + + + + + hr.timesheet.tree.timesheet + account.analytic.line + 20 + + + + + + + + + \ No newline at end of file diff --git a/account_budget_forecast/views/menu.xml b/account_budget_forecast/views/menu.xml new file mode 100644 index 0000000..1dedcc1 --- /dev/null +++ b/account_budget_forecast/views/menu.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/account_budget_forecast/views/product_template_form.xml b/account_budget_forecast/views/product_template_form.xml new file mode 100644 index 0000000..27fa303 --- /dev/null +++ b/account_budget_forecast/views/product_template_form.xml @@ -0,0 +1,13 @@ + + + + product.budget.level.form.inherit + product.template + + + + + + + + \ No newline at end of file diff --git a/account_budget_forecast/views/sale_order.xml b/account_budget_forecast/views/sale_order.xml new file mode 100644 index 0000000..7a57462 --- /dev/null +++ b/account_budget_forecast/views/sale_order.xml @@ -0,0 +1,17 @@ + + + + + sale.order.form + sale.order + + +
+ +
+
+
+ +
\ No newline at end of file