diff --git a/lims_purchase/README.rst b/lims_purchase/README.rst new file mode 100644 index 0000000..550fd73 --- /dev/null +++ b/lims_purchase/README.rst @@ -0,0 +1,105 @@ +========================= +LIMS Purchase Integration +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:7f9de5ed863d2f67657f763651bbb8e1770c27dcf8e277c0ef707c03cc054f43 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fconnector--lims-lightgray.png?logo=github + :target: https://github.com/OCA/connector-lims/tree/18.0/lims_purchase + :alt: OCA/connector-lims +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/connector-lims-18-0/connector-lims-18-0-lims_purchase + :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/connector-lims&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +========================== LIMS Purchase Integration +==================================================== + +This module integrates **Odoo LIMS** with **Purchase Orders**, allowing +laboratories to sell LIMS services directly as products. + +Link a LIMS order to a purchase order + +This module extends the ``lims`` and ``purchase`` modules. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +========================== LIMS Purchase Integration +==================================================== + +This module integrates **Odoo LIMS** with **Purchase Orders**, allowing +laboratories to sell LIMS services directly as products. + +Link a LIMS order to a purchase order + +This module extends the ``lims`` and ``purchase`` modules. + +Usage +===== + +Usage +===== + +Link a LIMS order to a purchase order + +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 +------- + +* Open Source Integrators + +Contributors +------------ + +- Hardik-OSI hsuthar@opensourceintegrators.com + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/connector-lims `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/lims_purchase/__init__.py b/lims_purchase/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/lims_purchase/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/lims_purchase/__manifest__.py b/lims_purchase/__manifest__.py new file mode 100644 index 0000000..226e4e8 --- /dev/null +++ b/lims_purchase/__manifest__.py @@ -0,0 +1,16 @@ +{ + "name": "LIMS Purchase Integration", + "version": "18.0.1.0.0", + "depends": ["purchase", "lims"], + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/connector-lims", + "category": "LIMS", + "summary": "Link LIMS orders and Purchase Orders; Smart buttons for navigation", + "license": "AGPL-3", + "data": [ + "views/purchase_order_views.xml", + "views/lims_order_views.xml", + ], + "installable": True, + "application": False, +} diff --git a/lims_purchase/models/__init__.py b/lims_purchase/models/__init__.py new file mode 100644 index 0000000..9726b47 --- /dev/null +++ b/lims_purchase/models/__init__.py @@ -0,0 +1,2 @@ +from . import purchase_order +from . import lims_order diff --git a/lims_purchase/models/lims_order.py b/lims_purchase/models/lims_order.py new file mode 100644 index 0000000..a16e005 --- /dev/null +++ b/lims_purchase/models/lims_order.py @@ -0,0 +1,63 @@ +from odoo import fields, models + + +class LIMSOrder(models.Model): + _inherit = "lims.order" + + purchase_order_ids = fields.One2many( + "purchase.order", + "lims_order_id", + string="Purchase Orders", + help="Purchase Orders created/linked for this LIMS Order", + ) + + purchase_order_count = fields.Integer( + string="PO Count", + compute="_compute_purchase_order_count", + ) + + purchase_order_m2m_ids = fields.Many2many( + "purchase.order", + "lims_order_purchase_rel", + "lims_order_id", + "purchase_order_id", + string="Linked Purchase Orders (M2M)", + help="Attach existing Purchase Orders that relate to this LIMS Order.", + ) + + def _compute_purchase_order_count(self): + for rec in self: + # read_group is faster in bulk but simple search_count is fine here + rec.purchase_order_count = self.env["purchase.order"].search_count( + [("lims_order_id", "=", rec.id)] + ) + + def action_view_purchase_orders(self): + """Open purchase orders linked to this LIMS order.""" + self.ensure_one() + purchase_orders = self.purchase_order_ids + + # Use the standard Purchase action (the one that lists RFQs/POs) + action = self.env.ref("purchase.purchase_rfq").read()[0] + + # If no POs, open the empty list + if not purchase_orders: + action["domain"] = [("id", "in", [])] + return action + + # If only one PO → open form view directly + if len(purchase_orders) == 1: + form_view = self.env.ref("purchase.purchase_order_form", False) + action.update( + { + "view_mode": "form", + "views": [(form_view.id, "form")] if form_view else [], + "res_id": purchase_orders.id, + "domain": [], + } + ) + return action + + # Multiple → show list view filtered + action["domain"] = [("id", "in", purchase_orders.ids)] + return action diff --git a/lims_purchase/models/purchase_order.py b/lims_purchase/models/purchase_order.py new file mode 100644 index 0000000..9c71d39 --- /dev/null +++ b/lims_purchase/models/purchase_order.py @@ -0,0 +1,38 @@ +from odoo import fields, models + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + lims_order_id = fields.Many2one( + "lims.order", + string="LIMS Order", + ondelete="set null", + help="Link to the LIMS Order related to this Purchase Order", + ) + + def action_open_linked_lims_order(self): + """ + Smart-button action: open the related LIMS Order. + If no lims_order_id is set, return an action to search for none (or raise). + """ + self.ensure_one() + if not self.lims_order_id: + # Best UX: return an empty tree view filtered to nothing + action = self.env.ref("lims.action_lims_operation_order").read()[0] + action["domain"] = [("id", "in", [])] + return action + + # If you want to open the form directly: + action = self.env.ref("lims.action_lims_operation_order").read()[0] + # If the action supports views, prefer to open the form for the single record + action.update( + { + "views": [(self.env.ref("lims.lims_order_form").id, "form")] + if self.env.ref("lims.lims_order_form", False) + else action.get("views"), + "res_id": self.lims_order_id.id, + "target": "current", + } + ) + return action diff --git a/lims_purchase/pyproject.toml b/lims_purchase/pyproject.toml new file mode 100644 index 0000000..4231d0c --- /dev/null +++ b/lims_purchase/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/lims_purchase/readme/CONFIGURE.md b/lims_purchase/readme/CONFIGURE.md new file mode 100644 index 0000000..3e7cb31 --- /dev/null +++ b/lims_purchase/readme/CONFIGURE.md @@ -0,0 +1,10 @@ +========================== +LIMS Purchase Integration +========================== + +This module integrates **Odoo LIMS** with **Purchase Orders**, allowing laboratories +to sell LIMS services directly as products. + +Link a LIMS order to a purchase order + +This module extends the `lims` and `purchase` modules. diff --git a/lims_purchase/readme/CONTRIBUTORS.md b/lims_purchase/readme/CONTRIBUTORS.md new file mode 100644 index 0000000..e04d19d --- /dev/null +++ b/lims_purchase/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +* Hardik-OSI \ No newline at end of file diff --git a/lims_purchase/readme/DESCRIPTION.md b/lims_purchase/readme/DESCRIPTION.md new file mode 100644 index 0000000..3e7cb31 --- /dev/null +++ b/lims_purchase/readme/DESCRIPTION.md @@ -0,0 +1,10 @@ +========================== +LIMS Purchase Integration +========================== + +This module integrates **Odoo LIMS** with **Purchase Orders**, allowing laboratories +to sell LIMS services directly as products. + +Link a LIMS order to a purchase order + +This module extends the `lims` and `purchase` modules. diff --git a/lims_purchase/readme/USAGE.md b/lims_purchase/readme/USAGE.md new file mode 100644 index 0000000..5067610 --- /dev/null +++ b/lims_purchase/readme/USAGE.md @@ -0,0 +1,5 @@ +Usage +===== + +Link a LIMS order to a purchase order + diff --git a/lims_purchase/static/description/index.html b/lims_purchase/static/description/index.html new file mode 100644 index 0000000..b18b585 --- /dev/null +++ b/lims_purchase/static/description/index.html @@ -0,0 +1,435 @@ + + + + + +LIMS Purchase Integration + + + +
+

LIMS Purchase Integration

+ + +

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

+
+

========================== LIMS Purchase Integration

+

This module integrates Odoo LIMS with Purchase Orders, allowing +laboratories to sell LIMS services directly as products.

+

Link a LIMS order to a purchase order

+

This module extends the lims and purchase modules.

+

Table of contents

+
+
+

Configuration

+
+
+

========================== LIMS Purchase Integration

+

This module integrates Odoo LIMS with Purchase Orders, allowing +laboratories to sell LIMS services directly as products.

+

Link a LIMS order to a purchase order

+

This module extends the lims and purchase modules.

+
+
+

Usage

+
+
+

Usage

+

Link a LIMS order to a purchase order

+
+
+

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

+
    +
  • Open Source Integrators
  • +
+
+
+

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

+

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

+
+
+
+ + diff --git a/lims_purchase/tests/__init__.py b/lims_purchase/tests/__init__.py new file mode 100644 index 0000000..790d0dc --- /dev/null +++ b/lims_purchase/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2025 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_purchase_order diff --git a/lims_purchase/tests/test_purchase_order.py b/lims_purchase/tests/test_purchase_order.py new file mode 100644 index 0000000..a2566f5 --- /dev/null +++ b/lims_purchase/tests/test_purchase_order.py @@ -0,0 +1,86 @@ +# Copyright (C) 2025 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields +from odoo.fields import Command + +from odoo.addons.lims.tests.common import LIMSCommon + + +class TestLIMSPurchaseOrder(LIMSCommon): + def setUp(self): + super().setUp() + self.LimsOrder = self.env["lims.order"] + + # Sample partner for test orders + self.partner_id = self.env.ref("base.res_partner_12", raise_if_not_found=False) + self.stage_new = self.env.ref( + "lims.lims_stage_order_new", raise_if_not_found=False + ) + self.service_product = self.env["product.product"].search([], limit=1) + + # Create LIMS order + self.lims_order = self.LimsOrder.create( + { + "name": "Test Order", + "stage_id": self.stage_new.id, + "company_id": self.env.company.id, + "laboratory_id": self.partner_laboratory.id, + "team_id": self.lims_team.id, + "partner_id": self.partner_id.id, + "tag_ids": [(6, 0, [self.lims_tag.id])], + "description": "Test Order For UT", + "operator_id": self.partner_operator.id, + "physician_id": self.partner_physician.id, + "specimen_id": self.lims_specimen.id, + "scheduled_date": fields.Date.today(), + "date": fields.Date.today(), + "test_ids": [ + Command.create( + { + "test_id": self.test_id.id, + "operator_id": self.partner_operator.id, + "scheduled_date": fields.Date.today(), + "date": fields.Date.today(), + "todo": "Test TODO", + } + ) + ], + "purchase_order_ids": [ + Command.create( + { + "partner_id": self.partner_id.id, + "order_line": [ + Command.create( + { + "product_id": self.service_product.id, + "product_uom_qty": 1, + } + ), + ], + } + ) + ], + } + ) + + def test_01_purchase_order_linkage(self): + """Ensure purchase order is correctly linked to LIMS order.""" + lims_order = self.lims_order + + self.assertTrue( + lims_order.purchase_order_ids, + "LIMS order should have linked purchase orders.", + ) + + # Simulate UI action from purchase order to open related LIMS order + lims_order.purchase_order_ids.action_open_linked_lims_order() + + # Compute and verify the purchase order count + lims_order._compute_purchase_order_count() + self.assertTrue( + lims_order.purchase_order_count, + "LIMS order should correctly compute the purchase_order_count field.", + ) + + # Simulate action to open purchase orders from LIMS order + lims_order.action_view_purchase_orders() diff --git a/lims_purchase/views/lims_order_views.xml b/lims_purchase/views/lims_order_views.xml new file mode 100644 index 0000000..50b8d17 --- /dev/null +++ b/lims_purchase/views/lims_order_views.xml @@ -0,0 +1,57 @@ + + + + lims.order.form.purchase.button + lims.order + + + + + + + + + + + lims.order.form.add.purchase.tab + lims.order + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+
+
+
+
diff --git a/lims_purchase/views/purchase_order_views.xml b/lims_purchase/views/purchase_order_views.xml new file mode 100644 index 0000000..cdeb2a0 --- /dev/null +++ b/lims_purchase/views/purchase_order_views.xml @@ -0,0 +1,31 @@ + + + + purchase.order.form.lims + purchase.order + + + + + + + + + + + + + + diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..fef89ce --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1 @@ +odoo-addon-lims @ git+https://github.com/OCA/connector-lims.git@refs/pull/20/head#subdirectory=lims