diff --git a/sale_timesheet_budget/README.rst b/sale_timesheet_budget/README.rst new file mode 100644 index 0000000000..d5104915d5 --- /dev/null +++ b/sale_timesheet_budget/README.rst @@ -0,0 +1,113 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +===================== +Sale timesheet budget +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:f87109f6f4372bd1ff932d9dd90bd4c19896fda92a1e729aacd3678eea72c1e1 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-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%2Ftimesheet-lightgray.png?logo=github + :target: https://github.com/OCA/timesheet/tree/19.0/sale_timesheet_budget + :alt: OCA/timesheet +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/timesheet-19-0/timesheet-19-0-sale_timesheet_budget + :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/timesheet&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds the "Budget" tab to the projects/sales orders to be +able to set some additional lines (incomes/expenses) linked to a planned +budget. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +1. Go to *Projects* and create a new one, setting Customer, analytic + account and Billable. +2. Go to *Sales -> Orders -> Quotations* and create a new one setting + the same customer and project previously created. +3. Alternatively, you can create a sales order with lines that creates a + project. +4. Go to the project, and edit it. +5. Add some records (positive or negative) in the "Budget" tab. +6. Go to *Project Updates* button and a new line (Budget) in the + *Profitability > Revenues* section will appear with the total amount + of the lines. + +Known issues / Roadmap +====================== + +- Consider the planned hours of the tasks based on price/hour profiles. + +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 +------- + +* Tecnativa + +Contributors +------------ + +- `Tecnativa `__: + + - Víctor Martínez + - Pedro M. Baeza + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-victoralmau| image:: https://github.com/victoralmau.png?size=40px + :target: https://github.com/victoralmau + :alt: victoralmau + +Current `maintainer `__: + +|maintainer-victoralmau| + +This module is part of the `OCA/timesheet `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_timesheet_budget/__init__.py b/sale_timesheet_budget/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/sale_timesheet_budget/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_timesheet_budget/__manifest__.py b/sale_timesheet_budget/__manifest__.py new file mode 100644 index 0000000000..53dd6694af --- /dev/null +++ b/sale_timesheet_budget/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2022 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Sale timesheet budget", + "version": "19.0.1.0.0", + "category": "Timesheet", + "website": "https://github.com/OCA/timesheet", + "author": "Tecnativa, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": ["sale_timesheet"], + "installable": True, + "data": [ + "security/ir.model.access.csv", + "views/project_project_view.xml", + ], + "maintainers": ["victoralmau"], +} diff --git a/sale_timesheet_budget/i18n/es.po b/sale_timesheet_budget/i18n/es.po new file mode 100644 index 0000000000..45207e7138 --- /dev/null +++ b/sale_timesheet_budget/i18n/es.po @@ -0,0 +1,146 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_timesheet_budget +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-06-27 11:03+0000\n" +"PO-Revision-Date: 2026-04-16 21:45+0000\n" +"Last-Translator: Ed-Spain \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.15.2\n" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__amount +#: model_terms:ir.ui.view,arch_db:sale_timesheet_budget.view_project_project_budget_tree +msgid "Amount" +msgstr "Importe" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__analytic_account_id +msgid "Analytic Account" +msgstr "Cuenta analítica" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,help:sale_timesheet_budget.field_project_project_budget__analytic_account_id +msgid "" +"Analytic account to which this project, its tasks and its timesheets are " +"linked. \n" +"Track the costs and revenues of your project by setting this analytic " +"account on your related documents (e.g. sales orders, invoices, purchase " +"orders, vendor bills, expenses etc.).\n" +"This analytic account can be changed on each task individually if " +"necessary.\n" +"An analytic account is required in order to use timesheets." +msgstr "" +"Cuenta analítica vinculada a este proyecto, sus tareas y sus hojas de " +"horas.. \n" +"Realice un seguimiento de los costos e ingresos de su proyecto configurando " +"esta cuenta analítica en los documentos relacionados (por ejemplo, pedidos " +"de venta, facturas, órdenes de compra, facturas de proveedores, gastos, etc.)" +".\n" +"Esta cuenta analítica se puede modificar individualmente para cada tarea si " +"es necesario.\n" +"Se requiere una cuenta analítica para utilizar las hojas de horas." + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project__budget_amount +msgid "Budget Amount" +msgstr "Importe de presupuesto" + +#. module: sale_timesheet_budget +#. odoo-python +#: code:addons/sale_timesheet_budget/models/project_project.py:0 +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project__budget_ids +#: model_terms:ir.ui.view,arch_db:sale_timesheet_budget.project_project_view_form +#, python-format +msgid "Budgets" +msgstr "Presupuestos" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__name +msgid "Concept" +msgstr "Concepto" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__date +msgid "Date" +msgstr "Fecha" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__id +msgid "ID" +msgstr "ID" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__write_uid +msgid "Last Updated by" +msgstr "Última Actualización por" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__write_date +msgid "Last Updated on" +msgstr "Actualizado el" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__price_unit +msgid "Product Price" +msgstr "Precio de producto" + +#. module: sale_timesheet_budget +#: model:ir.model,name:sale_timesheet_budget.model_project_project +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__project_id +msgid "Project" +msgstr "Proyecto" + +#. module: sale_timesheet_budget +#: model:ir.actions.act_window,name:sale_timesheet_budget.action_project_project_budget +msgid "Project Budgets" +msgstr "Presupuestos Proyectos" + +#. module: sale_timesheet_budget +#: model:ir.model,name:sale_timesheet_budget.model_project_project_budget +msgid "Project Project Budget" +msgstr "Proyecto Presupuesto del Proyecto" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__quantity +msgid "Quantity" +msgstr "Cantidad" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__sale_order_id +msgid "Sale Order" +msgstr "Pedido de venta" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__sale_order_id_domain +msgid "Sale Order Id Domain" +msgstr "Dominio del ID del pedido de venta" diff --git a/sale_timesheet_budget/i18n/it.po b/sale_timesheet_budget/i18n/it.po new file mode 100644 index 0000000000..1dac38c7d9 --- /dev/null +++ b/sale_timesheet_budget/i18n/it.po @@ -0,0 +1,144 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_timesheet_budget +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-07-08 08:59+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.6.2\n" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__amount +#: model_terms:ir.ui.view,arch_db:sale_timesheet_budget.view_project_project_budget_tree +msgid "Amount" +msgstr "Importo" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__analytic_account_id +msgid "Analytic Account" +msgstr "Conto analitico" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,help:sale_timesheet_budget.field_project_project_budget__analytic_account_id +msgid "" +"Analytic account to which this project, its tasks and its timesheets are " +"linked. \n" +"Track the costs and revenues of your project by setting this analytic " +"account on your related documents (e.g. sales orders, invoices, purchase " +"orders, vendor bills, expenses etc.).\n" +"This analytic account can be changed on each task individually if " +"necessary.\n" +"An analytic account is required in order to use timesheets." +msgstr "" +"Cono analitico a cui sono collegati il progetto, i suoi lavori e e suoi " +"fogli ore. \n" +"Traccia i costi e i ricavi dei progetti impostando questo conto analitico " +"nei documenti relativi (es. ordini di vendita, fatture, ordini di acquisto, " +"fatture cliente, spese, ecc.).\n" +"Questo conto analitico può esere modificato per singolo lavoro se necessario." +"\n" +"Per l'utilizzo dei fogli ore è richiesto un conto analitico." + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project__budget_amount +msgid "Budget Amount" +msgstr "Valore budget" + +#. module: sale_timesheet_budget +#. odoo-python +#: code:addons/sale_timesheet_budget/models/project_project.py:0 +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project__budget_ids +#: model_terms:ir.ui.view,arch_db:sale_timesheet_budget.project_project_view_form +#, python-format +msgid "Budgets" +msgstr "Budget" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__name +msgid "Concept" +msgstr "Concetto" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__date +msgid "Date" +msgstr "Data" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__id +msgid "ID" +msgstr "ID" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__price_unit +msgid "Product Price" +msgstr "Prezzo prodotto" + +#. module: sale_timesheet_budget +#: model:ir.model,name:sale_timesheet_budget.model_project_project +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__project_id +msgid "Project" +msgstr "Progetto" + +#. module: sale_timesheet_budget +#: model:ir.actions.act_window,name:sale_timesheet_budget.action_project_project_budget +msgid "Project Budgets" +msgstr "Budget progetto" + +#. module: sale_timesheet_budget +#: model:ir.model,name:sale_timesheet_budget.model_project_project_budget +msgid "Project Project Budget" +msgstr "Progettazione budget progetto" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__quantity +msgid "Quantity" +msgstr "Quantità" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__sale_order_id +msgid "Sale Order" +msgstr "Ordine di vendita" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__sale_order_id_domain +msgid "Sale Order Id Domain" +msgstr "Dominio ID ordine di vendita" diff --git a/sale_timesheet_budget/i18n/pt_BR.po b/sale_timesheet_budget/i18n/pt_BR.po new file mode 100644 index 0000000000..78ba94e98b --- /dev/null +++ b/sale_timesheet_budget/i18n/pt_BR.po @@ -0,0 +1,145 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_timesheet_budget +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-07-10 18:58+0000\n" +"Last-Translator: Rodrigo Sottomaior Macedo " +"\n" +"Language-Team: none\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.6.2\n" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__amount +#: model_terms:ir.ui.view,arch_db:sale_timesheet_budget.view_project_project_budget_tree +msgid "Amount" +msgstr "Montante" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__analytic_account_id +msgid "Analytic Account" +msgstr "Conta Analítica" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,help:sale_timesheet_budget.field_project_project_budget__analytic_account_id +msgid "" +"Analytic account to which this project, its tasks and its timesheets are " +"linked. \n" +"Track the costs and revenues of your project by setting this analytic " +"account on your related documents (e.g. sales orders, invoices, purchase " +"orders, vendor bills, expenses etc.).\n" +"This analytic account can be changed on each task individually if " +"necessary.\n" +"An analytic account is required in order to use timesheets." +msgstr "" +"Conta analítica à qual estão vinculados este projeto, suas tarefas e suas " +"planilhas de horas.\n" +"Acompanhe os custos e receitas do seu projeto definindo esta conta analítica " +"nos seus documentos relacionados (por exemplo, pedidos de vendas, faturas, " +"pedidos de compra, contas de fornecedores, despesas, etc.).\n" +"Esta conta analítica pode ser alterada em cada tarefa individualmente, se " +"necessário.\n" +"É necessária uma conta analítica para usar planilhas de horas." + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project__budget_amount +msgid "Budget Amount" +msgstr "Montante Orçado" + +#. module: sale_timesheet_budget +#. odoo-python +#: code:addons/sale_timesheet_budget/models/project_project.py:0 +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project__budget_ids +#: model_terms:ir.ui.view,arch_db:sale_timesheet_budget.project_project_view_form +#, python-format +msgid "Budgets" +msgstr "Orçamentos" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__name +msgid "Concept" +msgstr "Conceito" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__create_uid +msgid "Created by" +msgstr "Criado Por" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__create_date +msgid "Created on" +msgstr "Criado Em" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__date +msgid "Date" +msgstr "Data" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__display_name +msgid "Display Name" +msgstr "Nome exibido" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__id +msgid "ID" +msgstr "ID" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget____last_update +msgid "Last Modified on" +msgstr "Última Modificação Em" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__write_uid +msgid "Last Updated by" +msgstr "Última modificação feita por" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__write_date +msgid "Last Updated on" +msgstr "Última Atualização Feita em" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__price_unit +msgid "Product Price" +msgstr "Preço do Produto" + +#. module: sale_timesheet_budget +#: model:ir.model,name:sale_timesheet_budget.model_project_project +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__project_id +msgid "Project" +msgstr "Projeto" + +#. module: sale_timesheet_budget +#: model:ir.actions.act_window,name:sale_timesheet_budget.action_project_project_budget +msgid "Project Budgets" +msgstr "Orçamentos de projetos" + +#. module: sale_timesheet_budget +#: model:ir.model,name:sale_timesheet_budget.model_project_project_budget +msgid "Project Project Budget" +msgstr "Projeção do Orçamento do Projeto" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__quantity +msgid "Quantity" +msgstr "Quantidade" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__sale_order_id +msgid "Sale Order" +msgstr "Pedido de Venda" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__sale_order_id_domain +msgid "Sale Order Id Domain" +msgstr "Domínio de ID do pedido de venda" diff --git a/sale_timesheet_budget/i18n/sale_timesheet_budget.pot b/sale_timesheet_budget/i18n/sale_timesheet_budget.pot new file mode 100644 index 0000000000..130a2c387f --- /dev/null +++ b/sale_timesheet_budget/i18n/sale_timesheet_budget.pot @@ -0,0 +1,114 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_timesheet_budget +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \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: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__amount +#: model_terms:ir.ui.view,arch_db:sale_timesheet_budget.view_project_project_budget_tree +msgid "Amount" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project__budget_amount +msgid "Budget Amount" +msgstr "" + +#. module: sale_timesheet_budget +#. odoo-python +#: code:addons/sale_timesheet_budget/models/project_project.py:0 +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project__budget_ids +#: model_terms:ir.ui.view,arch_db:sale_timesheet_budget.project_project_view_form +msgid "Budgets" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__name +msgid "Concept" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__create_uid +msgid "Created by" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__create_date +msgid "Created on" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__date +msgid "Date" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__display_name +msgid "Display Name" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__id +msgid "ID" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__write_date +msgid "Last Updated on" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__price_unit +msgid "Product Price" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model,name:sale_timesheet_budget.model_project_project +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__project_id +msgid "Project" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__analytic_account_id +msgid "Project Account" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.actions.act_window,name:sale_timesheet_budget.action_project_project_budget +msgid "Project Budgets" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model,name:sale_timesheet_budget.model_project_project_budget +msgid "Project Project Budget" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__quantity +msgid "Quantity" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__sale_order_id +msgid "Sale Order" +msgstr "" + +#. module: sale_timesheet_budget +#: model:ir.model.fields,field_description:sale_timesheet_budget.field_project_project_budget__sale_order_id_domain +msgid "Sale Order Id Domain" +msgstr "" diff --git a/sale_timesheet_budget/models/__init__.py b/sale_timesheet_budget/models/__init__.py new file mode 100644 index 0000000000..c183898c17 --- /dev/null +++ b/sale_timesheet_budget/models/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import project_project diff --git a/sale_timesheet_budget/models/project_project.py b/sale_timesheet_budget/models/project_project.py new file mode 100644 index 0000000000..e83237ab6a --- /dev/null +++ b/sale_timesheet_budget/models/project_project.py @@ -0,0 +1,105 @@ +# Copyright 2022-2024 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ProjectProject(models.Model): + _inherit = "project.project" + + budget_ids = fields.One2many( + comodel_name="project.project.budget", + inverse_name="project_id", + string="Budgets", + copy=False, + ) + budget_amount = fields.Float(compute="_compute_budget_amount") + + @api.depends("budget_ids") + def _compute_budget_amount(self): + data = self.env["project.project.budget"]._read_group( + domain=[("project_id", "in", self.ids)], + groupby=["project_id"], + aggregates=["amount:sum"], + ) + mapped_data = {project.id: amount_sum for project, amount_sum in data} + for item in self: + item.budget_amount = mapped_data.get(item.id, 0) + + def action_profitability_budget_item(self): + action = self.env["ir.actions.act_window"]._for_xml_id( + "sale_timesheet_budget.action_project_project_budget" + ) + action["domain"] = [("project_id", "=", self.id)] + return action + + def _get_profitability_labels(self): + res = super()._get_profitability_labels() + res["budgets"] = self.env._("Budgets") + return res + + def _get_profitability_sequence_per_invoice_type(self): + res = super()._get_profitability_sequence_per_invoice_type() + res["budgets"] = 8 + return res + + def _get_profitability_items(self, with_action=True): + items = super()._get_profitability_items(with_action) + if not self.budget_ids: + return items + items["revenues"]["data"].append( + { + "id": "budgets", + "sequence": self._get_profitability_sequence_per_invoice_type()[ + "budgets" + ], + "invoiced": 0, + "to_invoice": self.budget_amount, + "action": { + "name": "action_profitability_budget_item", + "type": "object", + }, + } + ) + items["revenues"]["total"]["to_invoice"] += self.budget_amount + return items + + +class ProjectProjectBudget(models.Model): + _name = "project.project.budget" + _description = "Project Project Budget" + _order = "date, id" + + date = fields.Date() + name = fields.Char(string="Concept") + project_id = fields.Many2one( + comodel_name="project.project", string="Project", required=True + ) + sale_order_id_domain = fields.Binary(compute="_compute_sale_order_id_domain") + sale_order_id = fields.Many2one( + comodel_name="sale.order", + string="Sale Order", + domain="sale_order_id_domain", + ) + analytic_account_id = fields.Many2one(related="project_id.account_id") + quantity = fields.Float(digits="Account", default=1, required=True) + price_unit = fields.Float(string="Product Price", digits="Account", required=True) + amount = fields.Float(compute="_compute_amount", store=True) + + @api.depends("project_id", "project_id.partner_id", "project_id.account_id") + def _compute_sale_order_id_domain(self): + for item in self: + item.sale_order_id_domain = [ + ("partner_id", "=", item.project_id.partner_id.id), + ( + "project_account_id", + "=", + item.project_id.account_id.id, + ), + ("state", "!=", "cancel"), + ] + + @api.depends("quantity", "price_unit") + def _compute_amount(self): + for item in self: + item.amount = item.quantity * item.price_unit diff --git a/sale_timesheet_budget/pyproject.toml b/sale_timesheet_budget/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/sale_timesheet_budget/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/sale_timesheet_budget/readme/CONTRIBUTORS.md b/sale_timesheet_budget/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..5fee390427 --- /dev/null +++ b/sale_timesheet_budget/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- [Tecnativa](https://www.tecnativa.com): + - Víctor Martínez + - Pedro M. Baeza diff --git a/sale_timesheet_budget/readme/DESCRIPTION.md b/sale_timesheet_budget/readme/DESCRIPTION.md new file mode 100644 index 0000000000..4258f897ed --- /dev/null +++ b/sale_timesheet_budget/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +This module adds the "Budget" tab to the projects/sales orders to be +able to set some additional lines (incomes/expenses) linked to a planned +budget. diff --git a/sale_timesheet_budget/readme/ROADMAP.md b/sale_timesheet_budget/readme/ROADMAP.md new file mode 100644 index 0000000000..3628a068d9 --- /dev/null +++ b/sale_timesheet_budget/readme/ROADMAP.md @@ -0,0 +1 @@ +- Consider the planned hours of the tasks based on price/hour profiles. diff --git a/sale_timesheet_budget/readme/USAGE.md b/sale_timesheet_budget/readme/USAGE.md new file mode 100644 index 0000000000..36ebccfc0a --- /dev/null +++ b/sale_timesheet_budget/readme/USAGE.md @@ -0,0 +1,11 @@ +1. Go to *Projects* and create a new one, setting Customer, analytic + account and Billable. +2. Go to *Sales -\> Orders -\> Quotations* and create a new one setting + the same customer and project previously created. +3. Alternatively, you can create a sales order with lines that creates + a project. +4. Go to the project, and edit it. +5. Add some records (positive or negative) in the "Budget" tab. +6. Go to *Project Updates* button and a new line (Budget) in the + *Profitability \> Revenues* section will appear with the total + amount of the lines. diff --git a/sale_timesheet_budget/security/ir.model.access.csv b/sale_timesheet_budget/security/ir.model.access.csv new file mode 100644 index 0000000000..a047a568e8 --- /dev/null +++ b/sale_timesheet_budget/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_project_project_budget_admin,project_project_budget_portal,model_project_project_budget,project.group_project_manager,1,1,1,1 +access_project_project_budget_user,project_project_budget_user,model_project_project_budget,project.group_project_user,1,0,0,0 diff --git a/sale_timesheet_budget/static/description/icon.png b/sale_timesheet_budget/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/sale_timesheet_budget/static/description/icon.png differ diff --git a/sale_timesheet_budget/static/description/index.html b/sale_timesheet_budget/static/description/index.html new file mode 100644 index 0000000000..5a52e8e616 --- /dev/null +++ b/sale_timesheet_budget/static/description/index.html @@ -0,0 +1,461 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Sale timesheet budget

+ +

Beta License: AGPL-3 OCA/timesheet Translate me on Weblate Try me on Runboat

+

This module adds the “Budget” tab to the projects/sales orders to be +able to set some additional lines (incomes/expenses) linked to a planned +budget.

+

Table of contents

+ +
+

Usage

+
    +
  1. Go to Projects and create a new one, setting Customer, analytic +account and Billable.
  2. +
  3. Go to Sales -> Orders -> Quotations and create a new one setting +the same customer and project previously created.
  4. +
  5. Alternatively, you can create a sales order with lines that creates a +project.
  6. +
  7. Go to the project, and edit it.
  8. +
  9. Add some records (positive or negative) in the “Budget” tab.
  10. +
  11. Go to Project Updates button and a new line (Budget) in the +Profitability > Revenues section will appear with the total amount +of the lines.
  12. +
+
+
+

Known issues / Roadmap

+
    +
  • Consider the planned hours of the tasks based on price/hour profiles.
  • +
+
+
+

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

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+
    +
  • Tecnativa:
      +
    • Víctor Martínez
    • +
    • Pedro M. Baeza
    • +
    +
  • +
+
+
+

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.

+

Current maintainer:

+

victoralmau

+

This module is part of the OCA/timesheet project on GitHub.

+

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

+
+
+
+
+ + diff --git a/sale_timesheet_budget/tests/__init__.py b/sale_timesheet_budget/tests/__init__.py new file mode 100644 index 0000000000..34e6d3d843 --- /dev/null +++ b/sale_timesheet_budget/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_sale_timesheet_budget diff --git a/sale_timesheet_budget/tests/test_sale_timesheet_budget.py b/sale_timesheet_budget/tests/test_sale_timesheet_budget.py new file mode 100644 index 0000000000..6b3004e870 --- /dev/null +++ b/sale_timesheet_budget/tests/test_sale_timesheet_budget.py @@ -0,0 +1,78 @@ +# Copyright 2022 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests import Form + +from odoo.addons.base.tests.common import BaseCommon + + +class TestSaleTimesheetBudget(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.customer = cls.env["res.partner"].create({"name": "Mr Odoo"}) + cls.plan = cls.env["account.analytic.plan"].create({"name": "Test plan"}) + cls.analytic_account = cls.env["account.analytic.account"].create( + { + "name": "Test account", + "partner_id": cls.customer.id, + "plan_id": cls.plan.id, + } + ) + cls.project = cls.env["project.project"].create( + { + "name": "Test project", + "partner_id": cls.customer.id, + "account_id": cls.analytic_account.id, + "allow_billable": True, + } + ) + cls.sale_order = cls.env["sale.order"].create( + { + "partner_id": cls.customer.id, + "project_id": cls.project.id, + } + ) + + def test_project_budget(self): + # Test project without budgets to cover early return in _get_profitability_items + data_no_budget = self.project.get_panel_data() + if "profitability_items" in data_no_budget: + self.assertNotIn( + "budgets", + [ + item["id"] + for item in data_no_budget["profitability_items"]["revenues"][ + "data" + ] + ], + ) + + project_form = Form(self.project) + with project_form.budget_ids.new() as budget_form: + budget_form.sale_order_id = self.sale_order + budget_form.quantity = 2 + budget_form.price_unit = 10 + with project_form.budget_ids.new() as budget_form: + budget_form.sale_order_id = self.sale_order + budget_form.quantity = -1 + budget_form.price_unit = 10 + project_form.save() + self.assertEqual(self.project.budget_amount, 10) + labels = self.project._get_profitability_labels() + self.assertIn("budgets", labels) + data = self.project.get_panel_data() + revenues = data["profitability_items"]["revenues"] + self.assertEqual(len(revenues["data"]), 1) + revenues_data = revenues["data"][0] + self.assertEqual(revenues_data["id"], "budgets") + self.assertEqual(revenues_data["invoiced"], 0) + self.assertEqual(revenues_data["to_invoice"], 10) + expected_action = {"name": "action_profitability_budget_item", "type": "object"} + self.assertEqual(revenues_data["action"], expected_action) + self.assertEqual(revenues["total"]["invoiced"], 0) + self.assertEqual(revenues["total"]["to_invoice"], 10) + res = self.project.action_profitability_budget_item() + records = self.env[res["res_model"]].search(res["domain"]) + self.assertEqual(len(records), 2) + self.assertEqual(sum(records.mapped("amount")), 10) diff --git a/sale_timesheet_budget/views/project_project_view.xml b/sale_timesheet_budget/views/project_project_view.xml new file mode 100644 index 0000000000..ab938d6c85 --- /dev/null +++ b/sale_timesheet_budget/views/project_project_view.xml @@ -0,0 +1,50 @@ + + + project.project.budget + + + + + + + + + + + + + + project.project.budget + + primary + 1000 + + + 1 + + + parent.sale_order_id + + + + + project.project.form.inherit - Add budget + project.project + + + + + + + + + + + Project Budgets + project.project.budget + list + +