diff --git a/product_catalog_stock/README.rst b/product_catalog_stock/README.rst new file mode 100644 index 00000000000..520ee4344b4 --- /dev/null +++ b/product_catalog_stock/README.rst @@ -0,0 +1,94 @@ +===================== +Stock Product Catalog +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:e15b8a712eb3399c22281379449b50e2b307fe8076a721c03159ab634d3164a1 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fproduct--attribute-lightgray.png?logo=github + :target: https://github.com/OCA/product-attribute/tree/16.0/product_catalog_stock + :alt: OCA/product-attribute +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/product-attribute-16-0/product-attribute-16-0-product_catalog_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/product-attribute&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Implementation of the product catalog for stock pickings. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To access the catalog from a stock picking. + +1. Create a new draft picking. +2. Click on the product catalog smart button. +3. Click it and start adding products to the picking. + +Known issues / Roadmap +====================== + +- In v17 the product catalog has the price key as mandatory. So maybe + further adaptations will be needed to support more models. + +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 +------- + +* Odoo SA +* Tecnativa + +Contributors +------------ + +- `Tecnativa `__ + + - David Vidal + +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/product-attribute `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/product_catalog_stock/__init__.py b/product_catalog_stock/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/product_catalog_stock/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/product_catalog_stock/__manifest__.py b/product_catalog_stock/__manifest__.py new file mode 100644 index 00000000000..ab9c871bae9 --- /dev/null +++ b/product_catalog_stock/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2024 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "Stock Product Catalog", + "summary": "Use the product catalog on stock pickings", + "version": "16.0.1.0.0", + "author": "Odoo SA, Tecnativa, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/product-attribute", + "license": "AGPL-3", + "category": "Product", + "depends": ["stock", "product_catalog"], + "data": [ + "views/stock_picking_views.xml", + "views/stock_picking_type_views.xml", + ], +} diff --git a/product_catalog_stock/models/__init__.py b/product_catalog_stock/models/__init__.py new file mode 100644 index 00000000000..dae0bb2efe1 --- /dev/null +++ b/product_catalog_stock/models/__init__.py @@ -0,0 +1,2 @@ +from . import stock_picking +from . import stock_picking_type diff --git a/product_catalog_stock/models/stock_picking.py b/product_catalog_stock/models/stock_picking.py new file mode 100644 index 00000000000..c89557c68e3 --- /dev/null +++ b/product_catalog_stock/models/stock_picking.py @@ -0,0 +1,152 @@ +# Copyright 2024 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from collections import defaultdict + +from odoo import _, api, models +from odoo.osv import expression + + +class StockPicking(models.Model): + _name = "stock.picking" + _inherit = ["stock.picking", "product.catalog.mixin"] + + def _compute_catalog_button_text(self): + self.catalog_button_text = _("Back to picking") + + def _get_action_add_from_catalog_extra_context(self): + return { + **super()._get_action_add_from_catalog_extra_context(), + "order_id": self.id, + } + + def _default_order_line_values(self): + default_data = super()._default_order_line_values() + new_default_data = self.env["stock.move"]._get_product_catalog_lines_data() + return {**default_data, **new_default_data} + + def _get_product_catalog_domain(self): + return expression.AND( + [ + super()._get_product_catalog_domain(), + [("type", "in", ["consu", "product"])], + ] + ) + + def _get_product_catalog_record_lines(self, product_ids): + grouped_moves = defaultdict(lambda: self.env["stock.move"]) + for move in self.move_ids: + if move.product_id.id not in product_ids: + continue + grouped_moves[move.product_id] |= move + return grouped_moves + + @api.model + def _prepare_stock_move_vals_from_catalog(self, product_id, quantity): + self.ensure_one() + product_id = self.env["product.product"].browse(product_id) + return { + "name": product_id.display_name, + "product_id": product_id.id, + "product_uom_qty": quantity, + "product_uom": product_id.uom_id.id, + "location_id": self.location_id.id, + "location_dest_id": self.location_dest_id.id, + "picking_id": self.id, + "state": self.state, + "picking_type_id": self.picking_type_id.id, + "restrict_partner_id": self.owner_id.id, + "company_id": self.company_id.id, + "partner_id": self.partner_id.id, + # Put it at the end of the order + "sequence": ((self.move_ids and self.move_ids[-1].sequence + 1) or 10), + } + + def _update_order_line_info(self, product_id, quantity, **kwargs): + """Update stock move information for a given product or create a + new one if none exists yet. + :param int product_id: The product, as a `product.product` id. + :return: There's no price unit so we return always None show nothing is shown + :rtype: None + """ + move = self.move_ids.filtered(lambda move: move.product_id.id == product_id) + if move: + if quantity != 0: + move.product_uom_qty = quantity + elif self.state == "draft": + move.unlink() + return None + else: + move.product_uom_qty = 0 + elif quantity > 0: + move = self.env["stock.move"].create( + self._prepare_stock_move_vals_from_catalog(product_id, quantity) + ) + return None + + def _is_readonly(self): + """Return Whether the sale order is read-only or not based on the state or the + lock status. + + A sale order is considered read-only if its state is 'cancel' or if the sale + order is locked. + + :return: Whether the sale order is read-only or not. + :rtype: bool + """ + self.ensure_one() + return self.state in ["cancel", "done"] or self.is_locked + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _get_product_catalog_lines_data(self, **kwargs): + """Return information about sale order lines in `self`. + + If `self` is empty, this method returns only the default value(s) needed for + the product catalog. In this case, the quantity that equals 0. + + Otherwise, it returns a quantity and a price based on the product of the SOL(s) + and whether the product is read-only or not. + + A product is considered read-only if the picking is cancelled or done. + + Note: This method cannot be called with multiple records that have different + products linked. + + :raise odoo.exceptions.ValueError: ``len(self.product_id) != 1`` + :rtype: dict + :return: A dict with the following structure: + { + 'quantity': float, + 'readOnly': bool, + } + """ + if len(self) == 1: + res = { + "quantity": self.product_uom_qty, + "readOnly": self.picking_id.state in ["cancel", "done"], + } + return res + elif self: + self.product_id.ensure_one() + res = { + "readOnly": True, + "quantity": sum( + self.mapped( + lambda move: move.product_uom._compute_quantity( + qty=move.product_uom_qty, + to_unit=move.product_id.uom_id, + ) + ) + ), + } + return res + else: + return { + "quantity": 0, + } + + def action_add_from_catalog(self): + picking = self.env["stock.picking"].browse(self.env.context.get("order_id")) + return picking.action_add_from_catalog() diff --git a/product_catalog_stock/models/stock_picking_type.py b/product_catalog_stock/models/stock_picking_type.py new file mode 100644 index 00000000000..7c71545330a --- /dev/null +++ b/product_catalog_stock/models/stock_picking_type.py @@ -0,0 +1,24 @@ +# Copyright 2024 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import models +from odoo.tests import Form + + +class StockPickingType(models.Model): + _inherit = "stock.picking.type" + + def action_new_draft_picking_from_catalog(self): + """Create a new draft picking from the catalog view""" + picking_form = Form( + self.env["stock.picking"].with_context( + search_default_picking_type_id=self.ids, + default_picking_type_id=self.id, + contact_display="partner_address", + ) + ) + picking = picking_form.save() + action = picking.action_add_from_catalog() + # So we can go back safely to the new picking instead of returning to the + # previous screen + action["target"] = "main" + return action diff --git a/product_catalog_stock/readme/CONTRIBUTORS.md b/product_catalog_stock/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..3a16ddf047a --- /dev/null +++ b/product_catalog_stock/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [Tecnativa](https://tecnativa.com) + - David Vidal diff --git a/product_catalog_stock/readme/DESCRIPTION.md b/product_catalog_stock/readme/DESCRIPTION.md new file mode 100644 index 00000000000..05b4ead465d --- /dev/null +++ b/product_catalog_stock/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Implementation of the product catalog for stock pickings. diff --git a/product_catalog_stock/readme/ROADMAP.md b/product_catalog_stock/readme/ROADMAP.md new file mode 100644 index 00000000000..979a4f72fa0 --- /dev/null +++ b/product_catalog_stock/readme/ROADMAP.md @@ -0,0 +1,2 @@ +- In v17 the product catalog has the price key as mandatory. So maybe further + adaptations will be needed to support more models. diff --git a/product_catalog_stock/readme/USAGE.md b/product_catalog_stock/readme/USAGE.md new file mode 100644 index 00000000000..1e4a0581aff --- /dev/null +++ b/product_catalog_stock/readme/USAGE.md @@ -0,0 +1,5 @@ +To access the catalog from a stock picking. + +1. Create a new draft picking. +2. Click on the product catalog smart button. +3. Click it and start adding products to the picking. diff --git a/product_catalog_stock/static/description/index.html b/product_catalog_stock/static/description/index.html new file mode 100644 index 00000000000..7abca3691f2 --- /dev/null +++ b/product_catalog_stock/static/description/index.html @@ -0,0 +1,445 @@ + + + + + +Stock Product Catalog + + + +
+

Stock Product Catalog

+ + +

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

+

Implementation of the product catalog for stock pickings.

+

Table of contents

+ +
+

Usage

+

To access the catalog from a stock picking.

+
    +
  1. Create a new draft picking.
  2. +
  3. Click on the product catalog smart button.
  4. +
  5. Click it and start adding products to the picking.
  6. +
+
+
+

Known issues / Roadmap

+
    +
  • In v17 the product catalog has the price key as mandatory. So maybe +further adaptations will be needed to support more models.
  • +
+
+
+

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

+
    +
  • Odoo SA
  • +
  • Tecnativa
  • +
+
+
+

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

+

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

+
+
+
+ + diff --git a/product_catalog_stock/views/stock_picking_type_views.xml b/product_catalog_stock/views/stock_picking_type_views.xml new file mode 100644 index 00000000000..be8aee77907 --- /dev/null +++ b/product_catalog_stock/views/stock_picking_type_views.xml @@ -0,0 +1,17 @@ + + + + stock.picking.type + + + + + + + + diff --git a/product_catalog_stock/views/stock_picking_views.xml b/product_catalog_stock/views/stock_picking_views.xml new file mode 100644 index 00000000000..db9bada6bf7 --- /dev/null +++ b/product_catalog_stock/views/stock_picking_views.xml @@ -0,0 +1,19 @@ + + + + stock.picking + + +
+
+
+
+
diff --git a/setup/product_catalog_stock/odoo/addons/product_catalog_stock b/setup/product_catalog_stock/odoo/addons/product_catalog_stock new file mode 120000 index 00000000000..770111bf036 --- /dev/null +++ b/setup/product_catalog_stock/odoo/addons/product_catalog_stock @@ -0,0 +1 @@ +../../../../product_catalog_stock \ No newline at end of file diff --git a/setup/product_catalog_stock/setup.py b/setup/product_catalog_stock/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/product_catalog_stock/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)