diff --git a/pos_order_mgmt/README.rst b/pos_order_mgmt/README.rst new file mode 100644 index 00000000..ab0e579d --- /dev/null +++ b/pos_order_mgmt/README.rst @@ -0,0 +1,135 @@ +============================== +POS Frontend Orders Management +============================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/12.0/pos_order_mgmt + :alt: OCA/pos +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/pos-12-0/pos-12-0-pos_order_mgmt + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/184/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the functionality of the PoS frontend allowing to load +already done PoS Orders in order to be able to operate over them, being able to +reprint past tickets or return them. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to go to *Point of Sale > Configuration > +Point of Sale* and enable *Order Management* + +.. image:: https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-config.png + +#. Change *Maximum orders to load* to your desired amount (10 by default). + Please note that the more you load, the more it will take to load + them in the session opening. You can also set it to 0 and you'll just be + able to load them from the order list screen. + +#. Enable *Reprint orders* on if you want to be able to reprint past orders + in that PoS. + +#. Enable *Return orders* on if you want to be able to return past orders + in that PoS. + +#. Enable *Duplicate orders* on if you want to be able to return past orders + in that PoS. + +Usage +===== + +Once the PoS is loaded, you'll find a shopping trolley icon (🛒) in the top +bar that grants access to the order list screen. + +.. image:: https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-icon.png + +There you can find the number of past orders loaded according to your +configuration (see Configuration) as well as the orders you checked out in +the current session: + +.. image:: https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-list.png + +#. You can see their totals as well as their custumers if registered. +#. You can reprint their tickets clicking on the printer icon (⎙). +#. You can return them pressing on the arrow icon (↶). +#. You have a search input as well that lets you find past tickets by its + reference number. + +NOTE: You'll need your PoS to be online to be able to search or return a past +ticket. + +Known issues / Roadmap +====================== + +* It's possible to return the same order over and over. To avoid so, we should + load and control if there's a returned line id associated with the original + order. That would be a great improvement for future revisions. + This feature is implemented in the module ``pos_order_return`` in the back + office part, but not in front office part (implemented in this this 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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* GRAP +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* David Vidal +* Sylvain LE GAL (https://twitter.com/legalsylvain) +* Carlos Martínez +* Pierrick Brun +* Iván Todorovich +* Ammar Officewala + +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_order_mgmt/__init__.py b/pos_order_mgmt/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/pos_order_mgmt/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/pos_order_mgmt/__manifest__.py b/pos_order_mgmt/__manifest__.py new file mode 100644 index 00000000..84715e3d --- /dev/null +++ b/pos_order_mgmt/__manifest__.py @@ -0,0 +1,28 @@ +# Copyright 2018 GRAP - Sylvain LE GAL +# Copyright 2018 Tecnativa S.L. - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + 'name': 'POS Frontend Orders Management', + 'summary': 'Manage old POS Orders from the frontend', + 'version': '13.0.1.0.0', + 'category': 'Point of Sale', + 'author': 'GRAP, ' + 'Tecnativa, ' + 'Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/pos', + 'license': 'AGPL-3', + 'depends': [ + 'point_of_sale', + ], + 'data': [ + 'views/assets.xml', + 'views/view_pos_config.xml', + 'views/view_pos_order.xml', + ], + 'qweb': [ + 'static/src/xml/pos.xml' + ], + 'application': False, + 'installable': True, +} diff --git a/pos_order_mgmt/i18n/es.po b/pos_order_mgmt/i18n/es.po new file mode 100644 index 00000000..4963dca7 --- /dev/null +++ b/pos_order_mgmt/i18n/es.po @@ -0,0 +1,315 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_order_mgmt +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-10-18 11:15+0000\n" +"PO-Revision-Date: 2019-03-01 15:53+0000\n" +"Last-Translator: Marta Vázquez Rodríguez \n" +"Language-Team: \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 3.4\n" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_copy_done_order +#, fuzzy +#| msgid "Allow to load done orders in this POS" +msgid "Allows to duplicate already done orders in the frontend" +msgstr "Permitir cargar pedidos en este PdV" + +#. module: pos_order_mgmt +#: model_terms:ir.ui.view,arch_db:pos_order_mgmt.view_pos_config_form +#, fuzzy +#| msgid "Allow to load done orders in this POS" +msgid "Allows to manage already done orders in the frontend." +msgstr "Permitir cargar pedidos en este PdV" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_order_mgmt +#, fuzzy +#| msgid "Allow to load done orders in this POS" +msgid "Allows to manage orders in the frontend" +msgstr "Permitir cargar pedidos en este PdV" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_reprint_done_order +#, fuzzy +#| msgid "Allow to load done orders in this POS" +msgid "Allows to reprint already done orders in the frontend" +msgstr "Permitir cargar pedidos en este PdV" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_return_done_order +#, fuzzy +#| msgid "Allow to load done orders in this POS" +msgid "Allows to return already done orders in the frontend" +msgstr "Permitir cargar pedidos en este PdV" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:36 +#, fuzzy, python-format +#| msgid "Amount Total" +msgid "Amount" +msgstr "Importe Total" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:17 +#, python-format +msgid "Back" +msgstr "Volver" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:353 +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:408 +#, python-format +msgid "Can not execute this action because the POS is currently offline" +msgstr "" +"No se puede ejecutar esta acción porque el PdV está sin línea en este momento" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:352 +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:407 +#, python-format +msgid "Connection error" +msgstr "Error de conexión" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:61 +#, python-format +msgid "Create a new order based on this one" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:64 +#, python-format +msgid "Create a refund order of this order" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:34 +#, python-format +msgid "Customer" +msgstr "Cliente" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:89 +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:98 +#, python-format +msgid "DUPLICATE" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:35 +#, python-format +msgid "Date" +msgstr "Fecha" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_copy_done_order +#, fuzzy +#| msgid "Load Done Orders" +msgid "Duplicate Orders" +msgstr "Cargar Ventas Realizadas" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_load_done_order_max_qty +#, fuzzy +#| msgid "Maximum number orders to load" +msgid "Maximum Orders to load" +msgstr "Ventas máximas a cargar" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_load_done_order_max_qty +#, fuzzy +#| msgid "" +#| "Maximum number of orders to load on the PoS at its init. Set it to 0 to " +#| "load none (it's still posible to load them by ticket code)." +msgid "" +"Maximum number of orders to load on the PoS at its init. Set it to 0 to load " +"none (it's still possible to load them by ticket code)." +msgstr "" +"Número máximo de ventas a cargar en el PdV cuando este se inicia. " +"Establézcalo a 0 para no cargar ninguna (es posible cargarlas por referencia " +"del ticket)." + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_order_mgmt +msgid "Order Management" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model,name:pos_order_mgmt.model_pos_config +#, fuzzy +#| msgid "Point of Sale Orders" +msgid "Point of Sale Configuration" +msgstr "Ventas del Punto de Venta" + +#. module: pos_order_mgmt +#: model:ir.model,name:pos_order_mgmt.model_pos_order +msgid "Point of Sale Orders" +msgstr "Ventas del Punto de Venta" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:58 +#, python-format +msgid "Print a duplicate for this order" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:83 +#, python-format +msgid "Rectifies:" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:33 +#, python-format +msgid "Ref." +msgstr "'Ref.'" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__returned_order_reference +msgid "Reference of the returned Order" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:254 +#, python-format +msgid "Refund " +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__refund_order_ids +#, fuzzy +#| msgid "Returned order:" +msgid "Refund Orders" +msgstr "Devolución de venta:" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__refund_order_qty +#, fuzzy +#| msgid "Max. Done Orders Quantity To Load" +msgid "Refund Orders Quantity" +msgstr "Nº Máximo de Ventas Realizadas a Cargar" + +#. module: pos_order_mgmt +#: model_terms:ir.ui.view,arch_db:pos_order_mgmt.view_pos_order_form +msgid "Refunds" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_reprint_done_order +#, fuzzy +#| msgid "Load Done Orders" +msgid "Reprint Orders" +msgstr "Cargar Ventas Realizadas" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_return_done_order +#, fuzzy +#| msgid "Returned order:" +msgid "Return Orders" +msgstr "Devolución de venta:" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__returned_order_id +#, fuzzy +#| msgid "Returned order:" +msgid "Returned Order" +msgstr "Devolución de venta:" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:74 +#, python-format +msgid "Returned order:" +msgstr "Devolución de venta:" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:21 +#, python-format +msgid "Search Order" +msgstr "Buscar Venta" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:370 +#, python-format +msgid "" +"Unable to load some order lines because the products are not available in " +"the POS cache.\n" +"\n" +"Please check that lines :\n" +"\n" +" * " +msgstr "" +"No fue posible cargar algunas líneas porque los pedidos no están disponibles " +"en la caché del PdV.\n" +"\n" +"Por favor, compruebe estas líneas :\n" +"\n" +" * " + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:369 +#, python-format +msgid "Unknown Products" +msgstr "Productos desconocidos" + +#, fuzzy +#~| msgid "Allow to load done orders in this POS" +#~ msgid "Allow to duplicate done orders in this POS" +#~ msgstr "Permitir cargar pedidos en este PdV" + +#, fuzzy +#~| msgid "Allow to load done orders in this POS" +#~ msgid "Allow to reprint done orders in this POS" +#~ msgstr "Permitir cargar pedidos en este PdV" + +#, fuzzy +#~| msgid "Allow to load done orders in this POS" +#~ msgid "Allow to return done orders in this POS" +#~ msgstr "Permitir cargar pedidos en este PdV" + +#~ msgid "Load Done Order Max Qty." +#~ msgstr "Nº Máximo de Ventas a Cargar." + +#~ msgid "Max. Done Orders Quantity To Load" +#~ msgstr "Nº Máximo de Ventas Realizadas a Cargar" + +#, fuzzy +#~| msgid "Returned order:" +#~ msgid "Return Done Orders" +#~ msgstr "Devolución de venta:" + +#~ msgid "" +#~ "Allows to load already done orders in the frontend to operate over them, " +#~ "allowing reprint the tickets, return items, etc." +#~ msgstr "" +#~ "Permite cargar pedidos ya realizados desde el PdV para operar sobre " +#~ "ellos: reimprimir tickes, devoluciónes, etc." + +#~ msgid "pos.config" +#~ msgstr "'pos.config'" diff --git a/pos_order_mgmt/i18n/fr.po b/pos_order_mgmt/i18n/fr.po new file mode 100644 index 00000000..ba43373d --- /dev/null +++ b/pos_order_mgmt/i18n/fr.po @@ -0,0 +1,282 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_order_mgmt +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-07-15 20:54+0000\n" +"PO-Revision-Date: 2019-07-15 20:54+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_copy_done_order +msgid "Allows to duplicate already done orders in the frontend" +msgstr "Autoriser à dupliquer des commandes réalisées dans l'interface tactile" + +#. module: pos_order_mgmt +#: model_terms:ir.ui.view,arch_db:pos_order_mgmt.view_pos_config_form +#, fuzzy +#| msgid "Allows to return already done orders in the frontend" +msgid "Allows to manage already done orders in the frontend." +msgstr "" +"Autoriser à rembourser des commandes réalisées dans l'interface tactile" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_order_mgmt +#, fuzzy +#| msgid "Allows to return already done orders in the frontend" +msgid "Allows to manage orders in the frontend" +msgstr "" +"Autoriser à rembourser des commandes réalisées dans l'interface tactile" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_reprint_done_order +msgid "Allows to reprint already done orders in the frontend" +msgstr "" +"Autoriser à réimprimer des commandes réalisées dans l'interface tactile" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_return_done_order +msgid "Allows to return already done orders in the frontend" +msgstr "" +"Autoriser à rembourser des commandes réalisées dans l'interface tactile" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:36 +#, fuzzy, python-format +#| msgid "Amount Total" +msgid "Amount" +msgstr "Total" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:17 +#, python-format +msgid "Back" +msgstr "Retour" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:353 +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:408 +#, python-format +msgid "Can not execute this action because the POS is currently offline" +msgstr "" +"Vous ne pouvez pas exécuter cette action, car le Point de Vente est " +"actuellement hors ligne" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:352 +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:407 +#, python-format +msgid "Connection error" +msgstr "Erreur de connexion" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:61 +#, python-format +msgid "Create a new order based on this one" +msgstr "Créer une nouvelle commande basée sur celle-ci" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:64 +#, python-format +msgid "Create a refund order of this order" +msgstr "Rembourser cette commande" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:34 +#, python-format +msgid "Customer" +msgstr "Client" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:89 +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:98 +#, python-format +msgid "DUPLICATE" +msgstr "DUPLICATA" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:35 +#, python-format +msgid "Date" +msgstr "Date" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_copy_done_order +#, fuzzy +#| msgid "Duplicate Done Orders" +msgid "Duplicate Orders" +msgstr "Copier une commande réalisées" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_load_done_order_max_qty +#, fuzzy +#| msgid "Maximum number orders to load" +msgid "Maximum Orders to load" +msgstr "Nombre maximum de commande à charger" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_load_done_order_max_qty +msgid "" +"Maximum number of orders to load on the PoS at its init. Set it to 0 to load " +"none (it's still possible to load them by ticket code)." +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_order_mgmt +msgid "Order Management" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model,name:pos_order_mgmt.model_pos_config +msgid "Point of Sale Configuration" +msgstr "Paramétrage du point de vente" + +#. module: pos_order_mgmt +#: model:ir.model,name:pos_order_mgmt.model_pos_order +msgid "Point of Sale Orders" +msgstr "Commandes du point de vente" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:58 +#, python-format +msgid "Print a duplicate for this order" +msgstr "Imprimer un duplicata de cette commande" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:83 +#, python-format +msgid "Rectifies:" +msgstr "Rectifie : " + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:33 +#, python-format +msgid "Ref." +msgstr "Réf." + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__returned_order_reference +msgid "Reference of the returned Order" +msgstr "Réference de la vente retournée" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:254 +#, python-format +msgid "Refund " +msgstr "Rembourse " + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__refund_order_ids +msgid "Refund Orders" +msgstr "Commandes remboursées" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__refund_order_qty +msgid "Refund Orders Quantity" +msgstr "Nombre de remboursement" + +#. module: pos_order_mgmt +#: model_terms:ir.ui.view,arch_db:pos_order_mgmt.view_pos_order_form +msgid "Refunds" +msgstr "Remboursements" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_reprint_done_order +#, fuzzy +#| msgid "Reprint Done Orders" +msgid "Reprint Orders" +msgstr "Réimprimer le ticket d'une commande réalisée" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_return_done_order +#, fuzzy +#| msgid "Returned Order" +msgid "Return Orders" +msgstr "Commande retournée" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__returned_order_id +msgid "Returned Order" +msgstr "Commande retournée" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:74 +#, python-format +msgid "Returned order:" +msgstr "Commande retournée:" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:21 +#, python-format +msgid "Search Order" +msgstr "Recherche une commande" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:370 +#, python-format +msgid "" +"Unable to load some order lines because the products are not available in " +"the POS cache.\n" +"\n" +"Please check that lines :\n" +"\n" +" * " +msgstr "" +"Impossible de charger certaines lignes de ticket car les produits ne sont " +"pas disponible dans le point de vente\n" +"Veuillez vérifier les lignes suivantes : \n" +"\n" +" * " + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:369 +#, python-format +msgid "Unknown Products" +msgstr "Produit inconnu" + +#~ msgid "Allow to duplicate done orders in this POS" +#~ msgstr "" +#~ "Autoriser à dupliquer des commandes réalisées dans l'interface tactile" + +#~ msgid "Allow to reprint done orders in this POS" +#~ msgstr "" +#~ "Autoriser à réimprimer des commandes réalisées dans l'interface tactile" + +#~ msgid "Allow to return done orders in this POS" +#~ msgstr "" +#~ "Autoriser à rembourser des commandes réalisées dans l'interface tactile" + +#~ msgid "Load Done Order Max Qty." +#~ msgstr "Quantité maximale de commandes à charger" + +#~ msgid "Max. Done Orders Quantity To Load" +#~ msgstr "Quantité maximale de commandes à charger" + +#~ msgid "Return Done Orders" +#~ msgstr "Retourner une commande réalisée" diff --git a/pos_order_mgmt/i18n/pos_order_mgmt.pot b/pos_order_mgmt/i18n/pos_order_mgmt.pot new file mode 100644 index 00000000..affcd7a3 --- /dev/null +++ b/pos_order_mgmt/i18n/pos_order_mgmt.pot @@ -0,0 +1,231 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_order_mgmt +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.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_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_copy_done_order +msgid "Allows to duplicate already done orders in the frontend" +msgstr "" + +#. module: pos_order_mgmt +#: model_terms:ir.ui.view,arch_db:pos_order_mgmt.view_pos_config_form +msgid "Allows to manage already done orders in the frontend." +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_order_mgmt +msgid "Allows to manage orders in the frontend" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_reprint_done_order +msgid "Allows to reprint already done orders in the frontend" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_return_done_order +msgid "Allows to return already done orders in the frontend" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:36 +#, python-format +msgid "Amount" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:17 +#, python-format +msgid "Back" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:353 +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:408 +#, python-format +msgid "Can not execute this action because the POS is currently offline" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:352 +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:407 +#, python-format +msgid "Connection error" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:61 +#, python-format +msgid "Create a new order based on this one" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:64 +#, python-format +msgid "Create a refund order of this order" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:34 +#, python-format +msgid "Customer" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:89 +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:98 +#, python-format +msgid "DUPLICATE" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:35 +#, python-format +msgid "Date" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_copy_done_order +msgid "Duplicate Orders" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_load_done_order_max_qty +msgid "Maximum Orders to load" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,help:pos_order_mgmt.field_pos_config__iface_load_done_order_max_qty +msgid "Maximum number of orders to load on the PoS at its init. Set it to 0 to load none (it's still possible to load them by ticket code)." +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_order_mgmt +msgid "Order Management" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model,name:pos_order_mgmt.model_pos_config +msgid "Point of Sale Configuration" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model,name:pos_order_mgmt.model_pos_order +msgid "Point of Sale Orders" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:58 +#, python-format +msgid "Print a duplicate for this order" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:83 +#, python-format +msgid "Rectifies:" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:33 +#, python-format +msgid "Ref." +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__returned_order_reference +msgid "Reference of the returned Order" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:254 +#, python-format +msgid "Refund " +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__refund_order_ids +msgid "Refund Orders" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__refund_order_qty +msgid "Refund Orders Quantity" +msgstr "" + +#. module: pos_order_mgmt +#: model_terms:ir.ui.view,arch_db:pos_order_mgmt.view_pos_order_form +msgid "Refunds" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_reprint_done_order +msgid "Reprint Orders" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_config__iface_return_done_order +msgid "Return Orders" +msgstr "" + +#. module: pos_order_mgmt +#: model:ir.model.fields,field_description:pos_order_mgmt.field_pos_order__returned_order_id +msgid "Returned Order" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:74 +#, python-format +msgid "Returned order:" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/xml/pos.xml:21 +#, python-format +msgid "Search Order" +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:370 +#, python-format +msgid "Unable to load some order lines because the products are not available in the POS cache.\n" +"\n" +"Please check that lines :\n" +"\n" +" * " +msgstr "" + +#. module: pos_order_mgmt +#. openerp-web +#: code:addons/pos_order_mgmt/static/src/js/widgets.js:369 +#, python-format +msgid "Unknown Products" +msgstr "" + diff --git a/pos_order_mgmt/models/__init__.py b/pos_order_mgmt/models/__init__.py new file mode 100644 index 00000000..234b311e --- /dev/null +++ b/pos_order_mgmt/models/__init__.py @@ -0,0 +1,2 @@ +from . import pos_config +from . import pos_order diff --git a/pos_order_mgmt/models/pos_config.py b/pos_order_mgmt/models/pos_config.py new file mode 100644 index 00000000..8f53d4be --- /dev/null +++ b/pos_order_mgmt/models/pos_config.py @@ -0,0 +1,45 @@ +# Copyright 2018 GRAP - Sylvain LE GAL +# Copyright 2018 Tecnativa S.L. - David Vidal +# Copyright 2019 Coop IT Easy SCRLfs +# Pierrick Brun +# Copyright 2019 Druidoo - Iván Todorovich +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class PosConfig(models.Model): + _inherit = 'pos.config' + + iface_order_mgmt = fields.Boolean( + string='Order Management', + help='Allows to manage orders in the frontend', + default=True, + ) + + iface_reprint_done_order = fields.Boolean( + string='Reprint Orders', + default=True, + help='Allows to reprint already done orders in the frontend', + ) + + iface_return_done_order = fields.Boolean( + string='Return Orders', + default=True, + help='Allows to return already done orders in the frontend', + ) + + iface_copy_done_order = fields.Boolean( + string='Duplicate Orders', + default=True, + help='Allows to duplicate already done orders in the frontend', + ) + + iface_load_done_order_max_qty = fields.Integer( + string='Maximum Orders to load', + default=10, + required=True, + help='Maximum number of orders to load on the PoS at its init. ' + 'Set it to 0 to load none (it\'s still possible to load them by ' + 'ticket code).', + ) diff --git a/pos_order_mgmt/models/pos_order.py b/pos_order_mgmt/models/pos_order.py new file mode 100644 index 00000000..199508e6 --- /dev/null +++ b/pos_order_mgmt/models/pos_order.py @@ -0,0 +1,150 @@ +# Copyright 2018 GRAP - Sylvain LE GAL +# Copyright 2018 Tecnativa S.L. - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, models, fields + + +class PosOrder(models.Model): + _inherit = 'pos.order' + + returned_order_id = fields.Many2one( + comodel_name='pos.order', + string='Returned Order', + readonly=True, + ) + + returned_order_reference = fields.Char( + related='returned_order_id.pos_reference', + string='Reference of the returned Order') + + refund_order_ids = fields.One2many( + comodel_name='pos.order', + inverse_name='returned_order_id', + string='Refund Orders', + readonly=True, + ) + + refund_order_qty = fields.Integer( + compute='_compute_refund_order_qty', + string='Refund Orders Quantity', + ) + + @api.depends('refund_order_ids') + def _compute_refund_order_qty(self): + for order in self: + order.refund_order_qty = len(order.refund_order_ids) + + def action_view_refund_orders(self): + self.ensure_one() + + action = self.env.ref('point_of_sale.action_pos_pos_form').read()[0] + + if self.refund_order_qty == 1: + action['views'] = [ + (self.env.ref('point_of_sale.view_pos_pos_form').id, 'form')] + action['res_id'] = self.refund_order_ids.ids[0] + else: + action['domain'] = [('id', 'in', self.refund_order_ids.ids)] + return action + + def refund(self): + return super(PosOrder, self.with_context(refund=True)).refund() + + def copy(self, default=None): + self.ensure_one() + order = super().copy(default=default) + if self.env.context.get('refund', False): + order.returned_order_id = self.id + return order + + @api.model + def _prepare_filter_for_pos(self, pos_session_id): + return [ + ('state', 'in', ['paid', 'done', 'invoiced']), + ] + + @api.model + def _prepare_filter_query_for_pos(self, pos_session_id, query): + return [ + '|', '|', + ('name', 'ilike', query), + ('pos_reference', 'ilike', query), + ('partner_id.display_name', 'ilike', query), + ] + + @api.model + def _prepare_fields_for_pos_list(self): + return [ + 'name', 'pos_reference', 'partner_id', 'date_order', + 'amount_total', + ] + + @api.model + def search_done_orders_for_pos(self, query, pos_session_id): + session_obj = self.env['pos.session'] + config = session_obj.browse(pos_session_id).config_id + condition = self._prepare_filter_for_pos(pos_session_id) + if not query: + # Search only this POS orders + condition += [('config_id', '=', config.id)] + else: + # Search globally by criteria + condition += self._prepare_filter_query_for_pos( + pos_session_id, query) + field_names = self._prepare_fields_for_pos_list() + return self.search_read( + condition, field_names, limit=config.iface_load_done_order_max_qty) + + def _prepare_done_order_for_pos(self): + self.ensure_one() + order_lines = [] + payment_lines = [] + for order_line in self.lines: + order_line = self._prepare_done_order_line_for_pos(order_line) + order_lines.append(order_line) + for payment_line in self.payment_ids: + payment_line = self._prepare_done_order_payment_for_pos( + payment_line) + payment_lines.append(payment_line) + res = { + 'id': self.id, + 'date_order': self.date_order, + 'pos_reference': self.pos_reference, + 'name': self.name, + 'partner_id': self.partner_id.id, + 'fiscal_position': self.fiscal_position_id.id, + 'line_ids': order_lines, + 'payment_lines': payment_lines, + 'to_invoice': bool(self.account_move), + 'returned_order_id': self.returned_order_id.id, + 'returned_order_reference': self.returned_order_reference, + } + return res + + def _prepare_done_order_line_for_pos(self, order_line): + self.ensure_one() + return { + 'product_id': order_line.product_id.id, + 'qty': order_line.qty, + 'price_unit': order_line.price_unit, + 'discount': order_line.discount, + } + + def _prepare_done_order_payment_for_pos(self, payment_line): + self.ensure_one() + return { + 'payment_method_id': payment_line.payment_method_id.id, + 'amount': payment_line.amount, + } + + def load_done_order_for_pos(self): + self.ensure_one() + return self._prepare_done_order_for_pos() + + def _order_fields(self, ui_order): + res = super()._order_fields(ui_order) + res.update({ + 'returned_order_id': ui_order.get('returned_order_id', False), + }) + return res diff --git a/pos_order_mgmt/readme/CONFIGURE.rst b/pos_order_mgmt/readme/CONFIGURE.rst new file mode 100644 index 00000000..4bd22f33 --- /dev/null +++ b/pos_order_mgmt/readme/CONFIGURE.rst @@ -0,0 +1,18 @@ +To configure this module, you need to go to *Point of Sale > Configuration > +Point of Sale* and enable *Order Management* + +.. image:: ../static/description/order-mgmt-config.png + +#. Change *Maximum orders to load* to your desired amount (10 by default). + Please note that the more you load, the more it will take to load + them in the session opening. You can also set it to 0 and you'll just be + able to load them from the order list screen. + +#. Enable *Reprint orders* on if you want to be able to reprint past orders + in that PoS. + +#. Enable *Return orders* on if you want to be able to return past orders + in that PoS. + +#. Enable *Duplicate orders* on if you want to be able to return past orders + in that PoS. diff --git a/pos_order_mgmt/readme/CONTRIBUTORS.rst b/pos_order_mgmt/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..539ecc66 --- /dev/null +++ b/pos_order_mgmt/readme/CONTRIBUTORS.rst @@ -0,0 +1,6 @@ +* David Vidal +* Sylvain LE GAL (https://twitter.com/legalsylvain) +* Carlos Martínez +* Pierrick Brun +* Iván Todorovich +* Ammar Officewala diff --git a/pos_order_mgmt/readme/DESCRIPTION.rst b/pos_order_mgmt/readme/DESCRIPTION.rst new file mode 100644 index 00000000..0b3deafb --- /dev/null +++ b/pos_order_mgmt/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module extends the functionality of the PoS frontend allowing to load +already done PoS Orders in order to be able to operate over them, being able to +reprint past tickets or return them. diff --git a/pos_order_mgmt/readme/ROADMAP.rst b/pos_order_mgmt/readme/ROADMAP.rst new file mode 100644 index 00000000..99bb611b --- /dev/null +++ b/pos_order_mgmt/readme/ROADMAP.rst @@ -0,0 +1,5 @@ +* It's possible to return the same order over and over. To avoid so, we should + load and control if there's a returned line id associated with the original + order. That would be a great improvement for future revisions. + This feature is implemented in the module ``pos_order_return`` in the back + office part, but not in front office part (implemented in this this module). diff --git a/pos_order_mgmt/readme/USAGE.rst b/pos_order_mgmt/readme/USAGE.rst new file mode 100644 index 00000000..5a7fd0a8 --- /dev/null +++ b/pos_order_mgmt/readme/USAGE.rst @@ -0,0 +1,19 @@ +Once the PoS is loaded, you'll find a shopping trolley icon (🛒) in the top +bar that grants access to the order list screen. + +.. image:: ../static/description/order-mgmt-icon.png + +There you can find the number of past orders loaded according to your +configuration (see Configuration) as well as the orders you checked out in +the current session: + +.. image:: ../static/description/order-mgmt-list.png + +#. You can see their totals as well as their custumers if registered. +#. You can reprint their tickets clicking on the printer icon (⎙). +#. You can return them pressing on the arrow icon (↶). +#. You have a search input as well that lets you find past tickets by its + reference number. + +NOTE: You'll need your PoS to be online to be able to search or return a past +ticket. diff --git a/pos_order_mgmt/static/description/icon.png b/pos_order_mgmt/static/description/icon.png new file mode 100644 index 00000000..cafb0ac0 Binary files /dev/null and b/pos_order_mgmt/static/description/icon.png differ diff --git a/pos_order_mgmt/static/description/index.html b/pos_order_mgmt/static/description/index.html new file mode 100644 index 00000000..6b339b4a --- /dev/null +++ b/pos_order_mgmt/static/description/index.html @@ -0,0 +1,476 @@ + + + + + + +POS Frontend Orders Management + + + +
+

POS Frontend Orders Management

+ + +

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

+

This module extends the functionality of the PoS frontend allowing to load +already done PoS Orders in order to be able to operate over them, being able to +reprint past tickets or return them.

+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to go to Point of Sale > Configuration > +Point of Sale and enable Order Management

+https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-config.png +
    +
  1. Change Maximum orders to load to your desired amount (10 by default). +Please note that the more you load, the more it will take to load +them in the session opening. You can also set it to 0 and you’ll just be +able to load them from the order list screen.
  2. +
  3. Enable Reprint orders on if you want to be able to reprint past orders +in that PoS.
  4. +
  5. Enable Return orders on if you want to be able to return past orders +in that PoS.
  6. +
  7. Enable Duplicate orders on if you want to be able to return past orders +in that PoS.
  8. +
+
+
+

Usage

+

Once the PoS is loaded, you’ll find a shopping trolley icon (🛒) in the top +bar that grants access to the order list screen.

+https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-icon.png +

There you can find the number of past orders loaded according to your +configuration (see Configuration) as well as the orders you checked out in +the current session:

+https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-list.png +
    +
  1. You can see their totals as well as their custumers if registered.
  2. +
  3. You can reprint their tickets clicking on the printer icon (⎙).
  4. +
  5. You can return them pressing on the arrow icon (↶).
  6. +
  7. You have a search input as well that lets you find past tickets by its +reference number.
  8. +
+

NOTE: You’ll need your PoS to be online to be able to search or return a past +ticket.

+
+
+

Known issues / Roadmap

+
    +
  • It’s possible to return the same order over and over. To avoid so, we should +load and control if there’s a returned line id associated with the original +order. That would be a great improvement for future revisions. +This feature is implemented in the module pos_order_return in the back +office part, but not in front office part (implemented in this this 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 smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

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

+

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

+
+
+
+ + diff --git a/pos_order_mgmt/static/description/order-mgmt-config.png b/pos_order_mgmt/static/description/order-mgmt-config.png new file mode 100644 index 00000000..5d27647a Binary files /dev/null and b/pos_order_mgmt/static/description/order-mgmt-config.png differ diff --git a/pos_order_mgmt/static/description/order-mgmt-icon.png b/pos_order_mgmt/static/description/order-mgmt-icon.png new file mode 100644 index 00000000..e4723efb Binary files /dev/null and b/pos_order_mgmt/static/description/order-mgmt-icon.png differ diff --git a/pos_order_mgmt/static/description/order-mgmt-list.png b/pos_order_mgmt/static/description/order-mgmt-list.png new file mode 100644 index 00000000..4e232586 Binary files /dev/null and b/pos_order_mgmt/static/description/order-mgmt-list.png differ diff --git a/pos_order_mgmt/static/src/css/pos.css b/pos_order_mgmt/static/src/css/pos.css new file mode 100644 index 00000000..f8c1ec0f --- /dev/null +++ b/pos_order_mgmt/static/src/css/pos.css @@ -0,0 +1,45 @@ +/* Copyright 2018 Tecnativa - David Vidal + Copyright 2019 Druidoo - Iván Todorovich + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). +*/ + +.order-list-button { + font-size: 20px; +} + +.orderlist-screen .order-line .button { + cursor: pointer; + top: 0px; + line-height: 32px; + padding: 3px 13px; + font-size: 20px; + background: rgb(230, 230, 230); + margin-left: 12px; + border-radius: 3px; + border: solid 1px rgb(209, 209, 209); + transition: all 150ms linear; +} + +.orderlist-screen .order-line .button:active { + background: black; + border-color: black; + color: white; +} + +.orderlist-screen .order-returned-warning { + font-size: 16px; + font-style: italic; + padding: 8px 0; + background-color: rgb(239, 153, 65); + color: white; + text-align: center; +} + +.orderlist-screen table td[name='td_ol_amount_total'] { + text-align: right; +} + +/* Keep the table from breaking when the customer name is too long */ +.orderlist-screen table td:not([name='td_ol_customer']) { + white-space: nowrap; +} diff --git a/pos_order_mgmt/static/src/js/models.js b/pos_order_mgmt/static/src/js/models.js new file mode 100644 index 00000000..d57e8456 --- /dev/null +++ b/pos_order_mgmt/static/src/js/models.js @@ -0,0 +1,30 @@ +/* Copyright 2018 Tecnativa - David Vidal + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */ + +odoo.define('pos_order_mgmt.models', function (require) { + 'use strict'; + + var models = require('point_of_sale.models'); + + var order_super = models.Order.prototype; + + models.Order = models.Order.extend({ + init_from_JSON: function (json) { + order_super.init_from_JSON.apply(this, arguments); + this.returned_order_id = json.returned_order_id; + this.returned_order_reference = json.returned_order_reference; + }, + export_as_JSON: function () { + var res = order_super.export_as_JSON.apply(this, arguments); + res.returned_order_id = this.returned_order_id; + res.returned_order_reference = this.returned_order_reference; + return res; + }, + export_for_printing: function () { + var res = order_super.export_for_printing.apply(this, arguments); + res.returned_order_id = this.returned_order_id; + res.returned_order_reference = this.returned_order_reference; + return res; + }, + }); +}); diff --git a/pos_order_mgmt/static/src/js/widgets.js b/pos_order_mgmt/static/src/js/widgets.js new file mode 100644 index 00000000..35c475dc --- /dev/null +++ b/pos_order_mgmt/static/src/js/widgets.js @@ -0,0 +1,479 @@ +/* Copyright 2018 GRAP - Sylvain LE GAL + Copyright 2018 Tecnativa - David Vidal + Copyright 2019 Druidoo - Ivan Todorovich + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */ + +odoo.define('pos_order_mgmt.widgets', function(require) { + "use strict"; + + var core = require('web.core'); + var _t = core._t; + var PosBaseWidget = require('point_of_sale.BaseWidget'); + var screens = require('point_of_sale.screens'); + var gui = require('point_of_sale.gui'); + var chrome = require('point_of_sale.chrome'); + var pos = require('point_of_sale.models'); + + var QWeb = core.qweb; + var ScreenWidget = screens.ScreenWidget; + var DomCache = screens.DomCache; + + screens.ReceiptScreenWidget.include({ + render_receipt: function() { + if (!this.pos.reloaded_order) { + return this._super(); + } + var order = this.pos.reloaded_order; + this.$('.pos-receipt-container').html(QWeb.render('OrderReceipt', { + widget: this, + pos: this.pos, + order: order, + receipt: order.export_for_printing(), + orderlines: order.get_orderlines(), + paymentlines: order.get_paymentlines(), + })); + this.pos.from_loaded_order = true; + }, + click_next: function() { + if (!this.pos.from_loaded_order) { + return this._super(); + } + this.pos.from_loaded_order = false; + // When reprinting a loaded order we temporarily set it as the + // active one. When we get out from the printing screen, we set + // it back to the one that was active + if (this.pos.current_order) { + this.pos.set_order(this.pos.current_order); + this.pos.current_order = false; + } + return this.gui.show_screen(this.gui.startup_screen); + }, + }); + + var OrderListScreenWidget = ScreenWidget.extend({ + template: 'OrderListScreenWidget', + + init: function(parent, options) { + this._super(parent, options); + this.order_cache = new DomCache(); + this.orders = []; + this.unknown_products = []; + this.search_query = false; + this.perform_search(); + }, + + auto_back: true, + + show: function() { + var self = this; + var previous_screen = false; + if (this.pos.get_order()) { + previous_screen = this.pos.get_order().get_screen_data( + 'previous-screen'); + } + if (previous_screen === 'receipt') { + this.gui.screen_instances.receipt.click_next(); + this.gui.show_screen('orderlist'); + } + this._super(); + this.renderElement(); + this.old_order = this.pos.get_order(); + this.$('.back').click(function() { + return self.gui.show_screen(self.gui.startup_screen); + }); + + if (this.pos.config.iface_vkeyboard && + this.chrome.widget.keyboard) { + this.chrome.widget.keyboard.connect( + this.$('.searchbox input')); + } + + var search_timeout = null; + this.$('.searchbox input').on('keyup', function() { + self.search_query = this.value; + clearTimeout(search_timeout); + search_timeout = setTimeout(function() { + self.perform_search(); + }, 70); + }); + + this.$('.searchbox .search-clear').click(function() { + self.clear_search(); + }); + + this.perform_search(); + }, + + render_list: function() { + var self = this; + var orders = this.orders; + var contents = this.$el[0].querySelector('.order-list-contents'); + contents.innerHTML = ""; + for ( + var i = 0, len = Math.min(orders.length, 1000); i < len; i++ + ) { + var order = orders[i]; + var orderline = this.order_cache.get_node( + order.id || order.uid); + if (!orderline) { + var orderline_html = QWeb.render('OrderLine', { + widget: this, + order: order, + }); + orderline = document.createElement('tbody'); + orderline.innerHTML = orderline_html; + orderline = orderline.childNodes[1]; + this.order_cache.cache_node( + order.id || order.uid, orderline); + } + if (order === this.old_order) { + orderline.classList.add('highlight'); + } else { + orderline.classList.remove('highlight'); + } + contents.appendChild(orderline); + } + // FIXME: Everytime the list is rendered we need to reassing the + // button events. + this.$('.order-list-return').off('click'); + this.$('.order-list-reprint').off('click'); + this.$('.order-list-copy').off('click'); + this.$('.order-list-reprint').click(function(event) { + self.order_list_actions(event, 'print'); + }); + this.$('.order-list-copy').click(function(event) { + self.order_list_actions(event, 'copy'); + }); + this.$('.order-list-return').click(function(event) { + self.order_list_actions(event, 'return'); + }); + }, + + order_list_actions: function(event, action) { + var self = this; + var dataset = event.target.parentNode.dataset; + self.load_order_data(parseInt(dataset.orderId, 10)) + .then(function(order_data) { + self.order_action(order_data, action); + }); + }, + + order_action: function(order_data, action) { + if (this.old_order !== null) { + this.gui.back(); + } + var order = this.load_order_from_data(order_data, action); + if (!order) { + // The load of the order failed. (products not found, ... + // We cancel the action + return; + } + this['action_' + action](order_data, order); + }, + + action_print: function(order_data, order) { + // We store temporarily the current order so we can safely compute + // taxes based on fiscal position + this.pos.current_order = this.pos.get_order(); + + this.pos.set_order(order); + + if (this.pos.config.iface_print_via_proxy) { + this.pos.proxy.print_receipt(QWeb.render( + 'OrderReceipt', { + widget: this, + pos: this.pos, + order: order, + receipt: order.export_for_printing(), + orderlines: order.get_orderlines(), + paymentlinesf: order.get_paymentlines(), + })); + this.pos.set_order(this.pos.current_order); + this.pos.current_order = false; + } else { + this.pos.reloaded_order = order; + this.gui.show_screen('receipt'); + this.pos.reloaded_order = false; + } + + // If it's invoiced, we also print the invoice + if (order_data.to_invoice) { + this.pos.chrome.do_action('point_of_sale.pos_invoice_report', { + additional_context: { + active_ids: [order_data.id] + } + }) + } + + // Destroy the order so it's removed from localStorage + // Otherwise it will stay there and reappear on browser refresh + order.destroy(); + }, + + action_copy: function(order_data, order) { + order.trigger('change'); + this.pos.get('orders').add(order); + this.pos.set('selectedOrder', order); + return order; + }, + + action_return: function(order_data, order) { + order.trigger('change'); + this.pos.get('orders').add(order); + this.pos.set('selectedOrder', order); + return order; + }, + + _prepare_order_from_order_data: function(order_data, action) { + var self = this; + var order = new pos.Order({}, { + pos: this.pos, + }); + + // Get Customer + if (order_data.partner_id) { + order.set_client( + this.pos.db.get_partner_by_id(order_data.partner_id)); + } + + // Get fiscal position + if (order_data.fiscal_position && this.pos.fiscal_positions) { + var fiscal_positions = this.pos.fiscal_positions; + order.fiscal_position = fiscal_positions.filter(function(p) { + return p.id === order_data.fiscal_position; + })[0]; + order.trigger('change'); + } + + // Get order lines + self._prepare_orderlines_from_order_data( + order, order_data, action); + + // Get Name + if (['print'].indexOf(action) !== -1) { + order.name = order_data.pos_reference; + } else if (['return'].indexOf(action) !== -1) { + order.name = _t("Refund ") + order.uid; + } + + // Get to invoice + if (['return', 'copy'].indexOf(action) !== -1) { + // If previous order was invoiced, we need a refund too + order.set_to_invoice(order_data.to_invoice); + } + + // Get returned Order + if (['print'].indexOf(action) !== -1) { + // Get the same value as the original + order.returned_order_id = order_data.returned_order_id; + order.returned_order_reference = + order_data.returned_order_reference; + } else if (['return'].indexOf(action) !== -1) { + order.returned_order_id = order_data.id; + order.returned_order_reference = order_data.pos_reference; + } + + // Get Date + if (['print'].indexOf(action) !== -1) { + order.formatted_validation_date = + moment(order_data.date_order).format('YYYY-MM-DD HH:mm:ss'); + } + + // Get Payment lines + if (['print'].indexOf(action) !== -1) { + var paymentLines = order_data.payment_lines || []; + _.each(paymentLines, function(paymentLine) { + var line = paymentLine; + // In case of local data + if (line.length === 3) { + line = line[2]; + } + _.each(self.pos.payment_methods, function(payment_method) { + if (payment_method.id === line.payment_method_id) { + if (line.amount > 0) { + // If it is not change + order.add_paymentline(payment_method); + order.selected_paymentline.set_amount( + line.amount); + } + } + }); + }); + } + return order; + }, + + _prepare_orderlines_from_order_data: function( + order, order_data, action) { + var orderLines = order_data.line_ids || order_data.lines || []; + + var self = this; + _.each(orderLines, function(orderLine) { + var line = orderLine; + // In case of local data + if (line.length === 3) { + line = line[2]; + } + var product = self.pos.db.get_product_by_id(line.product_id); + // Check if product are available in pos + if (_.isUndefined(product)) { + self.unknown_products.push(String(line.product_id)); + } else { + var qty = line.qty; + if (['return'].indexOf(action) !== -1) { + // Invert line quantities + qty *= -1; + } + // Create a new order line + order.add_product(product, { + price: line.price_unit, + quantity: qty, + discount: line.discount, + merge: false, + }); + } + }); + }, + + load_order_data: function(order_id) { + var self = this; + return this._rpc({ + model: 'pos.order', + method: 'load_done_order_for_pos', + args: [order_id], + }).guardedCatch(function(reason) { + if (parseInt(reason.message.code, 10) === 200) { + // Business Logic Error, not a connection problem + self.gui.show_popup( + 'error-traceback', { + 'title': error.data.message, + 'body': error.data.debug, + } + ); + } else { + self.gui.show_popup('error', { + 'title': _t('Connection error'), + 'body': _t( + 'Can not execute this action because the POS' + + ' is currently offline'), + }); + } + }); + }, + + load_order_from_data: function(order_data, action) { + var self = this; + this.unknown_products = []; + var order = self._prepare_order_from_order_data( + order_data, action); + // Forbid POS Order loading if some products are unknown + if (self.unknown_products.length > 0) { + self.gui.show_popup('error-traceback', { + 'title': _t('Unknown Products'), + 'body': _t('Unable to load some order lines because the ' + + 'products are not available in the POS cache.\n\n' + + 'Please check that lines :\n\n * ') + + self.unknown_products.join("; \n *"), + }); + return false; + } + return order; + }, + + // Search Part + search_done_orders: function(query) { + var self = this; + return this._rpc({ + model: 'pos.order', + method: 'search_done_orders_for_pos', + args: [query || '', this.pos.pos_session.id], + }).then(function(result) { + self.orders = result; + // Get the date in local time + _.each(self.orders, function(order) { + if (order.date_order) { + order.date_order = moment.utc(order.date_order) + .local().format('YYYY-MM-DD HH:mm:ss'); + } + }); + }).guardedCatch(function(reason) { + if (parseInt(reason.message.code, 10) === 200) { + // Business Logic Error, not a connection problem + self.gui.show_popup( + 'error-traceback', { + 'title': error.data.message, + 'body': error.data.debug, + } + ); + } else { + self.gui.show_popup('error', { + 'title': _t('Connection error'), + 'body': _t( + 'Can not execute this action because the POS' + + ' is currently offline'), + }); + } + reason.event.preventDefault(); + }); + }, + + perform_search: function() { + var self = this; + return this.search_done_orders(self.search_query) + .then(function() { + self.render_list(); + }); + }, + + clear_search: function() { + var self = this; + self.$('.searchbox input')[0].value = ''; + self.$('.searchbox input').focus(); + self.search_query = false; + self.perform_search(); + }, + }); + + gui.define_screen({ + name: 'orderlist', + widget: OrderListScreenWidget, + }); + + var ListOrderButtonWidget = PosBaseWidget.extend({ + template: 'ListOrderButtonWidget', + init: function(parent, options) { + var opts = options || {}; + this._super(parent, opts); + this.action = opts.action; + this.label = opts.label; + }, + + button_click: function() { + this.gui.show_screen('orderlist'); + }, + + renderElement: function() { + var self = this; + this._super(); + this.$el.click(function() { + self.button_click(); + }); + }, + }); + + var widgets = chrome.Chrome.prototype.widgets; + widgets.push({ + 'name': 'list_orders', + 'widget': ListOrderButtonWidget, + 'prepend': '.pos-rightheader', + 'args': { + 'label': 'All Orders', + }, + }); + + return { + ListOrderButtonWidget: ListOrderButtonWidget, + OrderListScreenWidget: OrderListScreenWidget, + }; + +}); \ No newline at end of file diff --git a/pos_order_mgmt/static/src/xml/pos.xml b/pos_order_mgmt/static/src/xml/pos.xml new file mode 100644 index 00000000..769ae352 --- /dev/null +++ b/pos_order_mgmt/static/src/xml/pos.xml @@ -0,0 +1,104 @@ + + + + + +
+ +
+
+
+ + +
+
+
+ + + Back + + + + + + +
+
+
+
+
+
+ + + + + + + + + +
Ref.CustomerDateAmount +
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Returned order: +
+
+
+ + + + +
+ Rectifies: + +
+ + +
+
DUPLICATE
+
+
+
+
+ + + + +
DUPLICATE
+
+
+
+
+ +
diff --git a/pos_order_mgmt/tests/__init__.py b/pos_order_mgmt/tests/__init__.py new file mode 100644 index 00000000..d9b96c4f --- /dev/null +++ b/pos_order_mgmt/tests/__init__.py @@ -0,0 +1 @@ +from . import test_module diff --git a/pos_order_mgmt/tests/test_module.py b/pos_order_mgmt/tests/test_module.py new file mode 100644 index 00000000..874f46c2 --- /dev/null +++ b/pos_order_mgmt/tests/test_module.py @@ -0,0 +1,78 @@ +# Copyright (C) 2019 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields +from odoo.tests.common import TransactionCase + + +class TestModule(TransactionCase): + + def setUp(self): + super(TestModule, self).setUp() + + # Get Registry + self.PosOrder = self.env['pos.order'] + self.AccountPayment = self.env['account.payment'] + + # Get Object + self.pos_product = self.env.ref('point_of_sale.whiteboard_pen') + self.pricelist = self.env.ref('product.list0') + self.partner = self.env.ref('base.res_partner_12') + + # Create a new pos config and open it + self.pos_config = self.env.ref('point_of_sale.pos_config_main').copy() + self.pos_config.open_session_cb() + + # Test Section + def test_load_order(self): + order = self._create_order() + orders_data = self.PosOrder.search_done_orders_for_pos( + [], self.pos_config.current_session_id.id) + self.assertEqual(len(orders_data), 1) + self.assertEqual(order.id, + orders_data[0]['id']) + + detail_data = order.load_done_order_for_pos() + self.assertEqual( + len(detail_data.get('line_ids', [])), 1, + "Loading order detail failed") + + def _create_order(self): + # Create order + order_data = { + 'id': u'0006-001-0010', + 'to_invoice': True, + 'data': { + 'pricelist_id': self.pricelist.id, + 'user_id': 1, + 'name': 'Order 0006-001-0010', + 'partner_id': self.partner.id, + 'amount_paid': 0.9, + 'pos_session_id': self.pos_config.current_session_id.id, + 'lines': [[0, 0, { + 'product_id': self.pos_product.id, + 'price_unit': 0.9, + 'qty': 1, + 'price_subtotal': 0.9, + 'price_subtotal_incl': 0.9, + }]], + 'statement_ids': [[0, 0, { + 'payment_method_id': self.pos_config.payment_method_ids[0].id, + 'amount': 0.9, + 'name': fields.Datetime.now(), + 'payment_date' : fields.Datetime.now(), + 'session_id' : self.pos_config.current_session_id.id, + }]], + 'creation_date': u'2018-09-27 15:51:03', + 'amount_tax': 0, + 'fiscal_position_id': False, + 'uid': u'00001-001-0001', + 'amount_return': 0, + 'sequence_number': 1, + 'amount_total': 0.9, + }} + + result = self.PosOrder.create_from_ui([order_data]) + order = self.PosOrder.browse(result[0].get('id')) + return order diff --git a/pos_order_mgmt/views/assets.xml b/pos_order_mgmt/views/assets.xml new file mode 100644 index 00000000..126e81c3 --- /dev/null +++ b/pos_order_mgmt/views/assets.xml @@ -0,0 +1,13 @@ + + + +