diff --git a/project_task_product/README.rst b/project_task_product/README.rst new file mode 100644 index 0000000000..a55909fb99 --- /dev/null +++ b/project_task_product/README.rst @@ -0,0 +1,80 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +==================== +Project Task Product +==================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:98ba208bd705d4da9bab304404c33f194e7ea20fbe57cb59828fe6c1794d10f6 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fproject-lightgray.png?logo=github + :target: https://github.com/OCA/project/tree/19.0/project_task_product + :alt: OCA/project +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/project-19-0/project-19-0-project_task_product + :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/project&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Allows to specify the product that relates to the project task. + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* ForgeFlow + +Contributors +------------ + +- Marina Alapont + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/project `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/project_task_product/__init__.py b/project_task_product/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/project_task_product/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/project_task_product/__manifest__.py b/project_task_product/__manifest__.py new file mode 100644 index 0000000000..6f0e545b36 --- /dev/null +++ b/project_task_product/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Project Task Product", + "summary": "Allows to specify in a project task, the product the task relates to.", + "version": "19.0.1.0.0", + "category": "Project", + "website": "https://github.com/OCA/project", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": ["project", "product"], + "data": [ + "views/project_task_views.xml", + "views/product_views.xml", + ], + "application": False, + "installable": True, +} diff --git a/project_task_product/models/__init__.py b/project_task_product/models/__init__.py new file mode 100644 index 0000000000..452b8b66d9 --- /dev/null +++ b/project_task_product/models/__init__.py @@ -0,0 +1,2 @@ +from . import project_task +from . import product diff --git a/project_task_product/models/product.py b/project_task_product/models/product.py new file mode 100644 index 0000000000..85503a2887 --- /dev/null +++ b/project_task_product/models/product.py @@ -0,0 +1,43 @@ +# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + task_count = fields.Integer(compute="_compute_task_count") + + def _compute_task_count(self): + for product in self: + product.task_count = self.env["project.task"].search_count( + [("product_id", "=", product.id)] + ) + + def action_view_tasks(self): + action = self.env["ir.actions.actions"]._for_xml_id("project.action_view_task") + action.update( + { + "domain": [("product_id", "in", self.ids)], + "context": {"default_product_id": self.id}, + } + ) + return action + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + task_count = fields.Integer(compute="_compute_task_count") + + def _compute_task_count(self): + for template in self: + template.task_count = self.env["project.task"].search_count( + [("product_id", "in", template.product_variant_ids.ids)] + ) + + def action_view_tasks(self): + action = self.env["ir.actions.actions"]._for_xml_id("project.action_view_task") + action["domain"] = [("product_id", "in", self.product_variant_ids.ids)] + return action diff --git a/project_task_product/models/project_task.py b/project_task_product/models/project_task.py new file mode 100644 index 0000000000..5f02141535 --- /dev/null +++ b/project_task_product/models/project_task.py @@ -0,0 +1,12 @@ +# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ProjectTask(models.Model): + _inherit = "project.task" + + product_id = fields.Many2one( + "product.product", string="Product", check_company=True + ) diff --git a/project_task_product/pyproject.toml b/project_task_product/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/project_task_product/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/project_task_product/readme/CONTRIBUTORS.md b/project_task_product/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..25a2caefd6 --- /dev/null +++ b/project_task_product/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Marina Alapont \<\> diff --git a/project_task_product/readme/DESCRIPTION.md b/project_task_product/readme/DESCRIPTION.md new file mode 100644 index 0000000000..3f94551fcd --- /dev/null +++ b/project_task_product/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Allows to specify the product that relates to the project task. diff --git a/project_task_product/static/description/index.html b/project_task_product/static/description/index.html new file mode 100644 index 0000000000..72ed9e17fd --- /dev/null +++ b/project_task_product/static/description/index.html @@ -0,0 +1,429 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Project Task Product

+ +

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

+

Allows to specify the product that relates to the project task.

+

Table of contents

+ +
+

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

+
    +
  • ForgeFlow
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

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

+

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

+
+
+
+
+ + diff --git a/project_task_product/tests/__init__.py b/project_task_product/tests/__init__.py new file mode 100644 index 0000000000..32ab6e7d91 --- /dev/null +++ b/project_task_product/tests/__init__.py @@ -0,0 +1 @@ +from . import test_project_task_product diff --git a/project_task_product/tests/test_project_task_product.py b/project_task_product/tests/test_project_task_product.py new file mode 100644 index 0000000000..6c6af97612 --- /dev/null +++ b/project_task_product/tests/test_project_task_product.py @@ -0,0 +1,78 @@ +# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import TransactionCase + + +class TestProjectTaskProduct(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.product = cls.env["product.product"].create({"name": "Test Product"}) + cls.project = cls.env["project.project"].create( + { + "name": "Test Project", + } + ) + + def test_product_task_count(self): + """Task count on product.product must count only tasks linked to it.""" + + self.assertEqual(self.product.task_count, 0) + self.env["project.task"].create( + { + "name": "Task 1", + "product_id": self.product.id, + "project_id": self.project.id, + } + ) + + self.env["project.task"].create( + { + "name": "Task 2", + "product_id": self.product.id, + "project_id": self.project.id, + } + ) + self.product._compute_task_count() + self.assertEqual(self.product.task_count, 2) + + def test_product_action_view_tasks(self): + """Check action domain + context for product.product.""" + action = self.product.action_view_tasks() + + self.assertEqual(action["domain"], [("product_id", "in", [self.product.id])]) + self.assertEqual(action["context"]["default_product_id"], self.product.id) + + def test_template_task_count(self): + """Task count on product.template must sum all variant tasks.""" + template = self.product.product_tmpl_id + variant2 = self.env["product.product"].create( + {"name": "Test Product 2", "product_tmpl_id": template.id} + ) + + self.assertEqual(template.task_count, 0) + + self.env["project.task"].create( + { + "name": "Task 1", + "product_id": self.product.id, + "project_id": self.project.id, + } + ) + + self.env["project.task"].create( + {"name": "Task 2", "product_id": variant2.id, "project_id": self.project.id} + ) + + template._compute_task_count() + self.assertEqual(template.task_count, 2) + + def test_template_action_view_tasks(self): + """Check action domain for product.template.""" + template = self.product.product_tmpl_id + + action = template.action_view_tasks() + + self.assertEqual(action["domain"], [("product_id", "in", [self.product.id])]) diff --git a/project_task_product/views/product_views.xml b/project_task_product/views/product_views.xml new file mode 100644 index 0000000000..7e75333a1d --- /dev/null +++ b/project_task_product/views/product_views.xml @@ -0,0 +1,22 @@ + + + + product.template.form.inherit.tasks + product.template + + + + + + + + + diff --git a/project_task_product/views/project_task_views.xml b/project_task_product/views/project_task_views.xml new file mode 100644 index 0000000000..9f4c6877b2 --- /dev/null +++ b/project_task_product/views/project_task_views.xml @@ -0,0 +1,13 @@ + + + + view.task.form2.inherit + project.task + + + + + + + + diff --git a/project_task_product_stock/README.rst b/project_task_product_stock/README.rst new file mode 100644 index 0000000000..f06e528da0 --- /dev/null +++ b/project_task_product_stock/README.rst @@ -0,0 +1,81 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +========================== +Project Task Product Stock +========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:98ba208bd705d4da9bab304404c33f194e7ea20fbe57cb59828fe6c1794d10f6 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fproject-lightgray.png?logo=github + :target: https://github.com/OCA/project/tree/19.0/project_task_product_stock + :alt: OCA/project +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/project-19-0/project-19-0-project_task_product_stock + :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/project&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Allows to specify the lot/serial number of the product that relates to +the project task, if it is tracked by lots. + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* ForgeFlow + +Contributors +------------ + +- Marina Alapont + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/project `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/project_task_product_stock/__init__.py b/project_task_product_stock/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/project_task_product_stock/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/project_task_product_stock/__manifest__.py b/project_task_product_stock/__manifest__.py new file mode 100644 index 0000000000..bcb9b2a4ef --- /dev/null +++ b/project_task_product_stock/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Project Task Product Stock", + "summary": "Allows to specify in a project task, the lot or serial number of" + " the task product.", + "version": "19.0.1.0.0", + "category": "Project", + "website": "https://github.com/OCA/project", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": ["project_task_product", "stock"], + "data": [ + "views/project_task_views.xml", + "views/stock_lot_views.xml", + ], + "application": False, + "installable": True, +} diff --git a/project_task_product_stock/models/__init__.py b/project_task_product_stock/models/__init__.py new file mode 100644 index 0000000000..0146c3fcb6 --- /dev/null +++ b/project_task_product_stock/models/__init__.py @@ -0,0 +1,2 @@ +from . import project_task +from . import stock_lot diff --git a/project_task_product_stock/models/project_task.py b/project_task_product_stock/models/project_task.py new file mode 100644 index 0000000000..50ee343dda --- /dev/null +++ b/project_task_product_stock/models/project_task.py @@ -0,0 +1,15 @@ +# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ProjectTask(models.Model): + _inherit = "project.task" + + lot_id = fields.Many2one( + "stock.lot", + string="Lot/Serial Number", + domain="[('product_id', '=', product_id)]", + tracking=True, + ) diff --git a/project_task_product_stock/models/stock_lot.py b/project_task_product_stock/models/stock_lot.py new file mode 100644 index 0000000000..1a820a32f7 --- /dev/null +++ b/project_task_product_stock/models/stock_lot.py @@ -0,0 +1,32 @@ +# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class StockLot(models.Model): + _inherit = "stock.lot" + + task_count = fields.Integer(compute="_compute_task_count") + + def _compute_task_count(self): + for lot in self: + lot.task_count = self.env["project.task"].search_count( + [("product_id", "=", lot.product_id.id), ("lot_id", "=", lot.id)] + ) + + def action_view_tasks(self): + action = self.env["ir.actions.actions"]._for_xml_id("project.action_view_task") + action.update( + { + "domain": [ + ("product_id", "=", self.product_id.id), + ("lot_id", "=", self.id), + ], + "context": { + "default_product_id": self.product_id.id, + "default_lot_id": self.id, + }, + } + ) + return action diff --git a/project_task_product_stock/pyproject.toml b/project_task_product_stock/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/project_task_product_stock/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/project_task_product_stock/readme/CONTRIBUTORS.md b/project_task_product_stock/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..25a2caefd6 --- /dev/null +++ b/project_task_product_stock/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Marina Alapont \<\> diff --git a/project_task_product_stock/readme/DESCRIPTION.md b/project_task_product_stock/readme/DESCRIPTION.md new file mode 100644 index 0000000000..9d8af8a9b2 --- /dev/null +++ b/project_task_product_stock/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Allows to specify the lot/serial number of the product that relates to the project task, if it is tracked by lots. diff --git a/project_task_product_stock/static/description/index.html b/project_task_product_stock/static/description/index.html new file mode 100644 index 0000000000..808edeee60 --- /dev/null +++ b/project_task_product_stock/static/description/index.html @@ -0,0 +1,430 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Project Task Product Stock

+ +

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

+

Allows to specify the lot/serial number of the product that relates to +the project task, if it is tracked by lots.

+

Table of contents

+ +
+

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

+
    +
  • ForgeFlow
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

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

+

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

+
+
+
+
+ + diff --git a/project_task_product_stock/tests/__init__.py b/project_task_product_stock/tests/__init__.py new file mode 100644 index 0000000000..e8c77ed451 --- /dev/null +++ b/project_task_product_stock/tests/__init__.py @@ -0,0 +1 @@ +from . import test_project_task_product_stock diff --git a/project_task_product_stock/tests/test_project_task_product_stock.py b/project_task_product_stock/tests/test_project_task_product_stock.py new file mode 100644 index 0000000000..0efe33dd09 --- /dev/null +++ b/project_task_product_stock/tests/test_project_task_product_stock.py @@ -0,0 +1,46 @@ +# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import TransactionCase + + +class TestProjectTaskProductStock(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.product = cls.env["product.product"].create({"name": "Test Product"}) + cls.project = cls.env["project.project"].create( + { + "name": "Test Project", + } + ) + + def test_lot_task_count(self): + """Task count on stock.lot must count only tasks with correct product + lot.""" + product = self.product + lot = self.env["stock.lot"].create({"name": "Lot A", "product_id": product.id}) + + # Task with lot -> should count + self.env["project.task"].create( + {"name": "Task 1", "product_id": product.id, "lot_id": lot.id} + ) + + # Task without lot -> must not count + self.env["project.task"].create({"name": "Task 2", "product_id": product.id}) + + lot._compute_task_count() + self.assertEqual(lot.task_count, 1) + + def test_lot_action_view_tasks(self): + """Check action domain + context for stock.lot.""" + product = self.product + lot = self.env["stock.lot"].create({"name": "Lot A", "product_id": product.id}) + + action = lot.action_view_tasks() + + self.assertEqual( + action["domain"], [("product_id", "=", product.id), ("lot_id", "=", lot.id)] + ) + self.assertEqual(action["context"]["default_product_id"], product.id) + self.assertEqual(action["context"]["default_lot_id"], lot.id) diff --git a/project_task_product_stock/views/project_task_views.xml b/project_task_product_stock/views/project_task_views.xml new file mode 100644 index 0000000000..d8fb90cb59 --- /dev/null +++ b/project_task_product_stock/views/project_task_views.xml @@ -0,0 +1,17 @@ + + + + view.task.form2.inherit + project.task + + + + + + + + diff --git a/project_task_product_stock/views/stock_lot_views.xml b/project_task_product_stock/views/stock_lot_views.xml new file mode 100644 index 0000000000..a1e9fc1d9c --- /dev/null +++ b/project_task_product_stock/views/stock_lot_views.xml @@ -0,0 +1,21 @@ + + + + stock.lot.form.inherit.tasks + stock.lot + + + + + + + +