Browse Source

Merge c40de4fa58 into 050828d7a0

pull/397/merge
Ammar Officewala 4 years ago
committed by GitHub
parent
commit
6ad64421d3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 135
      pos_order_mgmt/README.rst
  2. 1
      pos_order_mgmt/__init__.py
  3. 28
      pos_order_mgmt/__manifest__.py
  4. 315
      pos_order_mgmt/i18n/es.po
  5. 282
      pos_order_mgmt/i18n/fr.po
  6. 231
      pos_order_mgmt/i18n/pos_order_mgmt.pot
  7. 2
      pos_order_mgmt/models/__init__.py
  8. 45
      pos_order_mgmt/models/pos_config.py
  9. 150
      pos_order_mgmt/models/pos_order.py
  10. 18
      pos_order_mgmt/readme/CONFIGURE.rst
  11. 6
      pos_order_mgmt/readme/CONTRIBUTORS.rst
  12. 3
      pos_order_mgmt/readme/DESCRIPTION.rst
  13. 5
      pos_order_mgmt/readme/ROADMAP.rst
  14. 19
      pos_order_mgmt/readme/USAGE.rst
  15. BIN
      pos_order_mgmt/static/description/icon.png
  16. 476
      pos_order_mgmt/static/description/index.html
  17. BIN
      pos_order_mgmt/static/description/order-mgmt-config.png
  18. BIN
      pos_order_mgmt/static/description/order-mgmt-icon.png
  19. BIN
      pos_order_mgmt/static/description/order-mgmt-list.png
  20. 45
      pos_order_mgmt/static/src/css/pos.css
  21. 30
      pos_order_mgmt/static/src/js/models.js
  22. 479
      pos_order_mgmt/static/src/js/widgets.js
  23. 104
      pos_order_mgmt/static/src/xml/pos.xml
  24. 1
      pos_order_mgmt/tests/__init__.py
  25. 78
      pos_order_mgmt/tests/test_module.py
  26. 13
      pos_order_mgmt/views/assets.xml
  27. 47
      pos_order_mgmt/views/view_pos_config.xml
  28. 22
      pos_order_mgmt/views/view_pos_order.xml

135
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 <https://github.com/OCA/pos/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 <https://github.com/OCA/pos/issues/new?body=module:%20pos_order_mgmt%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* GRAP
* Tecnativa
Contributors
~~~~~~~~~~~~
* David Vidal <david.vidal@tecnativa.com>
* Sylvain LE GAL (https://twitter.com/legalsylvain)
* Carlos Martínez <carlos@domatix.com>
* Pierrick Brun <pierrick.brun@akretion.com>
* Iván Todorovich <ivan.todorovich@druidoo.io>
* Ammar Officewala <https://twitter.com/AmmarOfficewala>
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 <https://github.com/OCA/pos/tree/12.0/pos_order_mgmt>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

1
pos_order_mgmt/__init__.py

@ -0,0 +1 @@
from . import models

28
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,
}

315
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 <vazrodmar@gmail.com>\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'"

282
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"

231
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 ""

2
pos_order_mgmt/models/__init__.py

@ -0,0 +1,2 @@
from . import pos_config
from . import pos_order

45
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 <pierrick.brun@akretion.com>
# 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).',
)

150
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

18
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.

6
pos_order_mgmt/readme/CONTRIBUTORS.rst

@ -0,0 +1,6 @@
* David Vidal <david.vidal@tecnativa.com>
* Sylvain LE GAL (https://twitter.com/legalsylvain)
* Carlos Martínez <carlos@domatix.com>
* Pierrick Brun <pierrick.brun@akretion.com>
* Iván Todorovich <ivan.todorovich@druidoo.io>
* Ammar Officewala <https://twitter.com/AmmarOfficewala>

3
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.

5
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).

19
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.

BIN
pos_order_mgmt/static/description/icon.png

After

Width: 560  |  Height: 560  |  Size: 15 KiB

476
pos_order_mgmt/static/description/index.html

@ -0,0 +1,476 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>POS Frontend Orders Management</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="pos-frontend-orders-management">
<h1 class="title">POS Frontend Orders Management</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/pos/tree/12.0/pos_order_mgmt"><img alt="OCA/pos" src="https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/pos-12-0/pos-12-0-pos_order_mgmt"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/184/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>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.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="id1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id3">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id4">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id5">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id6">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id7">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id8">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id1">Configuration</a></h1>
<p>To configure this module, you need to go to <em>Point of Sale &gt; Configuration &gt;
Point of Sale</em> and enable <em>Order Management</em></p>
<img alt="https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-config.png" src="https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-config.png" />
<ol class="arabic simple">
<li>Change <em>Maximum orders to load</em> 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.</li>
<li>Enable <em>Reprint orders</em> on if you want to be able to reprint past orders
in that PoS.</li>
<li>Enable <em>Return orders</em> on if you want to be able to return past orders
in that PoS.</li>
<li>Enable <em>Duplicate orders</em> on if you want to be able to return past orders
in that PoS.</li>
</ol>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id2">Usage</a></h1>
<p>Once the PoS is loaded, you’ll find a shopping trolley icon (🛒) in the top
bar that grants access to the order list screen.</p>
<img alt="https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-icon.png" src="https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-icon.png" />
<p>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:</p>
<img alt="https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-list.png" src="https://raw.githubusercontent.com/OCA/pos/12.0/pos_order_mgmt/static/description/order-mgmt-list.png" />
<ol class="arabic simple">
<li>You can see their totals as well as their custumers if registered.</li>
<li>You can reprint their tickets clicking on the printer icon (⎙).</li>
<li>You can return them pressing on the arrow icon (↶).</li>
<li>You have a search input as well that lets you find past tickets by its
reference number.</li>
</ol>
<p>NOTE: You’ll need your PoS to be online to be able to search or return a past
ticket.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id3">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>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 <tt class="docutils literal">pos_order_return</tt> in the back
office part, but not in front office part (implemented in this this module).</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id4">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/pos/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/pos/issues/new?body=module:%20pos_order_mgmt%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id5">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
<ul class="simple">
<li>GRAP</li>
<li>Tecnativa</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id7">Contributors</a></h2>
<ul class="simple">
<li>David Vidal &lt;<a class="reference external" href="mailto:david.vidal&#64;tecnativa.com">david.vidal&#64;tecnativa.com</a>&gt;</li>
<li>Sylvain LE GAL (<a class="reference external" href="https://twitter.com/legalsylvain">https://twitter.com/legalsylvain</a>)</li>
<li>Carlos Martínez &lt;<a class="reference external" href="mailto:carlos&#64;domatix.com">carlos&#64;domatix.com</a>&gt;</li>
<li>Pierrick Brun &lt;<a class="reference external" href="mailto:pierrick.brun&#64;akretion.com">pierrick.brun&#64;akretion.com</a>&gt;</li>
<li>Iván Todorovich &lt;<a class="reference external" href="mailto:ivan.todorovich&#64;druidoo.io">ivan.todorovich&#64;druidoo.io</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id8">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/pos/tree/12.0/pos_order_mgmt">OCA/pos</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

BIN
pos_order_mgmt/static/description/order-mgmt-config.png

After

Width: 472  |  Height: 234  |  Size: 14 KiB

BIN
pos_order_mgmt/static/description/order-mgmt-icon.png

After

Width: 678  |  Height: 271  |  Size: 26 KiB

BIN
pos_order_mgmt/static/description/order-mgmt-list.png

After

Width: 825  |  Height: 279  |  Size: 38 KiB

45
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;
}

30
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;
},
});
});

479
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,
};
});

104
pos_order_mgmt/static/src/xml/pos.xml

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="ListOrderButtonWidget">
<t t-if="widget.pos.config.iface_order_mgmt">
<div class="header-button order-list-button">
<i class='fa fa-fw fa-shopping-cart'/>
</div>
</t>
</t>
<t t-name="OrderListScreenWidget">
<div class="orderlist-screen clientlist-screen screen">
<div class="screen-content">
<section class="top-content">
<span class='button back'>
<i class='fa fa-angle-double-left'></i>
Back
</span>
<span class='searchbox'>
<input placeholder='Search Order' />
<span class='search-clear'></span>
</span>
<span class='searchbox'></span>
</section>
<section class="full-content">
<div class="window">
<section class="subwindow">
<div class="subwindow-container">
<div class="subwindow-container-fix touch-scrollable scrollable-y">
<table class="client-list">
<thead>
<th name="th_ol_ref">Ref.</th>
<th name="th_ol_customer">Customer</th>
<th name="th_ol_date">Date</th>
<th name="th_ol_amount_total">Amount</th>
<th name="th_ol_reprint"/>
</thead>
<tbody class="order-list-contents">
</tbody>
</table>
</div>
</div>
</section>
</div>
</section>
</div>
</div>
</t>
<t t-name="OrderLine">
<tr class='order-line' t-att-data-id='order.id' t-att-data-Uid='order.uid'>
<td name="td_ol_name"><t t-esc='order.pos_reference or order.name' /></td>
<td name="td_ol_customer"><t t-esc='order.partner_id[1]' /></td>
<td name="td_ol_date"><t t-esc='order.date_order' /></td>
<td name="td_ol_amount_total"><t t-esc='widget.format_currency(order.amount_total)' /></td>
<td name="td_ol_reprint" t-att-data-order-id="order.id" t-att-data-Uid='order.uid'>
<span t-if="widget.pos.config.iface_reprint_done_order" class="button order-list-reprint" t-att-data-order-id="order.id" t-att-data-Uid='order.uid' title="Print a duplicate for this order">
<i class='fa fa-fw fa-print'/>
</span>
<span t-if="widget.pos.config.iface_copy_done_order" class="button order-list-copy" t-att-data-order-id="order.id" t-att-data-Uid='order.uid' title="Create a new order based on this one">
<i class='fa fa-fw fa-copy'/>
</span>
<span t-if="widget.pos.config.iface_return_done_order and order.amount_total >= 0" class="button order-list-return" t-att-data-order-id="order.id" t-att-data-Uid='order.uid' title="Create a refund order of this order">
<i class='fa fa-fw fa-undo'/>
</span>
</td>
</tr>
</t>
<t t-extend="OrderWidget">
<t t-jquery=".summary" t-operation="after">
<div class="order-returned-warning" t-if="order.returned_order_id">
<span>Returned order: </span><t name="returned-order-reference" t-esc="order.returned_order_reference"></t>
</div>
</t>
</t>
<t t-extend="PosTicket">
<t t-jquery="t[t-esc='order.name']" t-operation="after">
<t t-if="order.returned_order_id">
<br/>
<span name="returned-order-reference">Rectifies: </span><t t-esc="order.returned_order_reference"/>
</t>
</t>
<t t-jquery=".receipt-user" t-operation="after">
<t t-if="pos.reloaded_order">
<br/>
<div style='text-align:center'>DUPLICATE</div>
<br/>
</t>
</t>
</t>
<t t-extend="XmlReceipt">
<t t-jquery=".orderlines" t-operation="before">
<t t-if="pos.current_order">
<div>DUPLICATE</div>
<br/>
</t>
</t>
</t>
</templates>

1
pos_order_mgmt/tests/__init__.py

@ -0,0 +1 @@
from . import test_module

78
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

13
pos_order_mgmt/views/assets.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/pos_order_mgmt/static/src/js/widgets.js"/>
<script type="text/javascript" src="/pos_order_mgmt/static/src/js/models.js"/>
<link rel="stylesheet" href="/pos_order_mgmt/static/src/css/pos.css"/>
</xpath>
</template>
</odoo>

47
pos_order_mgmt/views/view_pos_config.xml

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2018 GRAP - Sylvain LE GAL
Copyright 2018 Tecnativa - David Vidal
Copyright 2019 Druidoo - Iván Todorovich
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_pos_config_form" model="ir.ui.view">
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
<field name="arch" type="xml">
<div id="receipt" position="inside">
<div class="col-xs-12 col-md-6 o_setting_box" id="pos_order_mgmt">
<div class="o_setting_left_pane">
<field name="iface_order_mgmt"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_order_mgmt"/>
<div class="text-muted">
Allows to manage already done orders in the frontend.
</div>
<div class="content-group mt16" attrs="{'invisible': [('iface_order_mgmt', '=', False)]}">
<div class="row mt16" id="reprint_done_order">
<label for="iface_reprint_done_order" class="col-lg-3 o_light_label"/>
<field name="iface_reprint_done_order"/>
</div>
<div class="row mt16" id="copy_done_order">
<label for="iface_return_done_order" class="col-lg-3 o_light_label"/>
<field name="iface_return_done_order"/>
</div>
<div class="row mt16" id="return_done_order">
<label for="iface_copy_done_order" class="col-lg-3 o_light_label"/>
<field name="iface_copy_done_order"/>
</div>
<div class="row mt16" id="load_done_order_max_qty">
<label for="iface_load_done_order_max_qty" class="col-lg-3 o_light_label"/>
<field name="iface_load_done_order_max_qty"/>
</div>
</div>
</div>
</div>
</div>
</field>
</record>
</odoo>

22
pos_order_mgmt/views/view_pos_order.xml

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2018 GRAP - Sylvain LE GAL
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_pos_order_form" model="ir.ui.view">
<field name="model">pos.order</field>
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"/>
<field name="arch" type="xml">
<field name="session_id" position="after">
<field name="returned_order_id" attrs="{'invisible': [('returned_order_id', '=', False)]}" />
</field>
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_refund_orders" type="object" icon="fa-undo" class="oe_stat_button" attrs="{'invisible': [('refund_order_qty', '=', 0)]}">
<field string="Refunds" name="refund_order_qty" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
</odoo>
Loading…
Cancel
Save