Browse Source

Merge PR #491 into 12.0

Signed-off-by legalsylvain
pull/517/head
OCA-git-bot 4 years ago
parent
commit
ba4ebdfdbe
  1. 109
      pos_tare/README.rst
  2. 1
      pos_tare/__init__.py
  3. 25
      pos_tare/__manifest__.py
  4. 12
      pos_tare/data/barcode_rule.xml
  5. 3
      pos_tare/models/__init__.py
  6. 9
      pos_tare/models/barcode_rule.py
  7. 41
      pos_tare/models/pos_config.py
  8. 15
      pos_tare/models/pos_order_line.py
  9. 3
      pos_tare/readme/CONTRIBUTORS.rst
  10. 10
      pos_tare/readme/DESCRIPTION.rst
  11. 6
      pos_tare/readme/USAGE.rst
  12. BIN
      pos_tare/static/description/icon.png
  13. 450
      pos_tare/static/description/index.html
  14. BIN
      pos_tare/static/description/order.png
  15. BIN
      pos_tare/static/description/pos_tare.png
  16. BIN
      pos_tare/static/description/pos_ticket.png
  17. 71
      pos_tare/static/src/css/pos_tare.css
  18. 116
      pos_tare/static/src/js/models.js
  19. 201
      pos_tare/static/src/js/screens.js
  20. 62
      pos_tare/static/src/js/tools.js
  21. 107
      pos_tare/static/src/xml/pos_tare.xml
  22. 10
      pos_tare/views/templates.xml
  23. 54
      pos_tare/views/view_pos_config.xml
  24. 16
      pos_tare/views/view_pos_order.xml

109
pos_tare/README.rst

@ -0,0 +1,109 @@
====================
Point Of Sale - Tare
====================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! 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_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/OCA/pos/12.0/pos_tare/static/description/pos_tare.png
The net weight is displayed in the order with the tare value below.
.. 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
======================
- Print tare value on customer receipt.
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_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.
Credits
=======
Authors
~~~~~~~
* GRAP
* Le Nid
Contributors
~~~~~~~~~~~~
- 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>`__:
|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. To learn how please visit https://odoo-community.org/page/Contribute.

1
pos_tare/__init__.py

@ -0,0 +1 @@
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,
}

12
pos_tare/data/barcode_rule.xml

@ -0,0 +1,12 @@
<?xml version="1.0"?>
<odoo noupdate="1">
<record id="barcode_rule_tare" model="barcode.rule">
<field name="name">Tare</field>
<field name="barcode_nomenclature_id" ref="barcodes.default_barcode_nomenclature"/>
<field name="sequence">81</field>
<field name="type">tare</field>
<field name="encoding">ean13</field>
<field name="pattern">0700000{NNDDD}</field>
</record>
</odoo>

3
pos_tare/models/__init__.py

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

9
pos_tare/models/barcode_rule.py

@ -0,0 +1,9 @@
from odoo import models, fields
class BarcodeRule(models.Model):
_inherit = 'barcode.rule'
type = fields.Selection(selection_add=[
('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")
)

3
pos_tare/readme/CONTRIBUTORS.rst

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

10
pos_tare/readme/DESCRIPTION.rst

@ -0,0 +1,10 @@
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 net weight is displayed in the order with the tare value below.
.. figure:: ../static/description/order.png

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

After

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

450
pos_tare/static/description/index.html

@ -0,0 +1,450 @@
<?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>Point Of Sale - Tare</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="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/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">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id2">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<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>Print tare value on customer receipt.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id3">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_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">
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
<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>CoopITEasy</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">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>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/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>
</div>
</body>
</html>

BIN
pos_tare/static/description/order.png

After

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

BIN
pos_tare/static/description/pos_tare.png

After

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

BIN
pos_tare/static/description/pos_ticket.png

After

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

71
pos_tare/static/src/css/pos_tare.css

@ -0,0 +1,71 @@
/***************************************************************
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: 40%;
margin-bottom: 0px;
}
.pos .scale-screen .weight-value {
font-size: 25px;
display: inline-block;
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 .scale-screen .weight-value-fixed {
border-radius: 3px;
box-shadow: 0px 2px 0px rgb(225,225,225) inset;
text-align: right;
background: white;
}
.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;
});

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

107
pos_tare/static/src/xml/pos_tare.xml

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<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>
<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>
</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>

10
pos_tare/views/templates.xml

@ -0,0 +1,10 @@
<odoo>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<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>
</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