diff --git a/product_catalog_stock/README.rst b/product_catalog_stock/README.rst new file mode 100644 index 00000000000..713601bcfe4 --- /dev/null +++ b/product_catalog_stock/README.rst @@ -0,0 +1,96 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +===================== +Stock Product Catalog +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c2c9b703c7c3de458cf84b29cb464f90f1eb9e342e8e6fe6fc31dd9bac0163d3 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fproduct--attribute-lightgray.png?logo=github + :target: https://github.com/OCA/product-attribute/tree/19.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-19-0/product-attribute-19-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=19.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 button. +3. Click it and start adding products to the picking. + +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 `__ + + - David Vidal + - Víctor Martínez + +- `Heliconia Solutions Pvt. Ltd. `__ + + - Bhavesh Heliconia + +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..e4250ad5584 --- /dev/null +++ b/product_catalog_stock/__manifest__.py @@ -0,0 +1,21 @@ +# 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": "19.0.1.0.0", + "author": "Tecnativa, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/product-attribute", + "license": "AGPL-3", + "category": "Product", + "depends": ["stock"], + "data": [ + "views/stock_picking_views.xml", + "views/stock_picking_type_views.xml", + ], + "assets": { + "web.assets_backend": [ + "product_catalog_stock/static/src/**/*", + ], + }, +} diff --git a/product_catalog_stock/i18n/it.po b/product_catalog_stock/i18n/it.po new file mode 100644 index 00000000000..763a1724079 --- /dev/null +++ b/product_catalog_stock/i18n/it.po @@ -0,0 +1,54 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_catalog_stock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-05-06 12:24+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.10.4\n" + +#. module: product_catalog_stock +#. odoo-python +#: code:addons/product_catalog_stock/models/stock_picking.py:0 +#, python-format +msgid "Back to picking" +msgstr "Torna al prelievo" + +#. module: product_catalog_stock +#: model_terms:ir.ui.view,arch_db:product_catalog_stock.view_picking_form +msgid "Catalog" +msgstr "Catalogo" + +#. module: product_catalog_stock +#: model:ir.model.fields,field_description:product_catalog_stock.field_stock_picking__catalog_button_text +msgid "Catalog Button Text" +msgstr "Testo pulsante catalogo" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_picking_type +msgid "Picking Type" +msgstr "Tipo prelievo" + +#. module: product_catalog_stock +#: model_terms:ir.ui.view,arch_db:product_catalog_stock.stock_picking_type_kanban +msgid "Planned transfer from catalog" +msgstr "Trasferimento pianificato da catalogo" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_move +msgid "Stock Move" +msgstr "Movimento di magazzino" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_picking +msgid "Transfer" +msgstr "Trasferimento" diff --git a/product_catalog_stock/i18n/nl.po b/product_catalog_stock/i18n/nl.po new file mode 100644 index 00000000000..a1321eea4ab --- /dev/null +++ b/product_catalog_stock/i18n/nl.po @@ -0,0 +1,53 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_catalog_stock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-09-11 14:42+0000\n" +"Last-Translator: Bosd \n" +"Language-Team: none\n" +"Language: nl\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.10.4\n" + +#. module: product_catalog_stock +#: model_terms:ir.ui.view,arch_db:product_catalog_stock.view_picking_form +msgid "Add a line" +msgstr "Een regel toevoegen" + +#. module: product_catalog_stock +#. odoo-javascript +#: code:addons/product_catalog_stock/static/src/components/product_catalog/kanban_controller.esm.js:0 +msgid "Back to picking" +msgstr "Terug naar verplaatsing" + +#. module: product_catalog_stock +#: model_terms:ir.ui.view,arch_db:product_catalog_stock.view_picking_form +msgid "Catalog" +msgstr "Catalogus" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_picking_type +msgid "Picking Type" +msgstr "Type verplaatsing" + +#. module: product_catalog_stock +#: model_terms:ir.ui.view,arch_db:product_catalog_stock.stock_picking_type_kanban +msgid "Planned transfer from catalog" +msgstr "Geplande verplaatsing vanuit catalogus" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_move +msgid "Stock Move" +msgstr "Voorraadverplaatsing" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_picking +msgid "Transfer" +msgstr "Verplaatsing" diff --git a/product_catalog_stock/i18n/product_catalog_stock.pot b/product_catalog_stock/i18n/product_catalog_stock.pot new file mode 100644 index 00000000000..6826b059655 --- /dev/null +++ b/product_catalog_stock/i18n/product_catalog_stock.pot @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_catalog_stock +# +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: product_catalog_stock +#: model_terms:ir.ui.view,arch_db:product_catalog_stock.view_picking_form +msgid "Add a line" +msgstr "" + +#. module: product_catalog_stock +#. odoo-javascript +#: code:addons/product_catalog_stock/static/src/components/product_catalog/kanban_controller.esm.js:0 +msgid "Back to picking" +msgstr "" + +#. module: product_catalog_stock +#: model_terms:ir.ui.view,arch_db:product_catalog_stock.view_picking_form +msgid "Catalog" +msgstr "" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: product_catalog_stock +#: model_terms:ir.ui.view,arch_db:product_catalog_stock.stock_picking_type_kanban +msgid "Planned transfer from catalog" +msgstr "" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: product_catalog_stock +#: model:ir.model,name:product_catalog_stock.model_stock_picking +msgid "Transfer" +msgstr "" 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..9a59e677f11 --- /dev/null +++ b/product_catalog_stock/models/stock_picking.py @@ -0,0 +1,114 @@ +# 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.fields import Domain + + +class StockPicking(models.Model): + _name = "stock.picking" + _inherit = ["stock.picking", "product.catalog.mixin"] + + 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, child_field=False): + default_data = super()._default_order_line_values(child_field) + new_default_data = self.env["stock.move"]._get_product_catalog_lines_data( + parent_record=self + ) + return {**default_data, **new_default_data} + + def _get_product_catalog_domain(self): + return super()._get_product_catalog_domain() & Domain("type", "!=", "service") + + def _get_product_catalog_record_lines(self, product_ids, **kwargs): + 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 + + def _get_product_catalog_order_data(self, products, **kwargs): + # Extended to add price avoiding the Component.validateProps check error + product_catalog = super()._get_product_catalog_order_data(products, **kwargs) + for product in products: + product_catalog[product.id] |= self._get_product_price_and_data(product) + return product_catalog + + def _get_product_price_and_data(self, product): + self.ensure_one() + return {"price": product.list_price} + + @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 { + "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() + 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 self.env["product.product"].browse(product_id).list_price + + 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"] + + +class StockMove(models.Model): + _inherit = "stock.move" + + def action_add_from_catalog_picking(self): + picking = self.env["stock.picking"].browse(self.env.context.get("order_id")) + return picking.action_add_from_catalog() + + def _get_product_catalog_lines_data(self, parent_record=False, **kwargs): + data = super()._get_product_catalog_lines_data(parent_record, **kwargs) + if "readOnly" in data: + data["readOnly"] = self.picking_id._is_readonly() + return data 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..c132dbcc2af --- /dev/null +++ b/product_catalog_stock/models/stock_picking_type.py @@ -0,0 +1,16 @@ +# Copyright 2024 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import models + + +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 = self.env["stock.picking"].create({"picking_type_id": self.id}) + 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/pyproject.toml b/product_catalog_stock/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/product_catalog_stock/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/product_catalog_stock/readme/CONTRIBUTORS.md b/product_catalog_stock/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..9b6f2d2751d --- /dev/null +++ b/product_catalog_stock/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- [Tecnativa](https://tecnativa.com) + - David Vidal + - Víctor Martínez +- [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io) + - Bhavesh Heliconia 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/USAGE.md b/product_catalog_stock/readme/USAGE.md new file mode 100644 index 00000000000..ab96d4285cc --- /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 button. +3. Click it and start adding products to the picking. diff --git a/product_catalog_stock/static/description/icon.png b/product_catalog_stock/static/description/icon.png new file mode 100644 index 00000000000..1dcc49c24f3 Binary files /dev/null and b/product_catalog_stock/static/description/icon.png differ diff --git a/product_catalog_stock/static/description/index.html b/product_catalog_stock/static/description/index.html new file mode 100644 index 00000000000..ad8f4cd9c8d --- /dev/null +++ b/product_catalog_stock/static/description/index.html @@ -0,0 +1,447 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

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 button.
  4. +
  5. Click it and start adding products to the picking.
  6. +
+
+
+

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

+ +
+
+

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/static/src/components/product_catalog/kanban_controller.esm.js b/product_catalog_stock/static/src/components/product_catalog/kanban_controller.esm.js new file mode 100644 index 00000000000..5ab5ada5045 --- /dev/null +++ b/product_catalog_stock/static/src/components/product_catalog/kanban_controller.esm.js @@ -0,0 +1,13 @@ +import {_t} from "@web/core/l10n/translation"; +import {ProductCatalogKanbanController} from "@product/product_catalog/kanban_controller"; +import {patch} from "@web/core/utils/patch"; + +patch(ProductCatalogKanbanController.prototype, { + async _defineButtonContent() { + if (this.orderResModel === "stock.picking") { + this.buttonString = _t("Back to picking"); + } else { + super._defineButtonContent(); + } + }, +}); diff --git a/product_catalog_stock/static/src/components/product_catalog/order_line.esm.js b/product_catalog_stock/static/src/components/product_catalog/order_line.esm.js new file mode 100644 index 00000000000..1ee29dc402f --- /dev/null +++ b/product_catalog_stock/static/src/components/product_catalog/order_line.esm.js @@ -0,0 +1,8 @@ +import {ProductCatalogOrderLine} from "@product/product_catalog/order_line/order_line"; +import {patch} from "@web/core/utils/patch"; + +patch(ProductCatalogOrderLine.prototype, { + get showPrice() { + return super.showPrice && this.env.orderResModel !== "stock.picking"; + }, +}); 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..ea6529f47a3 --- /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..ae99d014363 --- /dev/null +++ b/product_catalog_stock/views/stock_picking_views.xml @@ -0,0 +1,22 @@ + + + + stock.picking + + + + + +