diff --git a/pos_stock_available_online/README.rst b/pos_stock_available_online/README.rst new file mode 100644 index 0000000000..08bc56082c --- /dev/null +++ b/pos_stock_available_online/README.rst @@ -0,0 +1,119 @@ +==================================== +Point of Sale Stock Available Online +==================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:d544301c362e728a3cab17fa70bc08796bb02c4435dd0edf4ed2df4a4376d1c1 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fpos-lightgray.png?logo=github + :target: https://github.com/OCA/pos/tree/18.0/pos_stock_available_online + :alt: OCA/pos +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/pos-18-0/pos-18-0-pos_stock_available_online + :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/pos&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to display product quantities in selected locations +in real time. Quantities are displayed directly on product tiles: |Pos +Quantity| + +Once a product quantity is changed it will be simultaneously updated in +all active POS. + +This module depends on stock_available module which is available in +https://github.com/OCA/stock-logistics-availability repo. + +.. |Pos Quantity| image:: https://raw.githubusercontent.com/OCA/pos/18.0/pos_stock_available_online/static/img/pos_quantity.png + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +In "Point of Sale" configuration "Product Quantity" section activate +"Display Product Quantity" feature: + +|Pos Config| + +By default quantity is displayed for the warehouse that is used in the +POS stock operation type. + +You can add additional warehouses to show quantity in by adding them +into "Additional Warehouses" field. + +In this case the following information will be displayed on product +tiles: + +- Total quantity = quantity in the default warehouse + quantity in the + additional warehouses +- Quantity in the default warehouse +- Quantity in the additional warehouses. + +.. |Pos Config| image:: https://raw.githubusercontent.com/OCA/pos/18.0/pos_stock_available_online/static/img/pos_config.png + +Known issues / Roadmap +====================== + +This module requires connection to update quantities and doesn't support +offline mode. Warehouses must belong to the same company as POS. Offline +mode support (probably additional module). + +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 +------- + +* Cetmix + +Contributors +------------ + +- Cetmix <`https://cetmix.com/\\> >`__ +- Dinar Gabbasov +- Nikul Chaudhary + +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/pos `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/pos_stock_available_online/__init__.py b/pos_stock_available_online/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/pos_stock_available_online/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/pos_stock_available_online/__manifest__.py b/pos_stock_available_online/__manifest__.py new file mode 100644 index 0000000000..3fbbcca27f --- /dev/null +++ b/pos_stock_available_online/__manifest__.py @@ -0,0 +1,23 @@ +{ + "name": "Point of Sale Stock Available Online", + "version": "18.0.1.0.0", + "category": "Sales/Point of Sale", + "summary": "Show the available quantity of products in the Point of Sale ", + "depends": [ + "point_of_sale", + "stock_available", + "base_automation", + ], + "website": "https://github.com/OCA/pos", + "author": "Cetmix, Odoo Community Association (OCA)", + "images": ["static/description/banner.png"], + "installable": True, + "data": ["views/res_config_settings_view.xml"], + "assets": { + # customer_display_assets + "point_of_sale._assets_pos": [ + "pos_stock_available_online/static/src/app/**/*", + ], + }, + "license": "AGPL-3", +} diff --git a/pos_stock_available_online/i18n/es.po b/pos_stock_available_online/i18n/es.po new file mode 100644 index 0000000000..79ec9a561b --- /dev/null +++ b/pos_stock_available_online/i18n/es.po @@ -0,0 +1,126 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_stock_available_online +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-10-24 11:06+0000\n" +"Last-Translator: Daniel Duque \n" +"Language-Team: none\n" +"Language: es\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: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__additional_warehouse_ids +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_additional_warehouse_ids +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Additional Warehouses" +msgstr "Almacenes Adicionales" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_res_config_settings +msgid "Config Settings" +msgstr "Ajustes de Configuración" + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__display_product_quantity +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_display_product_quantity +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Display Product Quantity" +msgstr "Mostrar Cantidad de Productos" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Display the quantity of available products in the POS for" +msgstr "Mostrar la cantidad de productos disponibles en el TPV para" + +#. module: pos_stock_available_online +#: model:ir.model.fields,help:pos_stock_available_online.field_pos_config__additional_warehouse_ids +#: model:ir.model.fields,help:pos_stock_available_online.field_res_config_settings__pos_additional_warehouse_ids +msgid "" +"For the selected warehouses will be displayed quantity of available products " +"in the POS" +msgstr "" +"Para los almacenes seleccionados se mostrará la cantidad de productos " +"disponibles en el TPV" + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__main_warehouse_id +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_main_warehouse_id +msgid "Main Warehouse" +msgstr "" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Mark quantity red if below or equal this value." +msgstr "Marque la cantidad en rojo si es inferior o igual a este valor." + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__minimum_product_quantity_alert +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_minimum_product_quantity_alert +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Minimum Product Quantity Alert" +msgstr "Alerta de Cantidad Mínima de Producto" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_pos_config +msgid "Point of Sale Configuration" +msgstr "Configuración del Punto de Venta" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_pos_session +msgid "Point of Sale Session" +msgstr "Sesiones del Punto de Venta" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Product Quantity" +msgstr "Cantidad de Productos" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_quant +msgid "Quants" +msgstr "Quants" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Show quantity in the following warehouses in additional to" +msgstr "Indicar la cantidad en los siguientes almacenes además de" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_notifier_pos_mixin +msgid "Stock Notifier POS Mixin" +msgstr "" + +#. module: pos_stock_available_online +#. odoo-javascript +#: code:addons/pos_stock_available_online/static/src/xml/Screens/ProductScreen/ProductItem.xml:0 +#, python-format +msgid "Total" +msgstr "Total" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_warehouse +msgid "Warehouse" +msgstr "Almacén" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Warehouses must belong to the same company as POS." +msgstr "Los almacenes deben pertenecer a la misma compañía que el TPV." + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "warehouse" +msgstr "almacén" diff --git a/pos_stock_available_online/i18n/it.po b/pos_stock_available_online/i18n/it.po new file mode 100644 index 0000000000..bca2a20fd1 --- /dev/null +++ b/pos_stock_available_online/i18n/it.po @@ -0,0 +1,126 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_stock_available_online +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-02-21 18:06+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: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__additional_warehouse_ids +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_additional_warehouse_ids +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Additional Warehouses" +msgstr "Magazzini aggiuntivi" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_res_config_settings +msgid "Config Settings" +msgstr "Impostazioni configurazione" + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__display_product_quantity +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_display_product_quantity +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Display Product Quantity" +msgstr "Visualizza quantità prdotto" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Display the quantity of available products in the POS for" +msgstr "Visualizza la quantità dei prodotti disponibili nel POS per" + +#. module: pos_stock_available_online +#: model:ir.model.fields,help:pos_stock_available_online.field_pos_config__additional_warehouse_ids +#: model:ir.model.fields,help:pos_stock_available_online.field_res_config_settings__pos_additional_warehouse_ids +msgid "" +"For the selected warehouses will be displayed quantity of available products " +"in the POS" +msgstr "" +"Per il magazzino selezionato verrà visualizzata nel POS la quantità " +"disponibile del prodotto" + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__main_warehouse_id +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_main_warehouse_id +msgid "Main Warehouse" +msgstr "Magazzino principale" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Mark quantity red if below or equal this value." +msgstr "Evidenzia in rosso la quantità se inferiore o uguale a questo valore." + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__minimum_product_quantity_alert +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_minimum_product_quantity_alert +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Minimum Product Quantity Alert" +msgstr "Allerta quantità minima prodotto" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_pos_config +msgid "Point of Sale Configuration" +msgstr "Configurazione punto vendita" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_pos_session +msgid "Point of Sale Session" +msgstr "Sessione punto vendita" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Product Quantity" +msgstr "Quantità prodotto" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_quant +msgid "Quants" +msgstr "Quanti" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Show quantity in the following warehouses in additional to" +msgstr "Mostra le quantità nei magazzini seguenti in aggiunta a" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_move +msgid "Stock Move" +msgstr "Movimento di magazzino" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_notifier_pos_mixin +msgid "Stock Notifier POS Mixin" +msgstr "Mixin POS avvisatore giacenza" + +#. module: pos_stock_available_online +#. odoo-javascript +#: code:addons/pos_stock_available_online/static/src/xml/Screens/ProductScreen/ProductItem.xml:0 +#, python-format +msgid "Total" +msgstr "Totale" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_warehouse +msgid "Warehouse" +msgstr "Magazzino" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Warehouses must belong to the same company as POS." +msgstr "Il magazzino deve appartenere alla stessa azienda del POS." + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "warehouse" +msgstr "magazzino" diff --git a/pos_stock_available_online/i18n/pos_stock_available_online.pot b/pos_stock_available_online/i18n/pos_stock_available_online.pot new file mode 100644 index 0000000000..6562ec70c8 --- /dev/null +++ b/pos_stock_available_online/i18n/pos_stock_available_online.pot @@ -0,0 +1,121 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_stock_available_online +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.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: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__additional_warehouse_ids +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_additional_warehouse_ids +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Additional Warehouses" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__display_product_quantity +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_display_product_quantity +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Display Product Quantity" +msgstr "" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Display the quantity of available products in the POS for" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model.fields,help:pos_stock_available_online.field_pos_config__additional_warehouse_ids +#: model:ir.model.fields,help:pos_stock_available_online.field_res_config_settings__pos_additional_warehouse_ids +msgid "" +"For the selected warehouses will be displayed quantity of available products" +" in the POS" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__main_warehouse_id +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_main_warehouse_id +msgid "Main Warehouse" +msgstr "" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Mark quantity red if below or equal this value." +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model.fields,field_description:pos_stock_available_online.field_pos_config__minimum_product_quantity_alert +#: model:ir.model.fields,field_description:pos_stock_available_online.field_res_config_settings__pos_minimum_product_quantity_alert +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Minimum Product Quantity Alert" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_pos_config +msgid "Point of Sale Configuration" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_pos_session +msgid "Point of Sale Session" +msgstr "" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Product Quantity" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_quant +msgid "Quants" +msgstr "" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Show quantity in the following warehouses in additional to" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_notifier_pos_mixin +msgid "Stock Notifier POS Mixin" +msgstr "" + +#. module: pos_stock_available_online +#. odoo-javascript +#: code:addons/pos_stock_available_online/static/src/xml/Screens/ProductScreen/ProductItem.xml:0 +#, python-format +msgid "Total" +msgstr "" + +#. module: pos_stock_available_online +#: model:ir.model,name:pos_stock_available_online.model_stock_warehouse +msgid "Warehouse" +msgstr "" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "Warehouses must belong to the same company as POS." +msgstr "" + +#. module: pos_stock_available_online +#: model_terms:ir.ui.view,arch_db:pos_stock_available_online.res_config_settings_view_form +msgid "warehouse" +msgstr "" diff --git a/pos_stock_available_online/models/__init__.py b/pos_stock_available_online/models/__init__.py new file mode 100644 index 0000000000..277775b373 --- /dev/null +++ b/pos_stock_available_online/models/__init__.py @@ -0,0 +1,7 @@ +from . import pos_config +from . import product +from . import res_config_settings +from . import stock_notifier_pos_mixin +from . import stock_quant +from . import stock_warehouse +from . import stock_move diff --git a/pos_stock_available_online/models/pos_config.py b/pos_stock_available_online/models/pos_config.py new file mode 100644 index 0000000000..21c73ed52a --- /dev/null +++ b/pos_stock_available_online/models/pos_config.py @@ -0,0 +1,56 @@ +import logging + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class PosConfig(models.Model): + _inherit = "pos.config" + + display_product_quantity = fields.Boolean( + default=True, + ) + main_warehouse_id = fields.Many2one( + "stock.warehouse", + string="Main Warehouse", + related="picking_type_id.warehouse_id", + store=True, + ) + additional_warehouse_ids = fields.Many2many( + "stock.warehouse", + "pos_config_stock_warehouse_rel", + "pos_config_id", + "warehouse_id", + string="Additional Warehouses", + domain="[('company_id', '=', company_id)]", + help="For the selected warehouses will be displayed " + "quantity of available products in the POS", + ) + minimum_product_quantity_alert = fields.Float( + default=0.0, + ) + + def _get_channel_name(self): + """ + Return full channel name as combination, POS Config ID and const CHANNEL + """ + self.ensure_one() + return '["{}","{}"]'.format("pos_stock_available_online", self.id) + + def _notify_available_quantity(self, message): + """ + Notify POSes about product updates + """ + if not isinstance(message, list): + message = [message] + notifications = [] + for config in self: + notifications.append( + [config._get_channel_name(), "pos.config/product_update", message] + ) + if notifications: + self.env["res.users"].search([])._bus_send( + "pos.config/product_update", notifications + ) + _logger.debug("POS notifications for %s: %s", self.ids, notifications) diff --git a/pos_stock_available_online/models/product.py b/pos_stock_available_online/models/product.py new file mode 100644 index 0000000000..921b6896e0 --- /dev/null +++ b/pos_stock_available_online/models/product.py @@ -0,0 +1,20 @@ +from odoo import models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + def _process_pos_ui_product_product(self, products, config_id): + if config_id and config_id.display_product_quantity: + product_obj = self.env["product.product"] + for product_info in products: + product = product_obj.browse(product_info["id"]) + # prepared first main warehouse info + warehouse_info = [ + config_id.main_warehouse_id._prepare_vals_for_pos(product) + ] + # prepared additional warehouses info + for warehouse in config_id.additional_warehouse_ids: + warehouse_info.append(warehouse._prepare_vals_for_pos(product)) + product_info["warehouse_info"] = warehouse_info + return super()._process_pos_ui_product_product(products, config_id) diff --git a/pos_stock_available_online/models/res_config_settings.py b/pos_stock_available_online/models/res_config_settings.py new file mode 100644 index 0000000000..1cf2838cad --- /dev/null +++ b/pos_stock_available_online/models/res_config_settings.py @@ -0,0 +1,23 @@ +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + pos_display_product_quantity = fields.Boolean( + related="pos_config_id.display_product_quantity", + readonly=False, + ) + pos_main_warehouse_id = fields.Many2one( + "stock.warehouse", + related="pos_config_id.main_warehouse_id", + ) + pos_additional_warehouse_ids = fields.Many2many( + "stock.warehouse", + related="pos_config_id.additional_warehouse_ids", + readonly=False, + ) + pos_minimum_product_quantity_alert = fields.Float( + related="pos_config_id.minimum_product_quantity_alert", + readonly=False, + ) diff --git a/pos_stock_available_online/models/stock_move.py b/pos_stock_available_online/models/stock_move.py new file mode 100644 index 0000000000..b9eefc1873 --- /dev/null +++ b/pos_stock_available_online/models/stock_move.py @@ -0,0 +1,30 @@ +from odoo import models + + +class StockMove(models.Model): + _name = "stock.move" + _inherit = ["stock.move", "stock.notifier.pos.mixin"] + + def write(self, vals): + states_dict = {line.id: line.state for line in self} if "state" in vals else {} + res = super().write(vals) + if not states_dict: + return res + for line in self: + if states_dict.get(line.id) != line.state: + line._notify_pos() + return res + + def _action_done(self, cancel_backorder=False): + # As stock will be updated once move has updated its state, skip + # notification from quant + return super( + StockMove, self.with_context(skip_quant_notify_pos=True) + )._action_done(cancel_backorder=cancel_backorder) + + def _get_warehouses_to_notify(self): + warehouses = super()._get_warehouses_to_notify() + warehouses |= self.warehouse_id + warehouses |= self.location_id.warehouse_id + warehouses |= self.location_dest_id.warehouse_id + return warehouses diff --git a/pos_stock_available_online/models/stock_notifier_pos_mixin.py b/pos_stock_available_online/models/stock_notifier_pos_mixin.py new file mode 100644 index 0000000000..65d4e75019 --- /dev/null +++ b/pos_stock_available_online/models/stock_notifier_pos_mixin.py @@ -0,0 +1,46 @@ +from odoo import models + + +class StockNotifierPosMixin(models.AbstractModel): + _name = "stock.notifier.pos.mixin" + _description = "Stock Notifier POS Mixin" + + def _skip_notify_pos(self): + """ + Skip notification to POS + """ + return False + + def _get_warehouses_to_notify(self): + self.ensure_one() + return self.env["stock.warehouse"] + + def _notify_pos(self): + """ + Send notification to POS + """ + pos_session_obj = self.env["pos.session"] + for record in self: + if record._skip_notify_pos(): + continue + for warehouse in record._get_warehouses_to_notify(): + configs = pos_session_obj.search( + [ + ("state", "=", "opened"), + ("config_id.display_product_quantity", "=", True), + "|", + ("config_id.additional_warehouse_ids", "in", [warehouse.id]), + ("config_id.main_warehouse_id", "=", warehouse.id), + "|", + ("config_id.iface_available_categ_ids", "=", False), + ( + "config_id.iface_available_categ_ids", + "in", + record.product_id.pos_categ_ids.ids, + ), + ], + ).mapped("config_id") + if configs: + configs._notify_available_quantity( + warehouse._prepare_vals_for_pos(record.product_id) + ) diff --git a/pos_stock_available_online/models/stock_quant.py b/pos_stock_available_online/models/stock_quant.py new file mode 100644 index 0000000000..4c6b282d77 --- /dev/null +++ b/pos_stock_available_online/models/stock_quant.py @@ -0,0 +1,23 @@ +from odoo import models + + +class StockQuant(models.Model): + _name = "stock.quant" + _inherit = ["stock.quant", "stock.notifier.pos.mixin"] + + def write(self, vals): + res = super().write(vals) + self._notify_pos() + return res + + def _skip_notify_pos(self): + self.ensure_one() + return ( + self.env.context.get("skip_quant_notify_pos", False) + or super()._skip_notify_pos() + ) + + def _get_warehouses_to_notify(self): + warehouses = super()._get_warehouses_to_notify() + warehouses |= self.warehouse_id + return warehouses diff --git a/pos_stock_available_online/models/stock_warehouse.py b/pos_stock_available_online/models/stock_warehouse.py new file mode 100644 index 0000000000..fa67cb4f39 --- /dev/null +++ b/pos_stock_available_online/models/stock_warehouse.py @@ -0,0 +1,19 @@ +from odoo import models + + +class StockWarehouse(models.Model): + _inherit = "stock.warehouse" + + def _prepare_vals_for_pos(self, product): + """ + Prepare warehouse info data to send a POS + """ + self.ensure_one() + return { + "id": self.id, + "name": self.name, + "code": self.code, + "quantity": product.with_context(warehouse=self.id).immediately_usable_qty, + "product_id": product.id, + "uom_id": product.uom_id.id, + } diff --git a/pos_stock_available_online/pyproject.toml b/pos_stock_available_online/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/pos_stock_available_online/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/pos_stock_available_online/readme/CONFIGURE.md b/pos_stock_available_online/readme/CONFIGURE.md new file mode 100644 index 0000000000..8da00ae747 --- /dev/null +++ b/pos_stock_available_online/readme/CONFIGURE.md @@ -0,0 +1,17 @@ +In "Point of Sale" configuration "Product Quantity" section activate "Display Product Quantity" feature: + +![Pos Config](../static/img/pos_config.png) + +By default quantity is displayed for the warehouse that is used in the +POS stock operation type. + +You can add additional warehouses to show quantity in by adding them +into "Additional Warehouses" field. + +In this case the following information will be displayed on product +tiles: + +- Total quantity = quantity in the default warehouse + quantity in the + additional warehouses +- Quantity in the default warehouse +- Quantity in the additional warehouses. diff --git a/pos_stock_available_online/readme/CONTRIBUTORS.md b/pos_stock_available_online/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..98fd39bb57 --- /dev/null +++ b/pos_stock_available_online/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- Cetmix \ +- Dinar Gabbasov +- Nikul Chaudhary \<\> diff --git a/pos_stock_available_online/readme/DESCRIPTION.md b/pos_stock_available_online/readme/DESCRIPTION.md new file mode 100644 index 0000000000..e73030f515 --- /dev/null +++ b/pos_stock_available_online/readme/DESCRIPTION.md @@ -0,0 +1,8 @@ +This module allows to display product quantities in selected locations in real time. Quantities are displayed directly on product tiles: +![Pos Quantity](../static/img/pos_quantity.png) + +Once a product quantity is changed it will be simultaneously updated in +all active POS. + +This module depends on stock_available module which is available in + repo. diff --git a/pos_stock_available_online/readme/ROADMAP.md b/pos_stock_available_online/readme/ROADMAP.md new file mode 100644 index 0000000000..66f29e172f --- /dev/null +++ b/pos_stock_available_online/readme/ROADMAP.md @@ -0,0 +1,3 @@ +This module requires connection to update quantities and doesn't support +offline mode. Warehouses must belong to the same company as POS. Offline +mode support (probably additional module). diff --git a/pos_stock_available_online/static/description/banner.png b/pos_stock_available_online/static/description/banner.png new file mode 100644 index 0000000000..a286fd8f5e Binary files /dev/null and b/pos_stock_available_online/static/description/banner.png differ diff --git a/pos_stock_available_online/static/description/icon.png b/pos_stock_available_online/static/description/icon.png new file mode 100644 index 0000000000..014c48f30f Binary files /dev/null and b/pos_stock_available_online/static/description/icon.png differ diff --git a/pos_stock_available_online/static/description/index.html b/pos_stock_available_online/static/description/index.html new file mode 100644 index 0000000000..8853fa4c97 --- /dev/null +++ b/pos_stock_available_online/static/description/index.html @@ -0,0 +1,456 @@ + + + + + +Point of Sale Stock Available Online + + + +
+

Point of Sale Stock Available Online

+ + +

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

+

This module allows to display product quantities in selected locations +in real time. Quantities are displayed directly on product tiles: Pos Quantity

+

Once a product quantity is changed it will be simultaneously updated in +all active POS.

+

This module depends on stock_available module which is available in +https://github.com/OCA/stock-logistics-availability repo.

+

Table of contents

+ +
+

Configuration

+

In “Point of Sale” configuration “Product Quantity” section activate +“Display Product Quantity” feature:

+

Pos Config

+

By default quantity is displayed for the warehouse that is used in the +POS stock operation type.

+

You can add additional warehouses to show quantity in by adding them +into “Additional Warehouses” field.

+

In this case the following information will be displayed on product +tiles:

+
    +
  • Total quantity = quantity in the default warehouse + quantity in the +additional warehouses
  • +
  • Quantity in the default warehouse
  • +
  • Quantity in the additional warehouses.
  • +
+
+
+

Known issues / Roadmap

+

This module requires connection to update quantities and doesn’t support +offline mode. Warehouses must belong to the same company as POS. Offline +mode support (probably additional module).

+
+
+

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

+
    +
  • Cetmix
  • +
+
+
+

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

+

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

+
+
+
+ + diff --git a/pos_stock_available_online/static/img/pos_config.png b/pos_stock_available_online/static/img/pos_config.png new file mode 100644 index 0000000000..1e698fabea Binary files /dev/null and b/pos_stock_available_online/static/img/pos_config.png differ diff --git a/pos_stock_available_online/static/img/pos_quantity.png b/pos_stock_available_online/static/img/pos_quantity.png new file mode 100644 index 0000000000..1a3fe1d3e0 Binary files /dev/null and b/pos_stock_available_online/static/img/pos_quantity.png differ diff --git a/pos_stock_available_online/static/src/app/generic_components/product_card/product_card.scss b/pos_stock_available_online/static/src/app/generic_components/product_card/product_card.scss new file mode 100644 index 0000000000..43f0724600 --- /dev/null +++ b/pos_stock_available_online/static/src/app/generic_components/product_card/product_card.scss @@ -0,0 +1,40 @@ +.pos .product-list { + .warehouse-info { + padding: 0.5rem; + font-weight: bold; + display: flex; + border-top: 1px solid #efefef; + justify-content: space-between; + + .warehouse { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + + .quantity { + color: black; + padding: 1px 2px; + font-size: 11px; + } + + .warehouse-name { + display: block; + color: #696969; + font-size: 10px; + } + + .quantity.available { + color: #32a868; + } + + .quantity.not-available { + color: #ef5350; + } + } + + .warehouse.total .warehouse-name { + font-weight: bold; + } + } +} diff --git a/pos_stock_available_online/static/src/app/generic_components/product_card/product_card.xml b/pos_stock_available_online/static/src/app/generic_components/product_card/product_card.xml new file mode 100644 index 0000000000..3708d4f922 --- /dev/null +++ b/pos_stock_available_online/static/src/app/generic_components/product_card/product_card.xml @@ -0,0 +1,41 @@ + + + + +
+
+
+ + + + +
+
+
+ +
+ + + + + + +
+
+
+
+
+
+
diff --git a/pos_stock_available_online/static/src/app/screens/product_screen/product_list/product_list.esm.js b/pos_stock_available_online/static/src/app/screens/product_screen/product_list/product_list.esm.js new file mode 100644 index 0000000000..a718ef5f35 --- /dev/null +++ b/pos_stock_available_online/static/src/app/screens/product_screen/product_list/product_list.esm.js @@ -0,0 +1,111 @@ +import {ProductCard} from "@point_of_sale/app/generic_components/product_card/product_card"; +import {formatFloat, roundPrecision} from "@web/core/utils/numbers"; +import {patch} from "@web/core/utils/patch"; +import {useEffect} from "@odoo/owl"; +import {usePos} from "@point_of_sale/app/store/pos_hook"; + +patch(ProductCard, { + props: { + ...ProductCard.props, + warehouse_info: {type: Array, optional: true}, + uom_id: {type: Number, optional: true}, + }, +}); + +patch(ProductCard.prototype, { + setup() { + super.setup(...arguments); + this.pos = usePos(); + const busService = this.env.services.bus_service; + useEffect(() => { + busService.subscribe("pos.config/product_update", (notifications) => { + this._onNotification(notifications); + }); + }); + }, + format_quantity(quantity) { + const unit = this.this.props.product.uom_id; + var formattedQuantity = `${quantity}`; + if (unit) { + if (unit.rounding) { + const decimals = this.pos.data.models["decimal.precision"].find( + (dp) => dp.name === "Product Unit of Measure" + ).digits; + formattedQuantity = formatFloat(quantity, { + digits: [69, decimals], + }); + } else { + formattedQuantity = roundPrecision(quantity, 1).toFixed(0); + } + } + return `${formattedQuantity}`; + }, + get display_total_quantity() { + return this.format_quantity(this.total_quantity); + }, + get total_quantity() { + return this.warehouses.reduce( + (partialSum, warehouse) => partialSum + warehouse.quantity, + 0 + ); + }, + get displayProductQuantity() { + return this.pos.config.display_product_quantity; + }, + get minimumProductQuantityAlert() { + return this.pos.config.minimum_product_quantity_alert; + }, + get warehouses() { + return this.props.product.baseData[this.props.product.id].warehouse_info || []; + }, + _getChannelName() { + return JSON.stringify([ + "pos_stock_available_online", + String(this.pos.config.id), + ]); + }, + _onNotification(notifications) { + const payloads = []; + for (const notification of notifications) { + if (notification[1] === "pos.config/product_update") { + payloads.push(notification[2]); + } + } + this._handleNotification(payloads); + }, + async _handleNotification(payloads) { + const ProductIds = []; + for (const payload of payloads) { + for (const message of payload) { + const productId = message.product_id; + if (!productId) { + continue; + } + const product = this.pos.models["product.product"].get(productId); + if (product) { + if (!product.warehouse_info) { + product.warehouse_info = product.raw?.warehouse_info + ? [...product.raw.warehouse_info] + : []; + } + const warehouse = product.warehouse_info.find( + (wh) => wh.id === message.id + ); + if (warehouse) { + warehouse.quantity = message.quantity; + } else { + product.warehouse_info.push(message); + } + } else { + ProductIds.push(productId); + } + } + } + if (ProductIds.length) { + const uniqueProductIds = [...new Set(ProductIds)]; + await this.pos.data.read("product.product", uniqueProductIds); + await this.pos.processProductAttributes(); + } + this.render(true); + }, +}); diff --git a/pos_stock_available_online/static/src/app/screens/product_screen/product_list/product_list.xml b/pos_stock_available_online/static/src/app/screens/product_screen/product_list/product_list.xml new file mode 100644 index 0000000000..f4da711a29 --- /dev/null +++ b/pos_stock_available_online/static/src/app/screens/product_screen/product_list/product_list.xml @@ -0,0 +1,15 @@ + + + + + + product.warehouse_info + uom_id + + + + diff --git a/pos_stock_available_online/views/res_config_settings_view.xml b/pos_stock_available_online/views/res_config_settings_view.xml new file mode 100644 index 0000000000..cbc5ff676b --- /dev/null +++ b/pos_stock_available_online/views/res_config_settings_view.xml @@ -0,0 +1,49 @@ + + + + res.config.settings.view.form + res.config.settings + + + + + + + + +
+ Show quantity in the following warehouses in additional to warehouse. +
+ + +
+ + + + +
+
+
+
+