Browse Source

[MIG] pos_tare: Migration to 12.0

[ADD] barcode nomenclature for tare
[ADD] tare field on pos.order.line model
[ADD] Tare button in PoS numpad
[IMP] handle correctly different UoM
[IMP] add warning at checkout if qty <=0
[IMP] display gross and tare weight for each line

Co-authored-by: François Kawala <francois.kawala@gmail.com>
Co-authored-by: Sylvain LE GAL <sylvain.legal@grap.coop>
pull/491/head
Sylvain LE GAL 4 years ago
parent
commit
3e88ad3fa1
  1. 127
      pos_barcode_tare/README.rst
  2. 1
      pos_barcode_tare/__init__.py
  3. 24
      pos_barcode_tare/__manifest__.py
  4. 10
      pos_barcode_tare/demo/pos_barcode_tare_demo.xml
  5. 185
      pos_barcode_tare/i18n/fr_CH.po
  6. 165
      pos_barcode_tare/i18n/pos_barcode_tare.pot
  7. 2
      pos_barcode_tare/models/__init__.py
  8. 9
      pos_barcode_tare/models/pos_config.py
  9. 8
      pos_barcode_tare/pos_barcode_tare.xml
  10. 4
      pos_barcode_tare/readme/CONTRIBUTORS.rst
  11. 30
      pos_barcode_tare/readme/DESCRIPTION.rst
  12. 1
      pos_barcode_tare/readme/ROADMAP.rst
  13. 5
      pos_barcode_tare/readme/USAGE.rst
  14. BIN
      pos_barcode_tare/static/description/POS_with_button.png
  15. BIN
      pos_barcode_tare/static/description/icon.png
  16. BIN
      pos_barcode_tare/static/description/label.png
  17. BIN
      pos_barcode_tare/static/description/null_weight.png
  18. BIN
      pos_barcode_tare/static/description/ready_to_print.png
  19. BIN
      pos_barcode_tare/static/description/zoom_action_widget.png
  20. 123
      pos_barcode_tare/static/src/css/pos_barcode_tare.css
  21. 366
      pos_barcode_tare/static/src/js/pos_barcode_tare.js
  22. 86
      pos_barcode_tare/static/src/xml/pos_barcode_tare.xml
  23. 26
      pos_barcode_tare/views/pos_config_view.xml
  24. 69
      pos_tare/README.rst
  25. 2
      pos_tare/__init__.py
  26. 25
      pos_tare/__manifest__.py
  27. 19
      pos_tare/__openerp__.py
  28. 0
      pos_tare/data/barcode_rule.xml
  29. 13
      pos_tare/demo/pos_config.xml
  30. 68
      pos_tare/i18n/fr.po
  31. 3
      pos_tare/models/__init__.py
  32. 6
      pos_tare/models/barcode_rule.py
  33. 41
      pos_tare/models/pos_config.py
  34. 15
      pos_tare/models/pos_order_line.py
  35. 4
      pos_tare/readme/CONTRIBUTORS.rst
  36. 8
      pos_tare/readme/DESCRIPTION.rst
  37. 1
      pos_tare/readme/ROADMAP.rst
  38. 6
      pos_tare/readme/USAGE.rst
  39. BIN
      pos_tare/static/description/icon.png
  40. 59
      pos_tare/static/description/index.html
  41. BIN
      pos_tare/static/description/order.png
  42. BIN
      pos_tare/static/description/pos_tare.png
  43. BIN
      pos_tare/static/description/pos_ticket.png
  44. 86
      pos_tare/static/src/css/pos_tare.css
  45. 116
      pos_tare/static/src/js/models.js
  46. 140
      pos_tare/static/src/js/pos_tare.js
  47. 201
      pos_tare/static/src/js/screens.js
  48. 62
      pos_tare/static/src/js/tools.js
  49. 132
      pos_tare/static/src/xml/pos_tare.xml
  50. 27
      pos_tare/views/templates.xml
  51. 54
      pos_tare/views/view_pos_config.xml
  52. 16
      pos_tare/views/view_pos_order.xml

127
pos_barcode_tare/README.rst

@ -1,127 +0,0 @@
====================================================
Point of Sale - Tare barecode labels for loose goods
====================================================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! 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/9.0/pos_barcode_tare
:alt: OCA/pos
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/pos-9-0/pos-9-0-pos_barcode_tare
: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/9.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This add-on enables POS to read and print tare barcodes. We print a barcode tare label to sell loose goods in a Bring Your Own pot (BYOC) scheme.
The BYOC scheme has five steps:
1. The cashier weighs the pot and sticks the tare barcode onto the customer's pot.
2. The customer go and put loose goods into the labeled pot.
3. The cashier weighs the pot with loose goods inside. POS computes the price including the pot.
4. The cashier scans the tare barcode. POS get the pot weight from the barcode. POS subtracts the pot weight from the weight of the latest product. POS sets the billable price for the loose goods.
5. The customer pays.
This add-on adds a news screen to POS to print (web) the tare barcode labels. This add-on enables POS to read a tare barcode. Reading a barcode makes POS adjust the weight of the latest article in the order list. The new weight is equal to the total weight minus the tare weight. The price is updated accordingly to the weight change.
POS home screen is now:
.. image:: https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/POS_with_button.png
The label screen is:
.. image:: https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/null_weight.png
When we read a weight the impression button is enabled
.. image:: https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/ready_to_print.png
A barcode label looks like:
.. image:: https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/label.png
Zoom on the action widget:
.. image:: https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/zoom_action_widget.png
**Table of contents**
.. contents::
:local:
Usage
=====
Install this add-on and configure the point of sale where you want to be able to use the tare barecode. The label printing is done using web print. To streamline the label printing it is advised to use the silent printing mode (firefox) or the kiosk printing (chrome).
The command line to start a chrome base browser in kiosk mode with silent printing looks like:
``chromium-browser --use-system-default-printer --kiosk --kiosk-printing http://localhost:8069/``
Known issues / Roadmap
======================
- Merge with Sylvain LE GAL tare module that allows user to set tare on the scale screen.
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_barcode_tare%0Aversion:%209.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
~~~~~~~
* Le Nid
Contributors
~~~~~~~~~~~~
- Le Nid
- Sylvain LE GAL
- CoopITEasy
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.
.. |maintainer-fkawala| image:: https://github.com/fkawala.png?size=40px
:target: https://github.com/fkawala
:alt: fkawala
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-fkawala|
This module is part of the `OCA/pos <https://github.com/OCA/pos/tree/9.0/pos_barcode_tare>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

1
pos_barcode_tare/__init__.py

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

24
pos_barcode_tare/__manifest__.py

@ -1,24 +0,0 @@
# @author: François Kawala
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': "Point of Sale - Tare barecode labels for loose goods",
'version': '12.0.1.0.0',
'category': 'Point of Sale',
'summary': """Point of Sale - Print and scan tare \
barecodes labels to sell loose goods.""",
'author': "Le Nid, Odoo Community Association (OCA)",
'website': "https://github.com/OCA/pos",
'license': 'AGPL-3',
'maintainers': ['fkawala'],
'depends': ['point_of_sale'],
'demo': ['demo/pos_barcode_tare_demo.xml'],
'data': [
'pos_barcode_tare.xml',
'views/pos_config_view.xml',
'data/barcode_rule.xml',
],
'qweb': [
'static/src/xml/pos_barcode_tare.xml',
],
'installable': True,
}

10
pos_barcode_tare/demo/pos_barcode_tare_demo.xml

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="product_uom_kgm_dbg" model="uom.uom">
<field name="category_id" ref="uom.product_uom_categ_kgm"/>
<field name="name">Kg</field>
<field name="factor" eval="1"/>
<field name="rounding" eval="0.001"/>
<field name="uom_type">bigger</field>
</record>
</odoo>

185
pos_barcode_tare/i18n/fr_CH.po

@ -1,185 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_barcode_tare
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 9.0c\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-02-14 06:44+0000\n"
"PO-Revision-Date: 2020-02-14 06:44+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_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:8
#, python-format
msgid "Back"
msgstr "Retour"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:130
#, python-format
msgid "Failed to read weight from scale."
msgstr "Echec lors de la lecture du poids depuis la balance"
#. module: pos_barcode_tare
#: model:product.uom,name:pos_barcode_tare.product_uom_kgm_dbg
msgid "Kg"
msgstr "Kg"
#. module: pos_barcode_tare
#: model:ir.ui.view,arch_db:pos_barcode_tare.view_pos_config_form
msgid "Loose good options"
msgstr "Options pour la vente en vrac"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:196
#, python-format
msgid "Maximum tare weight is 99.999kg"
msgstr "Le poids mesuré est supérieur au poids maximum pour la tare: 99.999kg"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:18
#, python-format
msgid "Print"
msgstr "Imprimer l'étiquette"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:11
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:41
#, python-format
msgid "Print a tare label"
msgstr "Imprimer une étiquette de tare"
#. module: pos_barcode_tare
#: model:ir.model.fields,help:pos_barcode_tare.field_pos_config_iface_tare_label
msgid "Print tare labels with this POS"
msgstr "Utiliser ce POS pour imprimer les étiquettes de tare"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:15
#, python-format
msgid "Set the pot on the scale and check the weight above"
msgstr ""
"Placez le récipient à tarer sur la balance puis vérifiez le poids ci-dessus"
#. module: pos_barcode_tare
#: model:ir.model.fields,field_description:pos_barcode_tare.field_pos_config_iface_tare_label
msgid "Show tare label button"
msgstr "Utiliser ce POS pour imprimer les étiquettes de tare"
#. module: pos_barcode_tare
#: code:addons/pos_barcode_tare/models/barcode_rule.py:13
#, python-format
msgid "Tare"
msgstr "Tare"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:164
#, python-format
msgid ""
"The scale sent a measure in %s unit. This unit of measure (UOM) in not found "
"in the point of sale. You may need to create a new UOM named %s. The UOM "
"name is case sensitive."
msgstr ""
"La balance à envoyé une mesure en %s. Cette unité de mesure (UOM) n'existe "
"pas. Vous devez probablement créer une UOM nommée %s. Le nom de l'UOM est "
"sensible à la casse."
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:288
#, python-format
msgid ""
"The tare (%s) is already set for the product \"%s\". We can not re-apply a "
"tare to this product."
msgstr ""
"La tare (%s) est déjà mesurée pour le produit \"%s\". Nous ne pouvons plus "
"appliquer de tare à ce produit."
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:319
#, python-format
msgid ""
"The tare weight is %s %s, it's greater or equal to the product weight %s. We "
"can not apply this tare."
msgstr ""
"Le poids de la tare est de %s %s, c'est plus que le poids du produit %s. "
"Nous ne pouvons pas appliquer cette tare."
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:74
#, python-format
msgid "We can not apply this tare barcode."
msgstr "Impossible d'appliquer cette étiquette de tare."
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:33
#, python-format
msgid "We can not cast a weight in %s into %s."
msgstr "Nous ne pouvons pas convertir un poids en %s en %s."
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:69
#, python-format
msgid "With a"
msgstr "Avec un(e)"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:61
#, python-format
msgid "at"
msgstr "à"
#. module: pos_barcode_tare
#: model:ir.model,name:pos_barcode_tare.model_barcode_rule
msgid "barcode.rule"
msgstr "barcode.rule"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:71
#, python-format
msgid "discount"
msgstr "remise"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:32
#, python-format
msgid "kg"
msgstr "kg"
#. module: pos_barcode_tare
#: model:ir.model,name:pos_barcode_tare.model_pos_config
msgid "pos.config"
msgstr "pos.config"
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:32
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:78
#, python-format
msgid "tare ="
msgstr "tare ="
#~ msgid "Weight"
#~ msgstr "Poids"

165
pos_barcode_tare/i18n/pos_barcode_tare.pot

@ -1,165 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_barcode_tare
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 9.0c\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_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:8
#, python-format
msgid "Back"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:130
#, python-format
msgid "Failed to read weight from scale."
msgstr ""
#. module: pos_barcode_tare
#: model:product.uom,name:pos_barcode_tare.product_uom_kgm_dbg
msgid "Kg"
msgstr ""
#. module: pos_barcode_tare
#: model:ir.ui.view,arch_db:pos_barcode_tare.view_pos_config_form
msgid "Loose good options"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:196
#, python-format
msgid "Maximum tare weight is 99.999kg"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:18
#, python-format
msgid "Print"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:11
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:41
#, python-format
msgid "Print a tare label"
msgstr ""
#. module: pos_barcode_tare
#: model:ir.model.fields,help:pos_barcode_tare.field_pos_config_iface_tare_label
msgid "Print tare labels with this POS"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:15
#, python-format
msgid "Set the pot on the scale and check the weight above"
msgstr ""
#. module: pos_barcode_tare
#: model:ir.model.fields,field_description:pos_barcode_tare.field_pos_config_iface_tare_label
msgid "Show tare label button"
msgstr ""
#. module: pos_barcode_tare
#: code:addons/pos_barcode_tare/models/barcode_rule.py:13
#, python-format
msgid "Tare"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:164
#, python-format
msgid "The scale sent a measure in %s unit. This unit of measure (UOM) in not found in the point of sale. You may need to create a new UOM named %s. The UOM name is case sensitive."
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:288
#, python-format
msgid "The tare (%s) is already set for the product \"%s\". We can not re-apply a tare to this product."
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:319
#, python-format
msgid "The tare weight is %s %s, it's greater or equal to the product weight %s. We can not apply this tare."
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:74
#, python-format
msgid "We can not apply this tare barcode."
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/js/pos_barcode_tare.js:33
#, python-format
msgid "We can not cast a weight in %s into %s."
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:69
#, python-format
msgid "With a"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:61
#, python-format
msgid "at"
msgstr ""
#. module: pos_barcode_tare
#: model:ir.model,name:pos_barcode_tare.model_barcode_rule
msgid "barcode.rule"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:71
#, python-format
msgid "discount"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:32
#, python-format
msgid "kg"
msgstr ""
#. module: pos_barcode_tare
#: model:ir.model,name:pos_barcode_tare.model_pos_config
msgid "pos.config"
msgstr ""
#. module: pos_barcode_tare
#. openerp-web
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:32
#: code:addons/pos_barcode_tare/static/src/xml/pos_barcode_tare.xml:78
#, python-format
msgid "tare ="
msgstr ""

2
pos_barcode_tare/models/__init__.py

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

9
pos_barcode_tare/models/pos_config.py

@ -1,9 +0,0 @@
from odoo import models, fields
class PosConfig(models.Model):
_inherit = 'pos.config'
iface_tare_label = fields.Boolean(
'Show tare label button',
help="Print tare labels with this POS"
)

8
pos_barcode_tare/pos_barcode_tare.xml

@ -1,8 +0,0 @@
<odoo>
<template id="assets_backend" name="pos_barcode_tare" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/pos_barcode_tare/static/src/css/pos_barcode_tare.css"/>
<script type="text/javascript" src="/pos_barcode_tare/static/src/js/pos_barcode_tare.js"></script>
</xpath>
</template>
</odoo>

4
pos_barcode_tare/readme/CONTRIBUTORS.rst

@ -1,4 +0,0 @@
- Le Nid
- Sylvain LE GAL
- CoopITEasy

30
pos_barcode_tare/readme/DESCRIPTION.rst

@ -1,30 +0,0 @@
This add-on enables POS to read and print tare barcodes. We print a barcode tare label to sell loose goods in a Bring Your Own pot (BYOC) scheme.
The BYOC scheme has five steps:
1. The cashier weighs the pot and sticks the tare barcode onto the customer's pot.
2. The customer go and put loose goods into the labeled pot.
3. The cashier weighs the pot with loose goods inside. POS computes the price including the pot.
4. The cashier scans the tare barcode. POS get the pot weight from the barcode. POS subtracts the pot weight from the weight of the latest product. POS sets the billable price for the loose goods.
5. The customer pays.
This add-on adds a news screen to POS to print (web) the tare barcode labels. This add-on enables POS to read a tare barcode. Reading a barcode makes POS adjust the weight of the latest article in the order list. The new weight is equal to the total weight minus the tare weight. The price is updated accordingly to the weight change.
POS home screen is now:
.. image:: ../static/description/POS_with_button.png
The label screen is:
.. image:: ../static/description/null_weight.png
When we read a weight the impression button is enabled
.. image:: ../static/description/ready_to_print.png
A barcode label looks like:
.. image:: ../static/description/label.png
Zoom on the action widget:
.. image:: ../static/description/zoom_action_widget.png

1
pos_barcode_tare/readme/ROADMAP.rst

@ -1 +0,0 @@
- Merge with Sylvain LE GAL tare module that allows user to set tare on the scale screen.

5
pos_barcode_tare/readme/USAGE.rst

@ -1,5 +0,0 @@
Install this add-on and configure the point of sale where you want to be able to use the tare barecode. The label printing is done using web print. To streamline the label printing it is advised to use the silent printing mode (firefox) or the kiosk printing (chrome).
The command line to start a chrome base browser in kiosk mode with silent printing looks like:
``chromium-browser --use-system-default-printer --kiosk --kiosk-printing http://localhost:8069/``

BIN
pos_barcode_tare/static/description/POS_with_button.png

Before

Width: 960  |  Height: 540  |  Size: 198 KiB

BIN
pos_barcode_tare/static/description/icon.png

Before

Width: 128  |  Height: 128  |  Size: 9.2 KiB

BIN
pos_barcode_tare/static/description/label.png

Before

Width: 576  |  Height: 393  |  Size: 7.4 KiB

BIN
pos_barcode_tare/static/description/null_weight.png

Before

Width: 960  |  Height: 540  |  Size: 20 KiB

BIN
pos_barcode_tare/static/description/ready_to_print.png

Before

Width: 960  |  Height: 540  |  Size: 21 KiB

BIN
pos_barcode_tare/static/description/zoom_action_widget.png

Before

Width: 437  |  Height: 292  |  Size: 15 KiB

123
pos_barcode_tare/static/src/css/pos_barcode_tare.css

@ -1,123 +0,0 @@
.pos .print-label.disabled {
background: #e2e2e2;
border: solid 1px #BEBEBE;
opacity: 0.5;
cursor: default;
color: inherit;
}
.pos .tare-screen .centered-content {
text-align:center;
}
@media screen {
.pos .tare-screen .pos-tare-label-container {
width: 300px;
background-color: white;
margin: 20px;
padding: 15px;
font-size: 21px;
padding-bottom:30px;
font-family: "Inconsolata";
border: solid 1px rgb(220,220,220);
border-radius: 3px;
overflow: hidden;
text-align: center;
display: inline-block;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}
.pos .tare-screen .pos-tare-label-container img {
width: 50mm;
height: 45mm;
}
}
.pos .tare-screen .pos-tare-label-container .caption {
display: block;
text-overflow: ellipsis;
flex-basis: 100vw;
}
.pos .tare-screen .pos-directions-for-user {
font-size: 25px;
margin: 8px;
text-align: center;
line-height: 2;
}
.pos .tare-screen .pos-directions-for-user span {
width:100px;
height: 50px;
background-color: rgb(49,174,218);
color: white !important;
font-weight: bold;
border: solid 1px black;
border-radius: 90% 30%;
display: flex;
align-items: center;
overflow: hidden;
vertical-align:middle;
justify-content: center;
margin-left: auto;
margin-right: auto;
}
.pos .tare-screen .centered-content .print-label {
text-align: center;
font-size: 32px;
background: rgb(110,200,155);
color: white;
border-radius: 3px;
padding: 16px;
margin: 16px;
cursor: pointer;
}
@media print {
body {
position: relative !important;
}
.pos .tare-screen header,
.pos .tare-screen .top-content,
.pos .tare-screen .print-label,
.pos .tare-screen .pos-directions-for-user {
display: none !important;
}
.pos .tare-screen .screen-content {
max-width: unset !important;
}
.pos .tare-screen .centered-content {
position: static;
text-align: center;
border: none;
width: 100vw;
height: 100vh;
margin: 0mm;
page-break-inside: avoid;
overflow: hidden;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
left: unset !important;
right: unset !important;
}
.pos .tare-screen .centered-content .pos-tare-label-container img {
width: 90vw;
height: 70vh;
}
.pos .tare-screen .centered-content .pos-tare-label-container .caption {
text-overflow: ellipsis;
font-size: 5vh;
flex-basis: 100%;
}
}

366
pos_barcode_tare/static/src/js/pos_barcode_tare.js

@ -1,366 +0,0 @@
odoo.define('pos_barcode_tare.screens', function (require) {
"use strict";
var core = require('web.core');
var gui = require('point_of_sale.gui');
var models = require('point_of_sale.models');
var screens = require('point_of_sale.screens');
var utils = require('web.utils');
var field_utils = require('web.field_utils');
var QWeb = core.qweb;
var _t = core._t;
var round_pr = utils.round_precision;
var round_di = utils.round_decimals;
var tare_barcode_type = "tare";
// Define functions used to do unit operation.
// Get unit search for unit based on unit name.
var get_unit = function (pos, unit_name) {
return pos.units.filter(
function (u) {
return u.name === unit_name;
})[0];
};
// Convert mass using the reference UOM as pivot unit.
var convert_mass = function (mass, from_unit, to_unit) {
// There is no conversion from one category to another.
if (from_unit.category_id[0] !== to_unit.category_id[0]) {
throw new Error(_.str.sprintf(
_t("We can not cast a weight in %s into %s."),
from_unit.name, to_unit.name));
}
// No need to convert as weights are measured in same unit.
if (from_unit.id === to_unit.id) {
return mass;
}
// Converts "from_unit" to reference unit of measure.
var result = mass;
if (from_unit.uom_type === "bigger") {
result /= from_unit.factor;
} else {
result *= from_unit.factor_inv;
}
// Converts reference unit of measure to "to_unit".
if (to_unit.uom_type === "bigger") {
result *= to_unit.factor;
} else {
result /= to_unit.factor_inv;
}
if (to_unit.rounding) {
// Return the rounded result if needed.
return round_pr(result || 0, to_unit.rounding);
}
return result || 0;
};
// Format the tare value.
var format_tare = function (pos, qty, unit) {
if (unit.rounding) {
var q = round_pr(qty, unit.rounding);
var decimals = pos.dp['Product Unit of Measure'];
return field_utils.format.float(
round_di(q, decimals),
{type: 'float', digits: [69, decimals]});
}
return qty.toFixed(0);
};
// This configures read action for tare barcode. A tare barcode contains a
// fake product ID and the weight to be subtracted from the product in the
// latest order line.
screens.ScreenWidget.include(
{
barcode_tare_action: function (code) {
try {
var order = this.pos.get_order();
var selected_order_line = order.get_selected_orderline();
var tare_weight = code.value;
selected_order_line.set_tare(tare_weight);
} catch (error) {
var title = _t("We can not apply this tare barcode.");
var popup = {title: title, body: error.message};
this.gui.show_popup('error', popup);
}
},
// Setup the callback action for the "weight" barcodes.
show: function () {
this._super();
this.pos.barcode_reader.set_action_callback(
'tare',
_.bind(this.barcode_tare_action, this));
},
});
// This create a new button on top of action widget. This button links to
// the barcode label printing screen defined below.
var TareScreenButton = screens.ActionButtonWidget.extend({
template: 'TareScreenButton',
button_click: function () {
this.gui.show_screen('tare');
},
});
screens.define_action_button({
'name': 'tareScreenButton',
'widget': TareScreenButton,
});
// This is a new screen that reads weight from the electronic scale and
// create a barcode label encoding the weight. The screen shows a preview
// of the label. The user is expected to check if the preview matches what's
// measured on the scale. The barcode image is generated by the report
// module.
var TareScreenWidget = screens.ScreenWidget.extend({
template: 'TareScreenWidget',
next_screen: 'products',
previous_screen: 'products',
default_tare_value: 0.0,
weight_barcode_prefix: null,
show: function () {
this._super();
// Fetch the unit of measure used to save the tare
this.kg_unit = get_unit(this.pos, "kg");
// Fetch the barcode prefix from POS barcode parser rules.
this.weight_barcode_prefix = this.get_barcode_prefix();
// Setup the proxy
var queue = this.pos.proxy_queue;
// The pooling of the scale starts here.
var self = this;
queue.schedule(function () {
return self.pos.proxy.scale_read().then(function (weight) {
try {
self.set_weight(weight);
} catch (error) {
var title = _t("Failed to read weight from scale.");
var popup = {title: title, body: error.message};
self.gui.show_popup('error', popup);
}
});
}, {duration:150, repeat: true});
// Shows a barcode whose weight might be zero, but this is preferred
// for UI/UX reasons.
this.render_receipt();
this.lock_screen(true);
},
get_barcode_prefix: function () {
var barcode_pattern = this.get_barcode_pattern();
return barcode_pattern.substr(0, 2);
},
get_barcode_pattern: function () {
var rules = this.get_barcode_rules();
var rule = rules.filter(
function (r) {
// We select the first (smallest sequence ID) barcode rule
// with the expected type.
return r.type === tare_barcode_type;
})[0];
return rule.pattern;
},
get_barcode_rules: function () {
return this.pos.barcode_reader.barcode_parser.nomenclature.rules;
},
set_weight: function (scale_measure) {
var weight = scale_measure.weight;
var unit = get_unit(this.pos, scale_measure.unit);
if (typeof unit === 'undefined') {
throw new Error(_.str.sprintf(
_t("The scale sent a measure in %s unit. This unit of "+
"measure (UOM) in not found in the point of sale. You " +
"may need to create a new UOM named %s. The UOM name is "+
"case sensitive."), scale_measure.unit,
scale_measure.unit));
}
if (weight > 0) {
this.weight_in_kg = convert_mass(weight, unit, this.kg_unit);
this.render_receipt();
this.lock_screen(false);
}
},
get_weight: function () {
if (typeof this.weight_in_kg === 'undefined') {
return this.default_tare_value;
}
return this.weight_in_kg;
},
barcode_data: function (weight) {
// We use EAN13 barcode, it looks like 07 00000 12345 x. First there
// is the prefix, here 07, that is used to decide which type of
// barcode we're dealing with. A weight barcode has then two groups
// of five digits. The first group encodes the product id. Here the
// product id is 00000. The second group encodes the weight in
// grams. Here the weight is 12.345kg. The last digit of the barcode
// is a checksum, here symbolized by x.
var padding_size = 5;
var void_product_id = '0'.repeat(padding_size);
var weight_in_gram = weight * 1e3;
if (weight_in_gram >= Math.pow(10, padding_size)) {
throw new RangeError(_t("Maximum tare weight is 99.999kg"));
}
// Weight has to be padded with zeros.
var weight_with_padding = '0'.repeat(padding_size) + weight_in_gram;
var padded_weight = weight_with_padding.substr(
weight_with_padding.length - padding_size);
// Builds the barcode using a placeholder checksum.
var barcode = this.weight_barcode_prefix
.concat(void_product_id, padded_weight)
.concat(0);
// Compute checksum
var barcode_parser = this.pos.barcode_reader.barcode_parser;
var checksum = barcode_parser.ean_checksum(barcode);
// Replace checksum placeholder by the actual checksum.
return barcode.substr(0, 12).concat(checksum);
},
get_barcode_data: function () {
return this.barcode_data(this.get_weight());
},
lock_screen: function (locked) {
this._locked = locked;
if (locked) {
this.$('.print-label').addClass('disabled');
} else {
this.$('.print-label').removeClass('disabled');
}
},
print_web: function () {
window.print();
// TODO check this
this.pos.get_order()._printed = true;
},
print: function () {
// See comment in print function of ReceiptScreenWidget
this.lock_screen(true);
var self = this;
setTimeout(function () {
self.lock_screen(false);
}, 1000);
this.print_web();
this.click_back();
},
click_back: function () {
this.close();
this.gui.show_screen(this.previous_screen);
},
renderElement: function () {
this._super();
var self = this;
this.$('.back').click(function () {
self.click_back();
});
this.$('.print-label').click(function () {
if (!self._locked) {
self.print();
}
});
},
render_receipt: function () {
this.$('.pos-tare-label-container').html(
QWeb.render('PosTareLabel', {widget:this}));
},
close: function () {
this._super();
delete this.weight;
this.pos.proxy_queue.clear();
},
get_tare_str: function () {
return format_tare(this.pos, this.get_weight(),
get_unit(this.pos, "kg"));
},
});
gui.define_screen({name:'tare', widget: TareScreenWidget});
// Update Orderline model
var _super_ = models.Orderline.prototype;
var OrderLineWithTare = models.Orderline.extend({
initialize: function (session, attributes) {
this.tareQuantity = 0;
this.tareQuantityStr = '0';
return _super_.initialize.call(this, session, attributes);
},
init_from_JSON: function (json) {
_super_.init_from_JSON.call(this, json);
this.tareQuantity = json.tareQuantity ||0;
this.tareQuantityStr = json.tareQuantityStr ||'0';
},
set_tare: function (quantity) {
this.order.assert_editable();
// Prevent to apply multiple times a tare to the same product.
if (this.get_tare() > 0) {
throw new RangeError(_.str.sprintf(
_t("The tare (%s) is already set for the " +
"product \"%s\". We can not re-apply a tare to this " +
"product."),
this.get_tare_str_with_unit(), this.product.display_name));
}
// We convert the tare that is always measured in kilogrammes into
// the unit of measure for this order line.
var kg = get_unit(this.pos, "kg");
var tare = parseFloat(quantity) || 0;
var unit = this.get_unit();
var tare_in_product_uom = convert_mass(tare, kg, unit);
var tare_in_product_uom_string = format_tare(this.pos,
tare_in_product_uom, unit);
var net_quantity = this.get_quantity() - tare_in_product_uom;
// This method fails when the net weight is negative.
if (net_quantity <= 0) {
throw new RangeError(_.str.sprintf(
_t("The tare weight is %s %s, it's greater or equal to " +
"the product weight %s. We can not apply this tare."),
tare_in_product_uom_string, unit.name,
this.get_quantity_str_with_unit()));
}
// Update tare value.
this.tareQuantity = tare_in_product_uom;
this.tareQuantityStr = tare_in_product_uom_string;
// Update the quantity with the new weight net of tare quantity.
this.set_quantity(net_quantity);
this.trigger('change', this);
},
get_tare: function () {
return this.tareQuantity;
},
get_tare_str: function () {
return this.tareQuantityStr;
},
get_tare_str_with_unit: function () {
var unit = this.get_unit();
return this.tareQuantityStr + ' ' + unit.name;
},
export_as_JSON: function () {
var json = _super_.export_as_JSON.call(this);
json.tareQuantity = this.get_tare();
json.tareQuantityStr = this.get_tare_str();
return json;
},
clone: function () {
var orderline = _super_.clone.call(this);
orderline.tareQuantity = this.tareQuantity;
orderline.tareQuantityStr = this.tareQuantityStr;
return orderline;
},
export_for_printing: function () {
var result = _super_.export_for_printing.call(this);
result.tare_quantity = this.get_tare();
return result;
},
});
models.Orderline = OrderLineWithTare;
return {TareScreenWidget: TareScreenWidget,
OrderLineWithTare: OrderLineWithTare,
get_unit: get_unit};
});

86
pos_barcode_tare/static/src/xml/pos_barcode_tare.xml

@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="TareScreenWidget">
<div class='tare-screen screen'>
<div class='screen-content'>
<div class='top-content'>
<span class='button back'>
<i class='fa fa-angle-double-left'></i>
Back
</span>
<h1>Print a tare label</h1>
</div>
<div class="centered-content">
<div class="pos-tare-label-container"></div>
<div class="pos-directions-for-user">
Set the pot on the scale and check the weight above
</div>
<div class='print-label'>
Print
<i class='fa fa-angle-double-right'></i>
</div>
</div>
</div>
</div>
</t>
<t t-name="PosTareLabel">
<div class="pos-center-align">
<div class="pos-tare-paper">
<div class="pos-tare-label">
<img t-att-src="'/report/barcode/EAN13/' + widget.get_barcode_data()" />
<span class="caption">tare = <t t-esc="widget.get_weight()" />kg</span>
</div>
</div>
</div>
</t>
<t t-name="TareScreenButton">
<t t-if="widget.pos.config.iface_tare_label">
<span class="control-button">
<i class="fa fa-print"></i>
Print a tare label
</span>
</t>
</t>
<t t-name="Orderline">
<li t-attf-class="orderline #{ line.selected ? 'selected' : '' }">
<span class="product-name">
<t t-esc="line.get_product().display_name"/>
</span>
<span class="price">
<t t-esc="widget.format_currency(line.get_display_price())"/>
</span>
<ul class="info-list">
<t t-if="line.get_quantity_str() !== '1' || line.selected ">
<li class="info">
<em>
<t t-esc="line.get_quantity_str()" />
</em>
<t t-esc="line.get_unit().name" />
at
<t t-esc="widget.format_currency(line.get_unit_display_price(),'Product Price')" />
/
<t t-esc="line.get_unit().name" />
</li>
</t>
<t t-if="line.get_discount_str() !== '0'">
<li class="info">
With a
<em>
<t t-esc="line.get_discount_str()" />%
</em>
discount
</li>
</t>
<t t-if="line.get_tare_str() !== '0'">
<li class="info">
tare = <t t-esc="line.get_tare_str_with_unit()" />
</li>
</t>
</ul>
</li>
</t>
</templates>

26
pos_barcode_tare/views/pos_config_view.xml

@ -1,26 +0,0 @@
<?xml version="1.0"?>
<odoo>
<record model="ir.ui.view" id="view_pos_config_form">
<field name="name">view.pos.config.form</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form" />
<field name="priority" eval="30"/>
<field name="arch" type="xml">
<div id="posbox_reference" position="inside">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="iface_tare_label"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_tare_label"/>
<div class="text-muted">
Show tare barcode printer button on top of the num pad.
</div>
</div>
</div>
</div>
</field>
</record>
</odoo>

69
pos_tare/README.rst

@ -13,38 +13,56 @@ Point Of Sale - Tare
.. |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-grap%2Fgrap--odoo--business-lightgray.png?logo=github
:target: https://github.com/grap/grap-odoo-business/tree/8.0/pos_tare
:alt: grap/grap-odoo-business
|badge1| |badge2| |badge3|
Give the possibility to the user to provide Gross weight and Tare weight.
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github
:target: https://github.com/OCA/pos/tree/12.0/pos_tare
: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_tare
: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|
Give the possibility to the user to enter a Tare weight, when weighting
products in the Point of Sale.
This will compute automatically net weight and set it to the current
selected order
.. figure:: https://raw.githubusercontent.com/grap/grap-odoo-business/8.0/pos_tare/static/description/pos_tare.png
.. figure:: https://raw.githubusercontent.com/OCA/pos/12.0/pos_tare/static/description/pos_tare.png
The net weight is displayed in the order with the tare value below.
The screen will be displayed only for 'to_weight' products and if scale is
enabled in PoS Configuration.
.. figure:: https://raw.githubusercontent.com/OCA/pos/12.0/pos_tare/static/description/order.png
**Table of contents**
.. contents::
:local:
Usage
=====
Install this add-on and configure your point of sale. To enable this addon, go to your point of sale configuration page. There, enable the electronic scale and barcode reader in the "IoT Box" section. In the same page, look up for the "Tare input method" field, and choose a tare method. There are three tare methods:
- "manual", you'll set the tare value when you weight the product;
- "barcode", you'll scan the tare value from a barcode;
- "both", you can both of the above.
To generate a tare barcode you need to use the default barcode nomenclature. The default barcode pattern is `0700000{NNDDD}`. Using that pattern, the barcode for a tare of 0.1kg is `0700000001006`. The `pos_tare_barcode_generator` allows you to create tare labels right from the POS.
Known issues / Roadmap
======================
For the time being, this module disable Scale functionnality.
- Print tare value on customer receipt.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/grap/grap-odoo-business/issues>`_.
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/grap/grap-odoo-business/issues/new?body=module:%20pos_tare%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
`feedback <https://github.com/OCA/pos/issues/new?body=module:%20pos_tare%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.
@ -55,18 +73,37 @@ Authors
~~~~~~~
* GRAP
* Le Nid
Contributors
~~~~~~~~~~~~
* Sylvain LE GAL (https://www.twitter.com/legalsylvain)
- Sylvain LE GAL (https://www.twitter.com/legalsylvain)
- Le Nid
- CoopITEasy
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.
.. |maintainer-fkawala| image:: https://github.com/fkawala.png?size=40px
:target: https://github.com/fkawala
:alt: fkawala
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
This module is part of the `grap/grap-odoo-business <https://github.com/grap/grap-odoo-business/tree/8.0/pos_tare>`_ project on GitHub.
|maintainer-fkawala|
This module is part of the `OCA/pos <https://github.com/OCA/pos/tree/12.0/pos_tare>`_ project on GitHub.
You are welcome to contribute.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

2
pos_tare/__init__.py

@ -1 +1 @@
# coding: utf-8
from . import models

25
pos_tare/__manifest__.py

@ -0,0 +1,25 @@
# @author: François Kawala
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Point Of Sale - Tare",
"summary": "Manage Tare in Point Of Sale module",
"version": "12.0.1.0.0",
"category": "Point of Sale",
"author": "GRAP, Le Nid, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/pos",
"license": "AGPL-3",
"maintainers": ["fkawala", "legalsylvain"],
"depends": [
"point_of_sale",
],
"data": [
"views/templates.xml",
"views/view_pos_config.xml",
"views/view_pos_order.xml",
"data/barcode_rule.xml",
],
"qweb": [
"static/src/xml/pos_tare.xml",
],
"installable": True,
}

19
pos_tare/__openerp__.py

@ -1,19 +0,0 @@
# coding: utf-8
# Copyright (C) 2015-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).
{
"name": "Point Of Sale - Tare",
"summary": "Manage Tare in Point Of Sale module",
"version": "8.0.1.0.0",
"category": "Point Of Sale",
"author": "GRAP",
"website": "http://www.grap.coop",
"license": "AGPL-3",
"depends": ["point_of_sale",],
"data": ["views/templates.xml",],
"demo": ["demo/pos_config.xml",],
"qweb": ["static/src/xml/pos_tare.xml",],
"installable": False,
}

0
pos_barcode_tare/data/barcode_rule.xml → pos_tare/data/barcode_rule.xml

13
pos_tare/demo/pos_config.xml

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2018 - 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).
-->
<openerp><data>
<record id="point_of_sale.pos_config_main" model="pos.config">
<field name="iface_electronic_scale" eval="1" />
</record>
</data></openerp>

68
pos_tare/i18n/fr.po

@ -1,68 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_tare
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-06-21 10:31+0000\n"
"PO-Revision-Date: 2016-06-21 10:31+0000\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_tare
#. openerp-web
#: code:addons/pos_tare/static/src/js/pos_tare.js:65
#, python-format
msgid "Back"
msgstr "Retour"
#. module: pos_tare
#. openerp-web
#: code:addons/pos_tare/static/src/xml/pos_tare.xml:17
#: code:addons/pos_tare/static/src/xml/pos_tare.xml:18
#, python-format
msgid "Gross Weight"
msgstr "Poids Brut"
#. module: pos_tare
#. openerp-web
#: code:addons/pos_tare/static/src/xml/pos_tare.xml:31
#, python-format
msgid "Net Weight"
msgstr "Poids Net"
#. module: pos_tare
#. openerp-web
#: code:addons/pos_tare/static/src/js/pos_tare.js:73
#, python-format
msgid "Order"
msgstr "Commander"
#. module: pos_tare
#. openerp-web
#: code:addons/pos_tare/static/src/xml/pos_tare.xml:21
#: code:addons/pos_tare/static/src/xml/pos_tare.xml:22
#, python-format
msgid "Tare"
msgstr "Tare"
#. module: pos_tare
#. openerp-web
#: code:addons/pos_tare/static/src/xml/pos_tare.xml:35
#, python-format
msgid "Total Price"
msgstr "Prix Total"
#. module: pos_tare
#. openerp-web
#: code:addons/pos_tare/static/src/xml/pos_tare.xml:26
#, python-format
msgid "Unit Price"
msgstr "Prix Unitaire"

3
pos_tare/models/__init__.py

@ -0,0 +1,3 @@
from . import pos_config
from . import pos_order_line
from . import barcode_rule

6
pos_barcode_tare/models/barcode_rule.py → pos_tare/models/barcode_rule.py

@ -1,9 +1,9 @@
from odoo import _, models, fields
from odoo import models, fields
class BarcodeRule(models.Model):
_inherit = 'barcode.rule'
type = fields.Selection(selection_add=[
('tare', _('Tare'))
('tare', 'Tare')
])

41
pos_tare/models/pos_config.py

@ -0,0 +1,41 @@
from odoo import api, models, fields
class PosConfig(models.Model):
_inherit = "pos.config"
iface_tare_method = fields.Selection(
[
("manual", "Input the tare manually"),
("barcode", "Scan a barcode to set the tare"),
("both", "Manual input and barcode"),
],
string="Tare Input Method",
default="both",
required=True,
help="Select tare method:\n"
"* 'manual' : the scale screen has an extra tare input field;\n"
"* 'barecode' : (scan a barcode to tare the selected order line;\n"
"* 'both' : manual input and barcode methods are enabled;",
)
iface_gross_weight_method = fields.Selection(
[
("manual", "Input the Gross Weight manually"),
("scale", "Input Gross Weight via Scale")
],
string="Gross Weight Input Method",
default="scale",
required=True,
)
iface_tare_uom_id = fields.Many2one(
string="Unit of Measure of the tare",
comodel_name="uom.uom",
default=lambda s: s._default_iface_tare_uom_id(),
required=True,
)
@api.model
def _default_iface_tare_uom_id(self):
return self.env.ref("uom.product_uom_kgm")

15
pos_tare/models/pos_order_line.py

@ -0,0 +1,15 @@
# Copyright (C) 2020-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 models, fields
from odoo.addons import decimal_precision as dp
class PosOrderLine(models.Model):
_inherit = "pos.order.line"
tare = fields.Float(
string="Tare",
digits=dp.get_precision("Product Unit of Measure")
)

4
pos_tare/readme/CONTRIBUTORS.rst

@ -1 +1,3 @@
* Sylvain LE GAL (https://www.twitter.com/legalsylvain)
- Sylvain LE GAL (https://www.twitter.com/legalsylvain)
- Le Nid
- CoopITEasy

8
pos_tare/readme/DESCRIPTION.rst

@ -1,8 +1,10 @@
Give the possibility to the user to provide Gross weight and Tare weight.
Give the possibility to the user to enter a Tare weight, when weighting
products in the Point of Sale.
This will compute automatically net weight and set it to the current
selected order
.. figure:: ../static/description/pos_tare.png
The screen will be displayed only for 'to_weight' products and if scale is
enabled in PoS Configuration.
The net weight is displayed in the order with the tare value below.
.. figure:: ../static/description/order.png

1
pos_tare/readme/ROADMAP.rst

@ -1 +0,0 @@
For the time being, this module disable Scale functionnality.

6
pos_tare/readme/USAGE.rst

@ -0,0 +1,6 @@
Install this add-on and configure your point of sale. To enable this addon, go to your point of sale configuration page. There, enable the electronic scale and barcode reader in the "IoT Box" section. In the same page, look up for the "Tare input method" field, and choose a tare method. There are three tare methods:
- "manual", you'll set the tare value when you weight the product;
- "barcode", you'll scan the tare value from a barcode;
- "both", you can both of the above.
To generate a tare barcode you need to use the default barcode nomenclature. The default barcode pattern is `0700000{NNDDD}`. Using that pattern, the barcode for a tare of 0.1kg is `0700000001006`. The `pos_tare_barcode_generator` allows you to create tare labels right from the POS.

BIN
pos_tare/static/description/icon.png

Before

Width: 64  |  Height: 64  |  Size: 3.3 KiB

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

59
pos_barcode_tare/static/description/index.html → pos_tare/static/description/index.html

@ -4,7 +4,7 @@
<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>Point of Sale - Tare barecode labels for loose goods</title>
<title>Point Of Sale - Tare</title>
<style type="text/css">
/*
@ -360,37 +360,25 @@ ul.auto-toc {
</style>
</head>
<body>
<div class="document" id="point-of-sale-tare-barecode-labels-for-loose-goods">
<h1 class="title">Point of Sale - Tare barecode labels for loose goods</h1>
<div class="document" id="point-of-sale-tare">
<h1 class="title">Point Of Sale - Tare</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/9.0/pos_barcode_tare"><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-9-0/pos-9-0-pos_barcode_tare"><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/9.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This add-on enables POS to read and print tare barcodes. We print a barcode tare label to sell loose goods in a Bring Your Own pot (BYOC) scheme.</p>
<dl class="docutils">
<dt>The BYOC scheme has five steps:</dt>
<dd><ol class="first last arabic simple">
<li>The cashier weighs the pot and sticks the tare barcode onto the customer’s pot.</li>
<li>The customer go and put loose goods into the labeled pot.</li>
<li>The cashier weighs the pot with loose goods inside. POS computes the price including the pot.</li>
<li>The cashier scans the tare barcode. POS get the pot weight from the barcode. POS subtracts the pot weight from the weight of the latest product. POS sets the billable price for the loose goods.</li>
<li>The customer pays.</li>
</ol>
</dd>
</dl>
<p>This add-on adds a news screen to POS to print (web) the tare barcode labels. This add-on enables POS to read a tare barcode. Reading a barcode makes POS adjust the weight of the latest article in the order list. The new weight is equal to the total weight minus the tare weight. The price is updated accordingly to the weight change.</p>
<p>POS home screen is now:</p>
<img alt="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/POS_with_button.png" src="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/POS_with_button.png" />
<p>The label screen is:</p>
<img alt="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/null_weight.png" src="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/null_weight.png" />
<p>When we read a weight the impression button is enabled</p>
<img alt="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/ready_to_print.png" src="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/ready_to_print.png" />
<p>A barcode label looks like:</p>
<img alt="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/label.png" src="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/label.png" />
<p>Zoom on the action widget:</p>
<img alt="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/zoom_action_widget.png" src="https://raw.githubusercontent.com/OCA/pos/9.0/pos_barcode_tare/static/description/zoom_action_widget.png" />
<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_tare"><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_tare"><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>Give the possibility to the user to enter a Tare weight, when weighting
products in the Point of Sale.
This will compute automatically net weight and set it to the current
selected order</p>
<div class="figure">
<img alt="https://raw.githubusercontent.com/OCA/pos/12.0/pos_tare/static/description/pos_tare.png" src="https://raw.githubusercontent.com/OCA/pos/12.0/pos_tare/static/description/pos_tare.png" />
</div>
<p>The net weight is displayed in the order with the tare value below.</p>
<div class="figure">
<img alt="https://raw.githubusercontent.com/OCA/pos/12.0/pos_tare/static/description/order.png" src="https://raw.githubusercontent.com/OCA/pos/12.0/pos_tare/static/description/order.png" />
</div>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
@ -407,14 +395,16 @@ ul.auto-toc {
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<p>Install this add-on and configure the point of sale where you want to be able to use the tare barecode. The label printing is done using web print. To streamline the label printing it is advised to use the silent printing mode (firefox) or the kiosk printing (chrome).</p>
<p>The command line to start a chrome base browser in kiosk mode with silent printing looks like:</p>
<p><tt class="docutils literal"><span class="pre">chromium-browser</span> <span class="pre">--use-system-default-printer</span> <span class="pre">--kiosk</span> <span class="pre">--kiosk-printing</span> <span class="pre">http://localhost:8069/</span></tt></p>
<p>Install this add-on and configure your point of sale. To enable this addon, go to your point of sale configuration page. There, enable the electronic scale and barcode reader in the “IoT Box” section. In the same page, look up for the “Tare input method” field, and choose a tare method. There are three tare methods:
- “manual”, you’ll set the tare value when you weight the product;
- “barcode”, you’ll scan the tare value from a barcode;
- “both”, you can both of the above.</p>
<p>To generate a tare barcode you need to use the default barcode nomenclature. The default barcode pattern is <cite>0700000{NNDDD}</cite>. Using that pattern, the barcode for a tare of 0.1kg is <cite>0700000001006</cite>. The <cite>pos_tare_barcode_generator</cite> allows you to create tare labels right from the POS.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id2">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>Merge with Sylvain LE GAL tare module that allows user to set tare on the scale screen.</li>
<li>Print tare value on customer receipt.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
@ -422,7 +412,7 @@ ul.auto-toc {
<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_barcode_tare%0Aversion:%209.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<a class="reference external" href="https://github.com/OCA/pos/issues/new?body=module:%20pos_tare%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">
@ -430,14 +420,15 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>GRAP</li>
<li>Le Nid</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li>Sylvain LE GAL (<a class="reference external" href="https://www.twitter.com/legalsylvain">https://www.twitter.com/legalsylvain</a>)</li>
<li>Le Nid</li>
<li>Sylvain LE GAL</li>
<li>CoopITEasy</li>
</ul>
</div>
@ -450,7 +441,7 @@ mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external" href="https://github.com/fkawala"><img alt="fkawala" src="https://github.com/fkawala.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/pos/tree/9.0/pos_barcode_tare">OCA/pos</a> project on GitHub.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/pos/tree/12.0/pos_tare">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>

BIN
pos_tare/static/description/order.png

After

Width: 434  |  Height: 258  |  Size: 23 KiB

BIN
pos_tare/static/description/pos_tare.png

Before

Width: 871  |  Height: 388  |  Size: 36 KiB

After

Width: 501  |  Height: 480  |  Size: 30 KiB

BIN
pos_tare/static/description/pos_ticket.png

After

Width: 500  |  Height: 395  |  Size: 24 KiB

86
pos_tare/static/src/css/pos_tare.css

@ -1,31 +1,71 @@
.pos .pos-tare-container .left-block{
/***************************************************************
Overloading existing PoS objects
****************************************************************/
/*Change the design of the numpad in the product-screen*/
.pos .product-screen .actionpad .button.pay {
height: 216px;
}
.pos .product-screen .numpad button.input-button.numpad-backspace {
width: 216px;
}
/*Fix the receipt in the non proxy mode as the order lines can now
content multiple lines*/
.pos .pos-right-align{
vertical-align: top;
}
/***************************************************************
New style for new scale objects
****************************************************************/
.pos .scale-screen .weight-label {
font-size: 25px;
margin: 16px;
text-align: center;
display: inline-block;
width:45%;
margin:0;
padding:0;
text-align:left;
font-size: 16px;
color: #555;
font-weight: bold;
width: 40%;
margin-bottom: 0px;
}
.pos .pos-tare-container .right-block, .pos .pos-tare-container .right-block-readonly{
.pos .scale-screen .weight-value {
font-size: 25px;
display: inline-block;
width:45%;
text-align:right;
height: 40px;
font-size: 20px;
font-family: Lato;
-moz-box-sizing: border-box;
outline: none;
border: none;
padding: 6px 8px;
color: #484848;
border-radius: 3px;
margin-top: 5px;
margin: 16px;
margin-top: 0px;
padding: 16px;
width: 40%;
font-family: Inconsolata;
font-weight: bold;
text-shadow: 0px 2px 0px rgb(210,210,210);
margin-bottom: 0px;
}
.pos .pos-tare-container .right-block{
.pos .scale-screen .weight-value-fixed {
border-radius: 3px;
box-shadow: 0px 2px 0px rgb(225,225,225) inset;
text-align: right;
background: white;
box-shadow: 0px 2px rgba(143, 143, 143, 0.3) inset;
}
.pos .scale-screen .weight-value-alterable {
text-align: left;
padding-bottom: 0px;
padding-left: 0px;
}
.pos .scale-screen .input-weight {
border: 1px solid #cecbcb;
border-radius: 20px;
padding: 15px 20px;
width: 50%;
font-family: Inconsolata;
font-weight: bold;
font-size: 25px;
}
.pos .scale-screen .input-weight:focus {
outline: none;
box-shadow: 0px 0px 0px 3px #6EC89B;
}

116
pos_tare/static/src/js/models.js

@ -0,0 +1,116 @@
odoo.define('pos_tare.models', function (require) {
"use strict";
var core = require('web.core');
var models = require('point_of_sale.models');
var pos_tare_tools = require('pos_tare.tools');
var _t = core._t;
var _super_ = models.Orderline.prototype;
var OrderLineWithTare = models.Orderline.extend({
// /////////////////////////////
// Overload Section
// /////////////////////////////
initialize: function (session, attributes) {
this.tare = 0;
return _super_.initialize.call(this, session, attributes);
},
init_from_JSON: function (json) {
_super_.init_from_JSON.call(this, json);
this.tare = json.tare ||0;
},
clone: function () {
var orderline = _super_.clone.call(this);
orderline.tare = this.tare;
return orderline;
},
export_as_JSON: function () {
var json = _super_.export_as_JSON.call(this);
json.tare = this.get_tare();
return json;
},
export_for_printing: function () {
var result = _super_.export_for_printing.call(this);
result.tare_quantity = this.get_tare();
result.gross_quantity = this.get_gross_weight();
return result;
},
// /////////////////////////////
// Custom Section
// /////////////////////////////
set_tare: function (quantity, update_net_weight) {
this.order.assert_editable();
// Prevent to apply multiple times a tare to the same product.
if (this.get_tare() > 0) {
// This is valid because the tare is stored using product UOM.
this.set_quantity(this.get_quantity() + this.get_tare());
this.reset_tare();
}
// We convert the tare that is always measured in the same UoM into
// the unit of measure for this order line.
var tare_uom = this.pos.config.iface_tare_uom_id[0];
var tare_unit = this.pos.units_by_id[tare_uom];
var tare = parseFloat(quantity) || 0;
var line_unit = this.get_unit();
var tare_in_product_uom = pos_tare_tools.convert_mass(
tare, tare_unit, line_unit);
var tare_in_product_uom_string = pos_tare_tools.format_tare(
this.pos, tare_in_product_uom, line_unit);
if (update_net_weight) {
var net_quantity = this.get_quantity() - tare_in_product_uom;
// Update the quantity with the new weight net of tare quantity.
this.set_quantity(net_quantity);
}
// Update tare value.
this.tare = tare_in_product_uom;
this.trigger('change', this);
},
reset_tare: function () {
this.tare = 0;
},
get_tare: function () {
return this.tare;
},
get_gross_weight: function () {
return this.get_tare() + this.get_quantity();
},
get_tare_str_with_unit: function () {
var unit = this.get_unit();
var tare_str = pos_tare_tools.format_tare(
this.pos,
this.tare,
this.get_unit()
);
return tare_str + ' ' + unit.name;
},
get_gross_weight_str_with_unit: function () {
var unit = this.get_unit();
var gross_weight_str = pos_tare_tools.format_tare(
this.pos,
this.get_gross_weight(),
this.get_unit()
);
return gross_weight_str + ' ' + unit.name;
},
});
models.Orderline = OrderLineWithTare;
});

140
pos_tare/static/src/js/pos_tare.js

@ -1,140 +0,0 @@
/*
Copyright (C) 2015-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).
*/
"use strict";
openerp.pos_tare = function(instance){
var module = instance.point_of_sale;
var _t = instance.web._t;
/*************************************************************************
Extend : Widget 'PosWidget'
*/
module.PosWidget = module.PosWidget.extend({
build_widgets: function(){
this._super();
// Add a new screen 'TareScreenWidget'
this.tare_screen = new module.TareScreenWidget(this,{});
this.tare_screen.appendTo(this.$('.screens'));
this.screen_selector.add_screen('tare', this.tare_screen);
},
});
/*************************************************************************
Extend : Widget 'ScaleScreenWidget'
*/
module.ScaleScreenWidget = module.ScaleScreenWidget.extend({
next_screen: 'tare',
// Overwrite 'show' function to display TareScreenWidget
show: function(){
this.pos_widget.screen_selector.set_current_screen(this.next_screen,{product: this.get_product()});
},
});
/*************************************************************************
Define : New Widget 'TareScreenWidget'
*/
module.TareScreenWidget = module.ScreenWidget.extend({
template:'TareScreenWidget',
next_screen: 'products',
previous_screen: 'products',
show_leftpane: false,
show: function(){
this._super();
this.renderElement();
var self = this;
// Initialize values
this.net_weight = 0;
this.current_product = this.get_product();
this.$('#product-name').html(this.get_product().display_name);
this.$('#unit-price').html(this.format_currency(this.get_product().price));
// Add a 'next' Button
this.add_action_button({
label: _t('Back'),
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
click: function(){
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
},
});
this.order_button = this.add_action_button({
label: _t('Order'),
icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
click: function() { self.order_product_click(); },
});
// Initialize Display
this.onChangeGrossWeightTareWeight();
this.$('#gross-weight').keyup(function(event){
self.onChangeGrossWeightTareWeight(event);
});
this.$('#tare-weight').keyup(function(event){
self.onChangeGrossWeightTareWeight(event);
});
// Focus on Gross Weight
this.$('#gross-weight').focus();
},
sanitize_value: function (input_name){
var res = this.$(input_name)[0].value.replace(',', '.').trim();
if (isNaN(res)){
this.$(input_name).css("background-color", "#F66");
}
else{
this.$(input_name).css("background-color", "#FFF");
}
return res;
},
onChangeGrossWeightTareWeight: function(event){
var gross_weight = this.sanitize_value('#gross-weight');
var tare_weight = this.sanitize_value('#tare-weight');
var ok = false;
if (!isNaN(gross_weight) && (gross_weight !== '') && (parseFloat(gross_weight) !== 0) && !isNaN(tare_weight)){
this.net_weight = gross_weight - tare_weight;
var price = this.get_product().price * this.net_weight;
this.current_net_weight_text = this.net_weight.toFixed(3);
this.current_total_price_text = this.format_currency(price);
ok = true;
}
else{
this.current_net_weight_text = '/';
this.current_total_price_text = '/';
}
this.$('#net-weight').html(this.current_net_weight_text);
this.$('#total-price').html(this.current_total_price_text);
this.order_button.set_disabled(!ok);
},
get_product: function(){
var ss = this.pos_widget.screen_selector;
if(ss){
return ss.get_current_screen_param('product');
}else{
return undefined;
}
},
order_product_click: function(){
this.pos.get('selectedOrder').addProduct(this.current_product,{ quantity:this.net_weight });
this.pos_widget.screen_selector.set_current_screen(this.next_screen);
},
});
};

201
pos_tare/static/src/js/screens.js

@ -0,0 +1,201 @@
odoo.define('pos_tare.screens', function (require) {
"use strict";
var core = require('web.core');
var screens = require('point_of_sale.screens');
var utils = require('web.utils');
var _t = core._t;
var round_pr = utils.round_precision;
var leq_zero_qty = (ol) => ol.get_quantity() <= 0;
// This configures read action for tare barcode. A tare barcode contains a
// fake product ID and the weight to be subtracted from the product in the
// latest order line.
screens.ScreenWidget.include({
barcode_tare_action: function (code) {
try {
var order = this.pos.get_order();
var selected_order_line = order.get_selected_orderline();
var tare_weight = code.value;
selected_order_line.set_tare(tare_weight, true);
} catch (error) {
var title = _t("We can not apply this tare barcode.");
var popup = {title: title, body: error.message};
this.gui.show_popup('error', popup);
}
},
// Setup the callback action for the "weight" barcodes.
show: function () {
this._super();
if (this.pos.config.iface_tare_method !== 'manual') {
this.pos.barcode_reader.set_action_callback(
'tare',
_.bind(this.barcode_tare_action, this));
}
},
});
screens.ScaleScreenWidget.include({
// /////////////////////////////
// Overload Section
// /////////////////////////////
show: function () {
this.tare = 0.0;
this.gross_weight = 0.0;
this._super();
var self = this;
this.$('#input_weight_tare').keyup(function (event) {
self.onchange_tare(event);
});
this.$('#input_gross_weight').keyup(function (event) {
self.onchange_gross_weight(event);
});
if (this.pos.config.iface_gross_weight_method === 'scale') {
this.$('#input_weight_tare').focus();
} else {
this.pos.proxy_queue.clear();
this.$('#input_gross_weight').focus();
}
},
// Overload set_weight function
// We assume that the argument is now the gross weight
// we compute the net weight, depending on the tare and the gross weight
// then we call super, with the net weight
set_weight: function (gross_weight) {
this.gross_weight = gross_weight;
var net_weight = gross_weight - (this.tare || 0);
this.$('#container_weight_gross').text(
this.get_product_gross_weight_string());
this._super(net_weight);
},
order_product: function () {
if (this.tare === undefined) {
this.gui.show_popup('error', {
'title': _t('Incorrect Tare Value'),
'body': _t('Please set a numeric value' +
' in the tare field, or let empty.'),
});
} else {
this._super();
if (this.tare > 0.0) {
var order = this.pos.get_order();
var orderline = order.get_last_orderline();
orderline.set_tare(this.tare, false);
}
}
},
// /////////////////////////////
// Custom Section
// /////////////////////////////
get_product_gross_weight_string: function () {
var product = this.get_product();
var defaultstr = (this.gross_weight || 0).toFixed(3) + ' Kg';
if (!product || !this.pos) {
return defaultstr;
}
var unit_id = product.uom_id;
if (!unit_id) {
return defaultstr;
}
var unit = this.pos.units_by_id[unit_id[0]];
var weight = round_pr(this.gross_weight || 0, unit.rounding);
var weightstr = weight.toFixed(
Math.ceil(Math.log(1.0/unit.rounding) / Math.log(10) ));
weightstr += ' ' + unit.name;
return weightstr;
},
onchange_tare: function () {
this.tare = this.check_sanitize_value('#input_weight_tare');
this.set_weight(this.gross_weight);
},
onchange_gross_weight: function () {
var gross_weight = this.check_sanitize_value('#input_gross_weight');
this.set_weight(gross_weight);
},
check_sanitize_value: function (input_name) {
var res = this.$(input_name)[0].value.replace(',', '.').trim();
if (isNaN(res)) {
this.$(input_name).css("background-color", "#F66");
return undefined;
}
this.$(input_name).css("background-color", "#FFF");
return parseFloat(res, 10);
},
});
screens.PaymentScreenWidget.include({
validate_order: function(options) {
var order = this.pos.get_order();
var orderlines = Array.from(order.get_orderlines());
if (orderlines.some(leq_zero_qty)) {
var _super_validate_order = this._super.bind(this);
var wrong_orderline = orderlines.find(leq_zero_qty);
var wrong_product = wrong_orderline.get_product().display_name;
this.gui.show_popup('confirm', {
title: _t('Quantity lower or equal to zero'),
body: _.str.sprintf(
_t("The quantity for \"%s\" is lower or equal to" +
" zero. Call for help unless you're perfectly" +
" sure you are doing right."), wrong_product),
confirm: function() {
_super_validate_order();
},
});
return;
}
return this._super(options);
},
});
screens.OrderWidget.include({
set_value: function (val) {
var order = this.pos.get_order();
if (order.get_selected_orderline()) {
var mode = this.numpad_state.get('mode');
if (mode === 'quantity') {
var orderline = order.get_selected_orderline();
var tare = orderline.get_tare();
orderline.reset_tare();
orderline.set_quantity(val);
if (tare > 0) {
orderline.set_tare(tare, true);
}
} else if (mode === 'discount') {
order.get_selected_orderline().set_discount(val);
} else if (mode === 'price') {
var selected_orderline = order.get_selected_orderline();
selected_orderline.price_manually_set = true;
selected_orderline.set_unit_price(val);
} else if (mode === 'tare') {
if (this.pos.config.iface_tare_method === 'barcode') {
this.gui.show_popup('error',
{'title': _t('Incorrect Tare Value'),
'body': _t('You can not set the tare.' +
' To be able to set the tare manually' +
' you have to change the tare input method' +
' in the POS configuration.')});
} else {
try {
order.get_selected_orderline().set_tare(val, true);
} catch (error) {
var title = _t("We can not apply this tare.");
var popup = {title: title, body: error.message};
this.gui.show_popup('error', popup);
}
}
}
}
},
});
});

62
pos_tare/static/src/js/tools.js

@ -0,0 +1,62 @@
odoo.define('pos_tare.tools', function (require) {
"use strict";
var core = require('web.core');
var utils = require('web.utils');
var field_utils = require('web.field_utils');
var _t = core._t;
var round_pr = utils.round_precision;
var round_di = utils.round_decimals;
// Convert mass using the reference UOM as pivot unit.
var convert_mass = function (mass, from_unit, to_unit) {
// There is no conversion from one category to another.
if (from_unit.category_id[0] !== to_unit.category_id[0]) {
throw new Error(_.str.sprintf(
_t("We can not cast a weight in %s into %s."),
from_unit.name, to_unit.name));
}
// No need to convert as weights are measured in same unit.
if (from_unit.id === to_unit.id) {
return mass;
}
// Converts "from_unit" to reference unit of measure.
var result = mass;
if (from_unit.uom_type === "bigger") {
result /= from_unit.factor;
} else {
result *= from_unit.factor_inv;
}
// Converts reference unit of measure to "to_unit".
if (to_unit.uom_type === "bigger") {
result *= to_unit.factor;
} else {
result /= to_unit.factor_inv;
}
if (to_unit.rounding) {
// Return the rounded result if needed.
return round_pr(result || 0, to_unit.rounding);
}
return result || 0;
};
// Format the tare value.
var format_tare = function (pos, qty, unit) {
if (unit.rounding) {
var q = round_pr(qty, unit.rounding);
var decimals = pos.dp['Product Unit of Measure'];
return field_utils.format.float(
round_di(q, decimals),
{type: 'float', digits: [69, decimals]});
}
return qty.toFixed(0);
};
return {
convert_mass: convert_mass,
format_tare: format_tare,
};
});

132
pos_tare/static/src/xml/pos_tare.xml

@ -1,45 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2015-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).
-->
<templates id="template" xml:space="preserve">
<t t-name="TareScreenWidget">
<div class="scale-screen screen">
<div class="screen-content">
<div class="top-content">
<h1 id="product-name" />
<t t-extend="ScaleScreenWidget">
<t t-jquery=".js-weight" t-operation="before">
<t t-if="widget.pos.config.iface_tare_method != 'barcode'">
<div class="weight-label">
Gross Weight
</div>
<div class="centered-content pos-tare-container">
<div>
<span class="left-block">Unit Price</span>
<span id="unit-price" class="right-block-readonly"/>
</div>
<div>
<span class="left-block">Gross Weight</span>
<input id="gross-weight" class="right-block" pattern="[0-9]+([\.][0-9]+)?"/>
</div>
<div>
<span class="left-block">Tare</span>
<input id="tare-weight" class="right-block" pattern="[0-9]+([\.][0-9]+)?"/>
</div>
<br />
<br />
<div>
<span class="left-block">Net Weight</span>
<span id="net-weight" class="right-block-readonly"/>
</div>
<br />
<div>
<span class="left-block">Total Price</span>
<span id="total-price" class="right-block-readonly"/>
<t t-if="widget.pos.config.iface_gross_weight_method == 'scale'">
<div class="weight-value weight-value-fixed" id="container_weight_gross"/>
</t>
<t t-else="1">
<div class="weight-value weight-value-alterable">
<input type="text" class="input-weight" id="input_gross_weight" placeholder="0.000"/>
<t t-esc="widget.pos.config.iface_tare_uom_id[1]"/>
</div>
</t>
<div class="weight-label">
Tare
</div>
<div class="weight-value weight-value-alterable">
<input type="text" class="input-weight" id="input_weight_tare" placeholder="0.000"/>
<t t-esc="widget.pos.config.iface_tare_uom_id[1]"/>
</div>
</t>
</t>
</t>
<t t-extend="Orderline">
<t t-jquery=".info-list:last-child" t-operation="append">
<t t-if="line.get_tare() !== 0">
<li class="info">
<i>
(Gross Weight:
<em><t t-esc="line.get_gross_weight_str_with_unit()"/></em>
- Tare:
<em><t t-esc="line.get_tare_str_with_unit()"/></em>)
</i>
</li>
</t>
</t>
</t>
<t t-name="PosTicket" t-extend="PosTicket">
<t t-jquery="tr[t-foreach='orderlines'] > td:nth-child(1)" t-operation="append">
<t t-if="orderline.get_tare() > 0">
<div class="pos-disc-font">
Gross Weight :
<t t-esc="orderline.get_gross_weight_str_with_unit()"/><br/>
Tare :
<t t-esc="orderline.get_tare_str_with_unit()"/><br/>
</div>
</div>
</div>
</t>
</t>
</t>
<t t-name="XmlReceipt" t-extend="XmlReceipt">
<t t-jquery="t[t-if='!simple']" t-operation="append">
<t t-if="line.tare_quantity !== 0">
<line indent='1'>
<left>
(
<value t-att-value-decimals='pos.dp["Product Unit of Measure"]' value-autoint='on'>
<t t-esc="line.gross_quantity" />
</value> -
<value t-att-value-decimals='pos.dp["Product Unit of Measure"]' value-autoint='on'>
<t t-esc="line.tare_quantity"/>
</value>
)
</left>
</line>
</t>
</t>
</t>
<t t-name="NumpadWidget">
<div class="numpad">
<button class="input-button number-char">1</button>
<button class="input-button number-char">2</button>
<button class="input-button number-char">3</button>
<button class="mode-button" data-mode='quantity'>Qty</button>
<br />
<button class="input-button number-char">4</button>
<button class="input-button number-char">5</button>
<button class="input-button number-char">6</button>
<button class="mode-button" data-mode='tare'>Tare</button>
<br />
<button class="input-button number-char">7</button>
<button class="input-button number-char">8</button>
<button class="input-button number-char">9</button>
<button class="mode-button" data-mode='discount'>Disc</button>
<br />
<button class="input-button numpad-minus" >+/-</button>
<button class="input-button number-char">0</button>
<button class="input-button number-char">.</button>
<button class="mode-button" data-mode='price'>Price</button>
<button class="input-button numpad-backspace">
<img style="pointer-events: none;" src="/point_of_sale/static/src/img/backspace.png" width="24" height="21" alt="Backspace"/>
</button>
</div>
</t>
</templates>

27
pos_tare/views/templates.xml

@ -1,23 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2015-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).
-->
<openerp><data>
<!-- JS Files -->
<template id="assets_backend" name="point_of_sale assets2" inherit_id="web.assets_backend">
<odoo>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/pos_tare/static/src/js/pos_tare.js"></script>
<link rel="stylesheet" href="/pos_tare/static/src/css/pos_tare.css"/>
<script type="text/javascript" src="/pos_tare/static/src/js/tools.js"></script>
<script type="text/javascript" src="/pos_tare/static/src/js/models.js"></script>
<script type="text/javascript" src="/pos_tare/static/src/js/screens.js"></script>
</xpath>
</template>
<!-- CSS Files -->
<template id="index" name="pos_tare index" inherit_id="point_of_sale.index">
<xpath expr="//link[@id='pos-stylesheet']" position="after">
<link rel="stylesheet" href="/pos_tare/static/src/css/pos_tare.css" id="pos_tare-stylesheet"/>
</xpath>
</template>
</data></openerp>
</odoo>

54
pos_tare/views/view_pos_config.xml

@ -0,0 +1,54 @@
<?xml version="1.0"?>
<odoo>
<record model="ir.ui.view" id="view_pos_config_form">
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form" />
<field name="priority" eval="30"/>
<field name="arch" type="xml">
<xpath expr="//h2[@name='order']/following-sibling::*[position()=1]" position="inside">
<div class="col-xs-12 col-lg-6 o_setting_box" id="iface_tare_method">
<div class="o_setting_right_pane">
<label for="iface_tare_method" string="Tare Input Method"/>
<div class="text-muted">
Configure how to tare products to weight with a scale.
</div>
<div class="content-group">
<div class="mt16">
<field name="iface_tare_method" class="o_light_label"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-lg-6 o_setting_box" id="iface_tare_method">
<div class="o_setting_right_pane">
<label for="iface_tare_uom_id" string="Tare UoM"/>
<div class="text-muted">
Unit of Measure of the tares.
</div>
<div class="content-group">
<div class="mt16">
<field name="iface_tare_uom_id" class="o_light_label"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-lg-6 o_setting_box" id="iface_gross_weight_method">
<div class="o_setting_right_pane">
<label for="iface_gross_weight_method" string="Gross Weight Input Method"/>
<div class="text-muted">
Configure how to input Gross Weight.
</div>
<div class="content-group">
<div class="mt16">
<field name="iface_gross_weight_method" class="o_light_label"/>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

16
pos_tare/views/view_pos_order.xml

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<odoo>
<record model="ir.ui.view" id="view_pos_order_form">
<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="qty" position="after">
<field name="tare"/>
</field>
</field>
</record>
</odoo>
Loading…
Cancel
Save