Rémy Taymans
4 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 3440 additions and 0 deletions
-
81pos_container/README.rst
-
2pos_container/__init__.py
-
36pos_container/__manifest__.py
-
20pos_container/data/product.xml
-
19pos_container/demo/demo.xml
-
384pos_container/i18n/fr.po
-
377pos_container/i18n/pos_container.pot
-
3pos_container/models/__init__.py
-
13pos_container/models/barcode.py
-
45pos_container/models/container.py
-
15pos_container/models/pos_order_line.py
-
2pos_container/readme/CONTRIBUTORS.rst
-
5pos_container/readme/DESCRIPTION.rst
-
2pos_container/readme/USAGE.rst
-
3pos_container/security/ir.model.access.csv
-
431pos_container/static/description/index.html
-
191pos_container/static/src/css/container.css
-
431pos_container/static/src/js/container.js
-
619pos_container/static/src/js/models_and_db.js
-
244pos_container/static/src/js/tests.js
-
442pos_container/static/src/xml/pos.xml
-
17pos_container/templates/templates.xml
-
1pos_container/tests/__init__.py
-
14pos_container/tests/test_pos_container.py
-
43pos_container/views/container.xml
@ -0,0 +1,81 @@ |
|||
============= |
|||
POS Container |
|||
============= |
|||
|
|||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
!! 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/github-OCA%2Fpos-lightgray.png?logo=github |
|||
:target: https://github.com/OCA/pos/tree/12.0/pos_container |
|||
:alt: OCA/pos |
|||
.. |badge3| 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_container |
|||
:alt: Translate me on Weblate |
|||
.. |badge4| 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| |
|||
|
|||
This module allows to handle use of reusable containers in POS, |
|||
this is useful to handle selling product in bulk without having to calculate |
|||
the tare of the container. |
|||
|
|||
Each container is identified by a barcode, the weight is stored in Odoo. |
|||
|
|||
**Table of contents** |
|||
|
|||
.. contents:: |
|||
:local: |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
You have to create a Barcode Nomenclature to handle containers before using the |
|||
module. |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/pos/issues>`_. |
|||
In case of trouble, please check there if your issue has already been reported. |
|||
If you spotted it first, help us smashing it by providing a detailed and welcomed |
|||
`feedback <https://github.com/OCA/pos/issues/new?body=module:%20pos_container%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 |
|||
~~~~~~~ |
|||
|
|||
* Coop IT Easy SCRLfs |
|||
|
|||
Contributors |
|||
~~~~~~~~~~~~ |
|||
|
|||
* Pierrick Brun <pierrick.brun@akretion.com> |
|||
* Robin Keunen <robin.keunen@coopiteasy.be> |
|||
|
|||
Maintainers |
|||
~~~~~~~~~~~ |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
This module is part of the `OCA/pos <https://github.com/OCA/pos/tree/12.0/pos_container>`_ project on GitHub. |
|||
|
|||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
@ -0,0 +1,2 @@ |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
from . import models |
@ -0,0 +1,36 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Robin Keunen <robin@coopiteasy.be> |
|||
# Pierrick Brun <pierrick.brun@akretion.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).{ |
|||
{ |
|||
'name': "POS Container", |
|||
'version': '12.0.1.0.0', |
|||
|
|||
'summary': """ |
|||
Allows managing pre-weighted containers for bulk shop""", |
|||
|
|||
"author": "Coop IT Easy SCRLfs, " |
|||
"Odoo Community Association (OCA)", |
|||
'license': "AGPL-3", |
|||
'website': "https://github.com/OCA/pos/", |
|||
|
|||
'category': 'Point of Sale', |
|||
|
|||
'depends': ['point_of_sale'], |
|||
|
|||
'data': [ |
|||
'data/product.xml', |
|||
'views/container.xml', |
|||
'templates/templates.xml', |
|||
'security/ir.model.access.csv', |
|||
], |
|||
'demo': [ |
|||
'demo/demo.xml', |
|||
], |
|||
|
|||
'qweb': [ |
|||
'static/src/xml/pos.xml', |
|||
], |
|||
|
|||
'installable': True, |
|||
} |
@ -0,0 +1,20 @@ |
|||
<odoo> |
|||
<record id="temporary_container_product" model="product.product"> |
|||
<field name="name">Container without product</field> |
|||
<!-- The barcode is used to find it from the POS --> |
|||
<field name="barcode">CONTAINER</field> |
|||
<field name="uom_id" ref="uom.product_uom_kgm"/> |
|||
<field name="uom_po_id" ref="uom.product_uom_kgm"/> |
|||
<field name="purchase_ok" eval="False"/> |
|||
<field name="description"> |
|||
This product is used to describe POS order lines having a container but no product yet |
|||
</field> |
|||
<field name="list_price">0</field> |
|||
<field name="available_in_pos" eval="True"/> |
|||
<field name="to_weight" eval="True"/> |
|||
<field name="active" eval="False"/> |
|||
</record> |
|||
<record id="temporary_container_product_product_template" model="product.template"> |
|||
<field name="active" eval="False"/> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,19 @@ |
|||
<odoo> |
|||
<record id="container_1" model="pos.container"> |
|||
<field name="name">Container 1</field> |
|||
<field name="barcode">0498765456789</field> |
|||
<field name="weight">0.123</field> |
|||
</record> |
|||
|
|||
<record id="container_2" model="pos.container"> |
|||
<field name="name">Container 2</field> |
|||
<field name="barcode">0490987654356</field> |
|||
<field name="weight">0.234</field> |
|||
</record> |
|||
|
|||
<record id="container_3" model="pos.container"> |
|||
<field name="name">Container 3</field> |
|||
<field name="barcode">0490987654398</field> |
|||
<field name="weight">0.567</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,384 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_container |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 12.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2019-11-19 09:34+0000\n" |
|||
"PO-Revision-Date: 2019-11-19 10:42+0100\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"Plural-Forms: \n" |
|||
"Language: fr\n" |
|||
"X-Generator: Poedit 2.2.4\n" |
|||
|
|||
#. module: pos_container |
|||
#: model:product.product,description:pos_container.temporary_container_product |
|||
#: model:product.template,description:pos_container.temporary_container_product_product_template |
|||
msgid "" |
|||
"\n" |
|||
" This product is used to describe POS order lines having a container but no product yet\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:161 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:202 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:252 |
|||
#, python-format |
|||
msgid "% discount" |
|||
msgstr "% de réduction" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:127 |
|||
#, python-format |
|||
msgid "Add a container" |
|||
msgstr "Ajouter un contenant" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Alias" |
|||
msgstr "Alias" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:184 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:318 |
|||
#, python-format |
|||
msgid "Automatic Weighing" |
|||
msgstr "Pesée automatique" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:124 |
|||
#, python-format |
|||
msgid "Back" |
|||
msgstr "Retour" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:48 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__barcode |
|||
#, python-format |
|||
msgid "Barcode" |
|||
msgstr "Code Barre" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_barcode_rule |
|||
msgid "Barcode Rule" |
|||
msgstr "Règle de code barre" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:22 |
|||
#, python-format |
|||
msgid "Cancel" |
|||
msgstr "Annuler" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Cashier" |
|||
msgstr "Caissier" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Client" |
|||
msgstr "Client" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:374 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:135 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__container_id |
|||
#, python-format |
|||
msgid "Container" |
|||
msgstr "Contenants" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__container_weight |
|||
msgid "Container Weight" |
|||
msgstr "Poids du contenant" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:80 |
|||
#, python-format |
|||
msgid "Container deletion" |
|||
msgstr "Suppression de contenant" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_pos_container |
|||
msgid "Container for bulk items" |
|||
msgstr "Contenant pour produits en vrac" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:134 |
|||
#, python-format |
|||
msgid "Container name:" |
|||
msgstr "Nom du contenant:" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Container unit" |
|||
msgstr "Unité de contenant" |
|||
|
|||
#. module: pos_container |
|||
#: model:product.product,name:pos_container.temporary_container_product |
|||
#: model:product.template,name:pos_container.temporary_container_product_product_template |
|||
msgid "Container without product" |
|||
msgstr "Contenant sans produit" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.actions.act_window,name:pos_container.pos_container_action_window |
|||
#: model:ir.ui.menu,name:pos_container.pos_container_menu |
|||
msgid "Containers" |
|||
msgstr "Contenants" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__create_uid |
|||
msgid "Created by" |
|||
msgstr "Créé par" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__create_date |
|||
msgid "Created on" |
|||
msgstr "Créé le" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:30 |
|||
#, python-format |
|||
msgid "Delete container" |
|||
msgstr "Supprimer le contenant" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:295 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:335 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:383 |
|||
#, python-format |
|||
msgid "Discount:" |
|||
msgstr "Réduction:" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Discounted Product" |
|||
msgstr "Article en promotion" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__display_name |
|||
msgid "Display Name" |
|||
msgstr "Nom affiché" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:81 |
|||
#, python-format |
|||
msgid "Do you want to delete this container ?\n" |
|||
msgstr "Voulez-vous supprimer ce contenant ?\n" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:144 |
|||
#, python-format |
|||
msgid "Error: Could not Save Changes" |
|||
msgstr "Erreur: Impossible de sauvegarder les changements" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:84 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:101 |
|||
#, python-format |
|||
msgid "Gross :" |
|||
msgstr "Poids Brut :" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__id |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container____last_update |
|||
msgid "Last Modified on" |
|||
msgstr "Dernière modification le" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "Dernière mise à jour par" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__write_date |
|||
msgid "Last Updated on" |
|||
msgstr "Dernière mise à jour le" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Location" |
|||
msgstr "Lieu" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Lot" |
|||
msgstr "Lot" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:234 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:366 |
|||
#, python-format |
|||
msgid "Manual Input" |
|||
msgstr "Saisie manuelle" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:47 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__name |
|||
#, python-format |
|||
msgid "Name" |
|||
msgstr "Nom" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__owner_id |
|||
msgid "Owner" |
|||
msgstr "Propriétaire" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Package" |
|||
msgstr "Colis" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_pos_order_line |
|||
msgid "Point of Sale Order Lines" |
|||
msgstr "Lignes de Commande en Point de Vente" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Priced Product" |
|||
msgstr "Article à prix fixe" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:137 |
|||
#, python-format |
|||
msgid "Save" |
|||
msgstr "Sauvegarder" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:26 |
|||
#, python-format |
|||
msgid "Search Containers" |
|||
msgstr "Recherche contenant" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:34 |
|||
#, python-format |
|||
msgid "Select a container" |
|||
msgstr "Sélection contenant" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:12 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__tare |
|||
#, python-format |
|||
msgid "Tare" |
|||
msgstr "Tare" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:215 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:266 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:356 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:404 |
|||
#, python-format |
|||
msgid "Tare :" |
|||
msgstr "PT :" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_barcode_rule__type |
|||
msgid "Type" |
|||
msgstr "Type" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Unit Product" |
|||
msgstr "Unité de produit" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:49 |
|||
#, python-format |
|||
msgid "Weight" |
|||
msgstr "Poids" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__weight |
|||
msgid "Weight (g)" |
|||
msgstr "Poids (kg)" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Weighted Product" |
|||
msgstr "Article pesé" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:160 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:201 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:251 |
|||
#, python-format |
|||
msgid "With a" |
|||
msgstr "Avec un" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:138 |
|||
#, python-format |
|||
msgid "Your Internet connection is probably down." |
|||
msgstr "Votre connexion internet est probablement coupée." |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:85 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:102 |
|||
#: model:product.product,uom_name:pos_container.temporary_container_product |
|||
#: model:product.product,weight_uom_name:pos_container.temporary_container_product |
|||
#: model:product.template,uom_name:pos_container.temporary_container_product_product_template |
|||
#: model:product.template,weight_uom_name:pos_container.temporary_container_product_product_template |
|||
#, python-format |
|||
msgid "kg" |
|||
msgstr "kg" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:90 |
|||
#, python-format |
|||
msgid "" |
|||
"kg \n" |
|||
" -" |
|||
msgstr "" |
|||
"kg\n" |
|||
" -" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:107 |
|||
#, python-format |
|||
msgid "" |
|||
"kg \n" |
|||
" - Manual tare" |
|||
msgstr "" |
|||
"kg \n" |
|||
" - Tare manuelle" |
@ -0,0 +1,377 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_container |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 12.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2019-11-19 09:33+0000\n" |
|||
"PO-Revision-Date: 2019-11-19 09:33+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_container |
|||
#: model:product.product,description:pos_container.temporary_container_product |
|||
#: model:product.template,description:pos_container.temporary_container_product_product_template |
|||
msgid "\n" |
|||
" This product is used to describe POS order lines having a container but no product yet\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:161 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:202 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:252 |
|||
#, python-format |
|||
msgid "% discount" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:127 |
|||
#, python-format |
|||
msgid "Add a container" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Alias" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:184 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:318 |
|||
#, python-format |
|||
msgid "Automatic Weighing" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:124 |
|||
#, python-format |
|||
msgid "Back" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:48 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__barcode |
|||
#, python-format |
|||
msgid "Barcode" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_barcode_rule |
|||
msgid "Barcode Rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:22 |
|||
#, python-format |
|||
msgid "Cancel" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Cashier" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Client" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:374 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:135 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__container_id |
|||
#, python-format |
|||
msgid "Container" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__container_weight |
|||
msgid "Container Weight" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:80 |
|||
#, python-format |
|||
msgid "Container deletion" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_pos_container |
|||
msgid "Container for bulk items" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:134 |
|||
#, python-format |
|||
msgid "Container name:" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Container unit" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:product.product,name:pos_container.temporary_container_product |
|||
#: model:product.template,name:pos_container.temporary_container_product_product_template |
|||
msgid "Container without product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.actions.act_window,name:pos_container.pos_container_action_window |
|||
#: model:ir.ui.menu,name:pos_container.pos_container_menu |
|||
msgid "Containers" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__create_uid |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__create_date |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:30 |
|||
#, python-format |
|||
msgid "Delete container" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:295 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:335 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:383 |
|||
#, python-format |
|||
msgid "Discount:" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Discounted Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__display_name |
|||
msgid "Display Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:81 |
|||
#, python-format |
|||
msgid "Do you want to delete this container ?\n" |
|||
"" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:144 |
|||
#, python-format |
|||
msgid "Error: Could not Save Changes" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:84 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:101 |
|||
#, python-format |
|||
msgid "Gross :" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__id |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container____last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__write_date |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Location" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Lot" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:234 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:366 |
|||
#, python-format |
|||
msgid "Manual Input" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:47 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__name |
|||
#, python-format |
|||
msgid "Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__owner_id |
|||
msgid "Owner" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Package" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_pos_order_line |
|||
msgid "Point of Sale Order Lines" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Priced Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:137 |
|||
#, python-format |
|||
msgid "Save" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:26 |
|||
#, python-format |
|||
msgid "Search Containers" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:34 |
|||
#, python-format |
|||
msgid "Select a container" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:12 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__tare |
|||
#, python-format |
|||
msgid "Tare" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:215 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:266 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:356 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:404 |
|||
#, python-format |
|||
msgid "Tare :" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_barcode_rule__type |
|||
msgid "Type" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Unit Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:49 |
|||
#, python-format |
|||
msgid "Weight" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__weight |
|||
msgid "Weight (g)" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Weighted Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:160 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:201 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:251 |
|||
#, python-format |
|||
msgid "With a" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:138 |
|||
#, python-format |
|||
msgid "Your Internet connection is probably down." |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:85 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:102 |
|||
#: model:product.product,uom_name:pos_container.temporary_container_product |
|||
#: model:product.product,weight_uom_name:pos_container.temporary_container_product |
|||
#: model:product.template,uom_name:pos_container.temporary_container_product_product_template |
|||
#: model:product.template,weight_uom_name:pos_container.temporary_container_product_product_template |
|||
#, python-format |
|||
msgid "kg" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:90 |
|||
#, python-format |
|||
msgid "kg \n" |
|||
" -" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:107 |
|||
#, python-format |
|||
msgid "kg \n" |
|||
" - Manual tare" |
|||
msgstr "" |
|||
|
@ -0,0 +1,3 @@ |
|||
from . import container |
|||
from . import barcode |
|||
from . import pos_order_line |
@ -0,0 +1,13 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Pierrick Brun <pierrick.brun@akretion.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import models, fields |
|||
|
|||
|
|||
class BarcodeRule(models.Model): |
|||
_inherit = 'barcode.rule' |
|||
|
|||
type = fields.Selection( |
|||
selection_add=[('container', 'Container unit')], |
|||
) |
@ -0,0 +1,45 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Robin Keunen <robin@coopiteasy.be> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import models, fields, api |
|||
|
|||
|
|||
class Container(models.Model): |
|||
_name = 'pos.container' |
|||
_description = 'Container for bulk items' |
|||
|
|||
name = fields.Char( |
|||
string='Name', |
|||
) |
|||
barcode = fields.Char( |
|||
'Barcode', |
|||
size=13, |
|||
) |
|||
weight = fields.Float( |
|||
string='Weight (kg)', |
|||
) |
|||
owner_id = fields.Many2one( |
|||
comodel_name='res.partner', |
|||
inverse_name='container_ids', |
|||
string='Owner', |
|||
) |
|||
|
|||
_sql_constraints = [ |
|||
('barcode_uniq', |
|||
'unique(barcode)', |
|||
"A barcode can only be assigned to one container !"), |
|||
] |
|||
|
|||
@api.model |
|||
def create_from_ui(self, containers): |
|||
# retourne la liste des ids dans le même ordre que la liste fournie |
|||
container_ids = [] |
|||
for container in containers: |
|||
container_id = container.pop('id', False) |
|||
if container_id: # Modifying existing container |
|||
self.browse(container_id).write(container) |
|||
else: |
|||
container_id = self.create(container).id |
|||
container_ids.append(container_id) |
|||
return container_ids |
@ -0,0 +1,15 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# @author Pierrick Brun <pierrick.brun@akretion.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class PosOrderLine(models.Model): |
|||
_inherit = 'pos.order.line' |
|||
|
|||
tare = fields.Float('Tare') |
|||
container_weight = fields.Float('Container Weight') |
|||
container_id = fields.Many2one( |
|||
'pos.container', |
|||
'Container') |
@ -0,0 +1,2 @@ |
|||
* Pierrick Brun <pierrick.brun@akretion.com> |
|||
* Robin Keunen <robin.keunen@coopiteasy.be> |
@ -0,0 +1,5 @@ |
|||
This module allows to handle use of reusable containers in POS, |
|||
this is useful to handle selling product in bulk without having to calculate |
|||
the tare of the container. |
|||
|
|||
Each container is identified by a barcode, the weight is stored in Odoo. |
@ -0,0 +1,2 @@ |
|||
You have to create a Barcode Nomenclature to handle containers before using the |
|||
module. |
@ -0,0 +1,3 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
access_pos_container_pos_user,access_pos_container_pos_user,model_pos_container,point_of_sale.group_pos_user,1,1,1,1 |
|||
access_pos_container_pos_manager,access_pos_container_pos_manager,model_pos_container,point_of_sale.group_pos_manager,1,1,1,1 |
@ -0,0 +1,431 @@ |
|||
<?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.14: http://docutils.sourceforge.net/" /> |
|||
<title>POS Container</title> |
|||
<style type="text/css"> |
|||
|
|||
/* |
|||
:Author: David Goodger (goodger@python.org) |
|||
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $ |
|||
:Copyright: This stylesheet has been placed in the public domain. |
|||
|
|||
Default cascading style sheet for the HTML output of Docutils. |
|||
|
|||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to |
|||
customize this style sheet. |
|||
*/ |
|||
|
|||
/* used to remove borders from tables and images */ |
|||
.borderless, table.borderless td, table.borderless th { |
|||
border: 0 } |
|||
|
|||
table.borderless td, table.borderless th { |
|||
/* Override padding for "table.docutils td" with "! important". |
|||
The right padding separates the table cells. */ |
|||
padding: 0 0.5em 0 0 ! important } |
|||
|
|||
.first { |
|||
/* Override more specific margin styles with "! important". */ |
|||
margin-top: 0 ! important } |
|||
|
|||
.last, .with-subtitle { |
|||
margin-bottom: 0 ! important } |
|||
|
|||
.hidden { |
|||
display: none } |
|||
|
|||
.subscript { |
|||
vertical-align: sub; |
|||
font-size: smaller } |
|||
|
|||
.superscript { |
|||
vertical-align: super; |
|||
font-size: smaller } |
|||
|
|||
a.toc-backref { |
|||
text-decoration: none ; |
|||
color: black } |
|||
|
|||
blockquote.epigraph { |
|||
margin: 2em 5em ; } |
|||
|
|||
dl.docutils dd { |
|||
margin-bottom: 0.5em } |
|||
|
|||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { |
|||
overflow: hidden; |
|||
} |
|||
|
|||
/* Uncomment (and remove this text!) to get bold-faced definition list terms |
|||
dl.docutils dt { |
|||
font-weight: bold } |
|||
*/ |
|||
|
|||
div.abstract { |
|||
margin: 2em 5em } |
|||
|
|||
div.abstract p.topic-title { |
|||
font-weight: bold ; |
|||
text-align: center } |
|||
|
|||
div.admonition, div.attention, div.caution, div.danger, div.error, |
|||
div.hint, div.important, div.note, div.tip, div.warning { |
|||
margin: 2em ; |
|||
border: medium outset ; |
|||
padding: 1em } |
|||
|
|||
div.admonition p.admonition-title, div.hint p.admonition-title, |
|||
div.important p.admonition-title, div.note p.admonition-title, |
|||
div.tip p.admonition-title { |
|||
font-weight: bold ; |
|||
font-family: sans-serif } |
|||
|
|||
div.attention p.admonition-title, div.caution p.admonition-title, |
|||
div.danger p.admonition-title, div.error p.admonition-title, |
|||
div.warning p.admonition-title, .code .error { |
|||
color: red ; |
|||
font-weight: bold ; |
|||
font-family: sans-serif } |
|||
|
|||
/* Uncomment (and remove this text!) to get reduced vertical space in |
|||
compound paragraphs. |
|||
div.compound .compound-first, div.compound .compound-middle { |
|||
margin-bottom: 0.5em } |
|||
|
|||
div.compound .compound-last, div.compound .compound-middle { |
|||
margin-top: 0.5em } |
|||
*/ |
|||
|
|||
div.dedication { |
|||
margin: 2em 5em ; |
|||
text-align: center ; |
|||
font-style: italic } |
|||
|
|||
div.dedication p.topic-title { |
|||
font-weight: bold ; |
|||
font-style: normal } |
|||
|
|||
div.figure { |
|||
margin-left: 2em ; |
|||
margin-right: 2em } |
|||
|
|||
div.footer, div.header { |
|||
clear: both; |
|||
font-size: smaller } |
|||
|
|||
div.line-block { |
|||
display: block ; |
|||
margin-top: 1em ; |
|||
margin-bottom: 1em } |
|||
|
|||
div.line-block div.line-block { |
|||
margin-top: 0 ; |
|||
margin-bottom: 0 ; |
|||
margin-left: 1.5em } |
|||
|
|||
div.sidebar { |
|||
margin: 0 0 0.5em 1em ; |
|||
border: medium outset ; |
|||
padding: 1em ; |
|||
background-color: #ffffee ; |
|||
width: 40% ; |
|||
float: right ; |
|||
clear: right } |
|||
|
|||
div.sidebar p.rubric { |
|||
font-family: sans-serif ; |
|||
font-size: medium } |
|||
|
|||
div.system-messages { |
|||
margin: 5em } |
|||
|
|||
div.system-messages h1 { |
|||
color: red } |
|||
|
|||
div.system-message { |
|||
border: medium outset ; |
|||
padding: 1em } |
|||
|
|||
div.system-message p.system-message-title { |
|||
color: red ; |
|||
font-weight: bold } |
|||
|
|||
div.topic { |
|||
margin: 2em } |
|||
|
|||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, |
|||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { |
|||
margin-top: 0.4em } |
|||
|
|||
h1.title { |
|||
text-align: center } |
|||
|
|||
h2.subtitle { |
|||
text-align: center } |
|||
|
|||
hr.docutils { |
|||
width: 75% } |
|||
|
|||
img.align-left, .figure.align-left, object.align-left, table.align-left { |
|||
clear: left ; |
|||
float: left ; |
|||
margin-right: 1em } |
|||
|
|||
img.align-right, .figure.align-right, object.align-right, table.align-right { |
|||
clear: right ; |
|||
float: right ; |
|||
margin-left: 1em } |
|||
|
|||
img.align-center, .figure.align-center, object.align-center { |
|||
display: block; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
table.align-center { |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
.align-left { |
|||
text-align: left } |
|||
|
|||
.align-center { |
|||
clear: both ; |
|||
text-align: center } |
|||
|
|||
.align-right { |
|||
text-align: right } |
|||
|
|||
/* reset inner alignment in figures */ |
|||
div.align-right { |
|||
text-align: inherit } |
|||
|
|||
/* div.align-center * { */ |
|||
/* text-align: left } */ |
|||
|
|||
.align-top { |
|||
vertical-align: top } |
|||
|
|||
.align-middle { |
|||
vertical-align: middle } |
|||
|
|||
.align-bottom { |
|||
vertical-align: bottom } |
|||
|
|||
ol.simple, ul.simple { |
|||
margin-bottom: 1em } |
|||
|
|||
ol.arabic { |
|||
list-style: decimal } |
|||
|
|||
ol.loweralpha { |
|||
list-style: lower-alpha } |
|||
|
|||
ol.upperalpha { |
|||
list-style: upper-alpha } |
|||
|
|||
ol.lowerroman { |
|||
list-style: lower-roman } |
|||
|
|||
ol.upperroman { |
|||
list-style: upper-roman } |
|||
|
|||
p.attribution { |
|||
text-align: right ; |
|||
margin-left: 50% } |
|||
|
|||
p.caption { |
|||
font-style: italic } |
|||
|
|||
p.credits { |
|||
font-style: italic ; |
|||
font-size: smaller } |
|||
|
|||
p.label { |
|||
white-space: nowrap } |
|||
|
|||
p.rubric { |
|||
font-weight: bold ; |
|||
font-size: larger ; |
|||
color: maroon ; |
|||
text-align: center } |
|||
|
|||
p.sidebar-title { |
|||
font-family: sans-serif ; |
|||
font-weight: bold ; |
|||
font-size: larger } |
|||
|
|||
p.sidebar-subtitle { |
|||
font-family: sans-serif ; |
|||
font-weight: bold } |
|||
|
|||
p.topic-title { |
|||
font-weight: bold } |
|||
|
|||
pre.address { |
|||
margin-bottom: 0 ; |
|||
margin-top: 0 ; |
|||
font: inherit } |
|||
|
|||
pre.literal-block, pre.doctest-block, pre.math, pre.code { |
|||
margin-left: 2em ; |
|||
margin-right: 2em } |
|||
|
|||
pre.code .ln { color: grey; } /* line numbers */ |
|||
pre.code, code { background-color: #eeeeee } |
|||
pre.code .comment, code .comment { color: #5C6576 } |
|||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } |
|||
pre.code .literal.string, code .literal.string { color: #0C5404 } |
|||
pre.code .name.builtin, code .name.builtin { color: #352B84 } |
|||
pre.code .deleted, code .deleted { background-color: #DEB0A1} |
|||
pre.code .inserted, code .inserted { background-color: #A3D289} |
|||
|
|||
span.classifier { |
|||
font-family: sans-serif ; |
|||
font-style: oblique } |
|||
|
|||
span.classifier-delimiter { |
|||
font-family: sans-serif ; |
|||
font-weight: bold } |
|||
|
|||
span.interpreted { |
|||
font-family: sans-serif } |
|||
|
|||
span.option { |
|||
white-space: nowrap } |
|||
|
|||
span.pre { |
|||
white-space: pre } |
|||
|
|||
span.problematic { |
|||
color: red } |
|||
|
|||
span.section-subtitle { |
|||
/* font-size relative to parent (h1..h6 element) */ |
|||
font-size: 80% } |
|||
|
|||
table.citation { |
|||
border-left: solid 1px gray; |
|||
margin-left: 1px } |
|||
|
|||
table.docinfo { |
|||
margin: 2em 4em } |
|||
|
|||
table.docutils { |
|||
margin-top: 0.5em ; |
|||
margin-bottom: 0.5em } |
|||
|
|||
table.footnote { |
|||
border-left: solid 1px black; |
|||
margin-left: 1px } |
|||
|
|||
table.docutils td, table.docutils th, |
|||
table.docinfo td, table.docinfo th { |
|||
padding-left: 0.5em ; |
|||
padding-right: 0.5em ; |
|||
vertical-align: top } |
|||
|
|||
table.docutils th.field-name, table.docinfo th.docinfo-name { |
|||
font-weight: bold ; |
|||
text-align: left ; |
|||
white-space: nowrap ; |
|||
padding-left: 0 } |
|||
|
|||
/* "booktabs" style (no vertical lines) */ |
|||
table.docutils.booktabs { |
|||
border: 0px; |
|||
border-top: 2px solid; |
|||
border-bottom: 2px solid; |
|||
border-collapse: collapse; |
|||
} |
|||
table.docutils.booktabs * { |
|||
border: 0px; |
|||
} |
|||
table.docutils.booktabs th { |
|||
border-bottom: thin solid; |
|||
text-align: left; |
|||
} |
|||
|
|||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, |
|||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { |
|||
font-size: 100% } |
|||
|
|||
ul.auto-toc { |
|||
list-style-type: none } |
|||
|
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="document" id="pos-container"> |
|||
<h1 class="title">POS Container</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="https://github.com/OCA/pos/tree/12.0/pos_container"><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_container"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/184/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> |
|||
<p>This module allows to handle use of reusable containers in POS, |
|||
this is useful to handle selling product in bulk without having to calculate |
|||
the tare of the container.</p> |
|||
<p>Each container is identified by a barcode, the weight is stored in Odoo.</p> |
|||
<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="#bug-tracker" id="id2">Bug Tracker</a></li> |
|||
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul> |
|||
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li> |
|||
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li> |
|||
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="usage"> |
|||
<h1><a class="toc-backref" href="#id1">Usage</a></h1> |
|||
<p>You have to create a Barcode Nomenclature to handle containers before using the |
|||
module.</p> |
|||
</div> |
|||
<div class="section" id="bug-tracker"> |
|||
<h1><a class="toc-backref" href="#id2">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_container%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="#id3">Credits</a></h1> |
|||
<div class="section" id="authors"> |
|||
<h2><a class="toc-backref" href="#id4">Authors</a></h2> |
|||
<ul class="simple"> |
|||
<li>Coop IT Easy SCRLfs</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="contributors"> |
|||
<h2><a class="toc-backref" href="#id5">Contributors</a></h2> |
|||
<blockquote> |
|||
<ul class="simple"> |
|||
<li>Pierrick Brun <<a class="reference external" href="mailto:pierrick.brun@akretion.com">pierrick.brun@akretion.com</a>></li> |
|||
<li>Robin Keunen <<a class="reference external" href="mailto:robin.keunen@coopiteasy.be">robin.keunen@coopiteasy.be</a>></li> |
|||
</ul> |
|||
</blockquote> |
|||
</div> |
|||
<div class="section" id="maintainers"> |
|||
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2> |
|||
<p>This module is maintained by the OCA.</p> |
|||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a> |
|||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use.</p> |
|||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/pos/tree/12.0/pos_container">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> |
@ -0,0 +1,191 @@ |
|||
/* firefox seems to ignore the relative positionning of the subwindow-container |
|||
* putting this inside subwindow-container fixes it. |
|||
*/ |
|||
.pos .containerlist-screen .window, |
|||
.pos .containerlist-screen .full-content .subwindow{ |
|||
display: block; |
|||
} |
|||
.pos .containerlist-screen .full-content .subwindow-container{ |
|||
display: block; |
|||
height: 100%; |
|||
} |
|||
.pos .containerlist-screen .full-content .subwindow.collapsed, |
|||
.pos .containerlist-screen .full-content .subwindow-container.collapsed{ |
|||
height: auto; |
|||
} |
|||
|
|||
/* The Scale Container Screen */ |
|||
.pos .scale-screen .add-container{ |
|||
text-align: center; |
|||
font-size: 32px; |
|||
background: rgb(110,200,155); |
|||
color: white; |
|||
border-radius: 3px; |
|||
padding: 16px; |
|||
margin: 16px; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.pos .scale-screen .container-name,.container-barcode{ |
|||
text-align: center; |
|||
font-size: 25px; |
|||
border-radius: 3px; |
|||
padding-top: 10px; |
|||
padding-bottom:10px; |
|||
margin-top: 5px; |
|||
} |
|||
|
|||
/* e) The Container List Screen */ |
|||
|
|||
.pos .containerlist-screen .container-list{ |
|||
font-size: 16px; |
|||
width: 100%; |
|||
line-height: 40px; |
|||
} |
|||
.pos .containerlist-screen .container-list th, |
|||
.pos .containerlist-screen .container-list td { |
|||
padding: 0px 8px; |
|||
} |
|||
.pos .containerlist-screen .container-list tr{ |
|||
transition: all 150ms linear; |
|||
background: rgb(230,230,230); |
|||
} |
|||
.pos .containerlist-screen .container-list thead > tr, |
|||
.pos .containerlist-screen .container-list tr:nth-child(even) { |
|||
background: rgb(247,247,247); |
|||
} |
|||
.pos .containerlist-screen .container-list tr.highlight{ |
|||
transition: all 150ms linear; |
|||
background: rgb(110,200,155) !important; |
|||
color: white; |
|||
} |
|||
.pos .containerlist-screen .container-list tr.lowlight{ |
|||
transition: all 150ms linear; |
|||
background: rgb(216, 238, 227); |
|||
} |
|||
.pos .containerlist-screen .container-list tr.lowlight:nth-child(even){ |
|||
transition: all 150ms linear; |
|||
background: rgb(227, 246, 237); |
|||
} |
|||
.pos .containerlist-screen .container-details{ |
|||
padding: 16px; |
|||
border-bottom: solid 5px rgb(110,200,155); |
|||
} |
|||
.pos .containerlist-screen .container-picture{ |
|||
height: 64px; |
|||
width: 64px; |
|||
border-radius: 32px; |
|||
overflow: hidden; |
|||
text-align: center; |
|||
float: left; |
|||
margin-right: 16px; |
|||
background: white; |
|||
position: relative; |
|||
} |
|||
.pos .containerlist-screen .container-picture > img { |
|||
position: absolute; |
|||
top: -9999px; |
|||
bottom: -9999px; |
|||
right: -9999px; |
|||
left: -9999px; |
|||
max-height: 64px; |
|||
margin: auto; |
|||
} |
|||
.pos .containerlist-screen .container-picture > .fa { |
|||
line-height: 64px; |
|||
font-size: 32px; |
|||
} |
|||
.pos .containerlist-screen .container-picture .image-uploader { |
|||
position: absolute; |
|||
z-index: 1000; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
opacity: 0; |
|||
cursor: pointer; |
|||
} |
|||
.pos .containerlist-screen .container-name { |
|||
font-size: 32px; |
|||
line-height: 64px; |
|||
margin-bottom:16px; |
|||
} |
|||
.pos .containerlist-screen .edit-buttons { |
|||
position: absolute; |
|||
right: 16px; |
|||
top: 10px; |
|||
} |
|||
.pos .containerlist-screen .edit-buttons .button{ |
|||
display: inline-block; |
|||
margin-left: 16px; |
|||
color: rgb(128,128,128); |
|||
cursor: pointer; |
|||
font-size: 36px; |
|||
} |
|||
.pos .containerlist-screen .container-details-box{ |
|||
position: relative; |
|||
font-size: 16px; |
|||
} |
|||
.pos .containerlist-screen .container-details-left{ |
|||
width: 50%; |
|||
float: left; |
|||
} |
|||
.pos .containerlist-screen .container-details-right{ |
|||
width: 50%; |
|||
float: left; |
|||
} |
|||
.pos .containerlist-screen .container-detail{ |
|||
line-height: 24px; |
|||
} |
|||
.pos .containerlist-screen .container-detail > .label{ |
|||
font-weight: bold; |
|||
display: inline-block; |
|||
width: 75px; |
|||
text-align: right; |
|||
margin-right: 8px; |
|||
} |
|||
.pos .containerlist-screen .container-details input, |
|||
.pos .containerlist-screen .container-details select |
|||
{ |
|||
padding: 4px; |
|||
border-radius: 3px; |
|||
border: solid 1px #cecbcb; |
|||
margin-bottom: 4px; |
|||
background: white; |
|||
font-family: "Lato","Lucida Grande", Helvetica, Verdana, Arial; |
|||
color: #555555; |
|||
width: 340px; |
|||
font-size: 14px; |
|||
box-sizing: border-box; |
|||
} |
|||
.pos .containerlist-screen .container-details input.container-name { |
|||
font-size: 24px; |
|||
line-height: 24px; |
|||
margin: 18px 6px; |
|||
width: 340px; |
|||
} |
|||
.pos .containerlist-screen .container-detail > .empty{ |
|||
opacity: 0.3; |
|||
} |
|||
.pos .containerlist-screen .searchbox{ |
|||
right: auto; |
|||
margin-left: -90px; |
|||
margin-top:8px; |
|||
left: 45%; |
|||
} |
|||
.pos .containerlist-screen .searchbox input{ |
|||
width: 120px; |
|||
} |
|||
.pos .containerlist-screen .button.delete-container { |
|||
left: 50%; |
|||
margin-left: 120px; |
|||
} |
|||
|
|||
/* Container Action buttons */ |
|||
.pos .control-button.main { |
|||
width: 75%; |
|||
} |
|||
.pos .control-button.second { |
|||
width: 15%; |
|||
flex-grow: 0; |
|||
} |
@ -0,0 +1,431 @@ |
|||
/* |
|||
Copyright 2019 Coop IT Easy SCRLfs |
|||
Robin Keunen <robin@coopiteasy.be> |
|||
Pierrick Brun <pierrick.brun@akretion.com> |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
*/ |
|||
|
|||
|
|||
odoo.define('pos_container.container', function (require) { |
|||
"use strict"; |
|||
|
|||
var models_and_db = require('pos_container.models_and_db'); |
|||
|
|||
var screens = require('point_of_sale.screens'); |
|||
var gui = require('point_of_sale.gui'); |
|||
var models = require('point_of_sale.models'); |
|||
|
|||
var core = require('web.core'); |
|||
var rpc = require('web.rpc'); |
|||
var QWeb = core.qweb; |
|||
var _t = core._t; |
|||
|
|||
|
|||
var TareButton = screens.ActionButtonWidget.extend({ |
|||
template: 'TareButton', |
|||
}); |
|||
|
|||
screens.define_action_button({ |
|||
'name': 'tare', |
|||
'widget': TareButton, |
|||
}); |
|||
|
|||
screens.NumpadWidget.include({ |
|||
// to put selected-mode on tare button outside the numpadwidget
|
|||
changedMode: function() { |
|||
this._super(); |
|||
if (this.state.get('mode') === 'tare'){ |
|||
$('.mode-button[data-mode="tare"]').addClass('selected-mode'); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
var ContainerButton = screens.ActionButtonWidget.extend({ |
|||
template: 'ContainerButton', |
|||
button_click: function(){ |
|||
this.gui.show_screen('containerlist'); |
|||
} |
|||
}); |
|||
|
|||
screens.define_action_button({ |
|||
'name': 'container', |
|||
'widget': ContainerButton, |
|||
}); |
|||
|
|||
var ContainerListScreenWidget = screens.ScreenWidget.extend({ |
|||
template: 'ContainerListScreenWidget', |
|||
|
|||
init: function(parent, options){ |
|||
this._super(parent, options); |
|||
this.container_cache = new screens.DomCache(); |
|||
}, |
|||
|
|||
show: function(){ |
|||
var self = this; |
|||
this._super(); |
|||
|
|||
this.renderElement(); |
|||
this.$('.back').click(function(){ |
|||
self.gui.back(); |
|||
}); |
|||
|
|||
this.$('.next').click(function(){ |
|||
self.save_changes(); |
|||
self.gui.show_screen('products'); |
|||
}); |
|||
|
|||
this.$('.delete-container').click(function(){ |
|||
if (self.container){ |
|||
self.gui.show_popup('confirm', { |
|||
'title': _t('Container deletion'), |
|||
'body': _t( |
|||
'Do you want to delete this container ?\n').concat( |
|||
self.container.name), |
|||
confirm: function(){ |
|||
self.delete_selected_container(); |
|||
}, |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
var containers = this.pos.db.get_containers_sorted(1000); |
|||
this.render_list(containers); |
|||
|
|||
this.reload_containers(); |
|||
|
|||
this.$('.container-list-contents').delegate('.container-line', |
|||
'click', function(event){ |
|||
self.line_select(event,$(this),$(this).data('id')); |
|||
}); |
|||
|
|||
var search_timeout = null; |
|||
|
|||
if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){ |
|||
this.chrome.widget.keyboard.connect( |
|||
this.$('.searchbox input') |
|||
); |
|||
} |
|||
|
|||
this.$('.searchbox input').on('keyup',function(event){ |
|||
clearTimeout(search_timeout); |
|||
|
|||
var query = this.value; |
|||
|
|||
search_timeout = setTimeout(function(){ |
|||
self.perform_search(query,event.which === 13); |
|||
},70); |
|||
}); |
|||
|
|||
this.$('.searchbox .search-clear').click(function(){ |
|||
self.clear_search(); |
|||
}); |
|||
}, |
|||
delete_selected_container: function(){ |
|||
var self = this; |
|||
|
|||
if (!self.container.id){ |
|||
self.deleted_container(self.container.barcode) |
|||
} |
|||
else { |
|||
rpc.query({ |
|||
model: 'pos.container', |
|||
method: 'unlink', |
|||
args: [self.container.id], |
|||
}).then(function(){ |
|||
self.deleted_container(self.container.barcode); |
|||
},function(err,ev){ |
|||
ev.preventDefault(); |
|||
var error_body = _t('Your Internet connection is probably down.'); |
|||
if (err.data) { |
|||
var except = err.data; |
|||
error_body = except.arguments && except.arguments[0] || except.message || error_body; |
|||
} |
|||
self.gui.show_popup('error',{ |
|||
'title': _t('Error: Could not Save Changes'), |
|||
'body': error_body, |
|||
}); |
|||
} |
|||
); |
|||
} |
|||
}, |
|||
deleted_container: function(barcode){ |
|||
var self = this; |
|||
this.pos.db.remove_containers([barcode]); |
|||
this.$('.container-list .highlight').remove(); |
|||
this.container = null; |
|||
}, |
|||
perform_search: function(query, associate_result){ |
|||
if(query){ |
|||
var containers = this.pos.db.search_container(query); |
|||
if ( associate_result && containers.length === 1){ |
|||
this.container = containers[0]; |
|||
this.save_changes(); |
|||
this.gui.back(); |
|||
} |
|||
this.render_list(containers); |
|||
}else{ |
|||
var containers = this.pos.db.get_containers_sorted(); |
|||
this.render_list(containers); |
|||
} |
|||
}, |
|||
clear_search: function(){ |
|||
var containers = this.pos.db.get_containers_sorted(1000); |
|||
this.render_list(containers); |
|||
this.$('.searchbox input')[0].value = ''; |
|||
this.$('.searchbox input').focus(); |
|||
}, |
|||
render_list: function(containers) { |
|||
var contents = this.$el[0].querySelector('.container-list-contents'); |
|||
contents.innerHTML = ""; |
|||
for(var i = 0, len = Math.min(containers.length,1000); i < len; i++){ |
|||
var container = containers[i]; |
|||
var containerline_html = QWeb.render('ContainerLine',{widget: this, container:containers[i]}); |
|||
var containerline = document.createElement('tbody'); |
|||
containerline.innerHTML = containerline_html; |
|||
containerline = containerline.childNodes[1]; |
|||
|
|||
if(containers === this.container) { |
|||
containerline.classList.add('highlight'); |
|||
} else { |
|||
containerline.classList.remove('highlight'); |
|||
} |
|||
|
|||
contents.appendChild(containerline); |
|||
} |
|||
}, |
|||
save_changes: function(){ |
|||
this.pos.get_order().add_container(this.container); |
|||
}, |
|||
toggle_delete_button: function(){ |
|||
var $button = this.$('.button.delete-container'); |
|||
$button.toggleClass('oe_hidden', !this.container); |
|||
}, |
|||
toggle_save_button: function(){ |
|||
var $button = this.$('.button.next'); |
|||
if (this.container) { |
|||
$button.text('Set Container'); |
|||
} |
|||
$button.toggleClass('oe_hidden', !this.container); |
|||
}, |
|||
line_select: function(event,$line,barcode){ |
|||
var container = this.pos.db.get_container_by_barcode(barcode); |
|||
this.$('.container-list .lowlight').removeClass('lowlight'); |
|||
if ( $line.hasClass('highlight') ){ |
|||
$line.removeClass('highlight'); |
|||
$line.addClass('lowlight'); |
|||
this.toggle_delete_button(); |
|||
this.toggle_save_button(); |
|||
}else{ |
|||
this.$('.container-list .highlight').removeClass('highlight'); |
|||
$line.addClass('highlight'); |
|||
var y = event.pageY - $line.parent().offset().top; |
|||
this.container = container; |
|||
this.toggle_delete_button(); |
|||
this.toggle_save_button(); |
|||
} |
|||
}, |
|||
|
|||
// This fetches container changes on the server, and in case of changes,
|
|||
// rerenders the affected views
|
|||
reload_containers: function(){ |
|||
var self = this; |
|||
return this.pos.load_new_containers().then(function(){ |
|||
// containers may have changed in the backend
|
|||
self.container_cache = new screens.DomCache(); |
|||
|
|||
self.render_list(self.pos.db.get_containers_sorted(1000)); |
|||
|
|||
var last_orderline = self.pos.get_order().get_last_orderline(); |
|||
|
|||
if(last_orderline) { |
|||
// update the currently assigned container if it has been changed in db.
|
|||
var curr_container = last_orderline.get_container(); |
|||
} |
|||
|
|||
if (curr_container) { |
|||
last_orderline.set_container( |
|||
self.pos.db.get_container_by_barcode(curr_container.barcode)); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
close: function(){ |
|||
this._super(); |
|||
if (this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard) { |
|||
this.chrome.widget.keyboard.hide(); |
|||
} |
|||
}, |
|||
|
|||
auto_back: true, |
|||
}); |
|||
|
|||
gui.define_screen({ |
|||
name:'containerlist', |
|||
widget: ContainerListScreenWidget |
|||
}); |
|||
|
|||
// add container barcode scan
|
|||
screens.ScreenWidget.include({ |
|||
barcode_container_action: function(code){ |
|||
var self = this; |
|||
if (self.pos.scan_container(code)) { |
|||
// nothing to do now, the container is added
|
|||
// as an orderline if found.
|
|||
} else { |
|||
self.gui.show_screen('containerscale', {barcode: code.base_code}); |
|||
} |
|||
}, |
|||
show: function(){ |
|||
var self = this; |
|||
this._super(); |
|||
this.pos.barcode_reader.set_action_callback({ |
|||
'container': _.bind(self.barcode_container_action, self), |
|||
}); |
|||
}, |
|||
}); |
|||
|
|||
screens.ProductScreenWidget.include({ |
|||
// to use Tare Button from outside the NumpadWidget
|
|||
start: function(){ |
|||
this._super(); |
|||
var tare_button = $('.mode-button[data-mode="tare"]'); |
|||
tare_button.click(_.bind(this.numpad.clickChangeMode, this.numpad)); |
|||
}, |
|||
// to add a product to a container orderline
|
|||
click_product: function(product) { |
|||
var order = this.pos.get_order(); |
|||
var selected_orderline = order.get_selected_orderline(); |
|||
if (product.to_weight && selected_orderline && |
|||
selected_orderline.product === this.pos.get_container_product()){ |
|||
var container = selected_orderline.get_container(); |
|||
this.gui.show_screen( |
|||
'scale', |
|||
{product: product, |
|||
container: container, |
|||
old_orderline: selected_orderline}); |
|||
} else { |
|||
this._super(product); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
screens.ScaleScreenWidget.include({ |
|||
order_product: function(){ |
|||
// Replace the orderline if the product is the placeholder
|
|||
// container product.
|
|||
var container = this.gui.get_current_screen_param('container'); |
|||
if (container){ |
|||
var order = this.pos.get_order(); |
|||
order.add_product(this.get_product(),{ quantity: this.weight, price: this.price }); |
|||
var orderline = order.get_selected_orderline(); |
|||
orderline.set_container(container); |
|||
var old_orderline = this.gui.get_current_screen_param( |
|||
'old_orderline'); |
|||
if (old_orderline){ |
|||
order.remove_orderline(old_orderline); |
|||
} |
|||
orderline.set_quantity(this.weight); |
|||
orderline.set_gross_weight(this.weight + container.weight); |
|||
orderline.set_tare_mode('AUTO'); |
|||
orderline.trigger('change', orderline); |
|||
} else { |
|||
this._super(); |
|||
var orderline = this.pos.get_order().get_selected_orderline(); |
|||
orderline.set_tare_mode('AUTO'); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
var ContainerScaleScreenWidget = screens.ScaleScreenWidget.extend({ |
|||
template: 'ContainerScaleScreenWidget', |
|||
|
|||
next_screen: 'products', |
|||
previous_screen: 'products', |
|||
|
|||
init: function(parent, options){ |
|||
this._super(parent, options); |
|||
}, |
|||
|
|||
show: function(){ |
|||
this._super(); |
|||
var self = this; |
|||
|
|||
this.$('.next,.add-container').click(function(){ |
|||
self.create_container(); |
|||
}); |
|||
|
|||
if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){ |
|||
this.chrome.widget.keyboard.connect($(this.el.querySelector('.container-name input'))); |
|||
} |
|||
}, |
|||
get_product: function(){ |
|||
return this.pos.get_container_product(); |
|||
}, |
|||
create_container: function(){ |
|||
var self = this; |
|||
var fields = {}; |
|||
|
|||
fields['weight'] = this.weight; |
|||
|
|||
this.$('.container-name .detail').each(function(idx,el){ |
|||
fields['name'] = el.value; |
|||
|
|||
}); |
|||
|
|||
fields.barcode = this.gui.get_current_screen_param('barcode') || false; |
|||
fields.name = fields.name || _t('Container'); |
|||
|
|||
this.pos.push_container(fields).then( |
|||
this.pushed_container(fields["barcode"]) |
|||
); |
|||
}, |
|||
pushed_container: function(barcode){ |
|||
var self = this; |
|||
self.gui.show_screen(self.next_screen); |
|||
}, |
|||
close: function(){ |
|||
this._super(); |
|||
if (this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard) { |
|||
this.chrome.widget.keyboard.hide(); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
gui.define_screen({ |
|||
name:'containerscale', |
|||
widget: ContainerScaleScreenWidget, |
|||
}); |
|||
|
|||
screens.ProductListWidget.include({ |
|||
render_product: function(product){ |
|||
if(product.barcode != 'CONTAINER'){ |
|||
return this._super(product); |
|||
} else { |
|||
return document.createElement('div'); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
screens.OrderWidget.include({ |
|||
set_value: function(val) { |
|||
this._super(val); |
|||
var order = this.pos.get_order(); |
|||
if (order.get_selected_orderline()) { |
|||
var oline = order.get_selected_orderline(); |
|||
var mode = this.numpad_state.get('mode'); |
|||
if( mode === 'tare'){ |
|||
oline.set_tare(val); |
|||
} |
|||
if( mode === 'quantity' && oline.container) { |
|||
oline.set_gross_weight(parseFloat(val) + oline.container.weight); |
|||
} |
|||
oline.set_tare_mode('MAN'); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
return { |
|||
ContainerScaleScreenWidget: ContainerScaleScreenWidget, |
|||
}; |
|||
|
|||
}); |
@ -0,0 +1,619 @@ |
|||
/* |
|||
Copyright 2019 Coop IT Easy SCRLfs |
|||
Pierrick Brun <pierrick.brun@akretion.com> |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
*/ |
|||
|
|||
|
|||
odoo.define('pos_container.models_and_db', function (require) { |
|||
"use strict"; |
|||
|
|||
var PosDB = require('point_of_sale.DB'); |
|||
var models = require('point_of_sale.models'); |
|||
var rpc = require('web.rpc'); |
|||
var config = require('web.config'); |
|||
|
|||
var core = require('web.core'); |
|||
var QWeb = core.qweb; |
|||
|
|||
var utils = require('web.utils'); |
|||
var field_utils = require('web.field_utils'); |
|||
var round_di = utils.round_decimals; |
|||
var round_pr = utils.round_precision; |
|||
|
|||
// include not available => extend
|
|||
models.PosModel = models.PosModel.extend({ |
|||
get_container_product: function(){ |
|||
// assign value if not already assigned.
|
|||
// Avoids rewriting init function
|
|||
if (!this.container_product){ |
|||
this.container_product = this.db.get_product_by_barcode( |
|||
'CONTAINER'); |
|||
} |
|||
return this.container_product |
|||
}, |
|||
scan_container: function(parsed_code){ |
|||
var selected_order = this.get_order(); |
|||
var container = this.db.get_container_by_barcode( |
|||
parsed_code.base_code); |
|||
|
|||
if(!container){ |
|||
return false; |
|||
} |
|||
|
|||
selected_order.add_container(container); |
|||
return true; |
|||
}, |
|||
// reload the list of container, returns as a deferred that resolves if there were
|
|||
// updated containers, and fails if not
|
|||
load_new_containers: function(){ |
|||
var self = this; |
|||
var def = new $.Deferred(); |
|||
var fields = _.find(this.models,function(model){ |
|||
return model.model === 'pos.container'; |
|||
}).fields; |
|||
var domain = []; |
|||
rpc.query({ |
|||
model: 'pos.container', |
|||
method: 'search_read', |
|||
args: [domain, fields], |
|||
}, { |
|||
timeout: 3000, |
|||
shadow: true, |
|||
}) |
|||
.then(function(containers){ |
|||
if (self.db.add_containers(containers)) { |
|||
// check if the partners we got were real updates
|
|||
def.resolve(); |
|||
} else { |
|||
def.reject(); |
|||
} |
|||
}, function(type,err){ def.reject(); }); |
|||
return def; |
|||
}, |
|||
// load placeholder product for containers.
|
|||
// it is done here to load it even if inactivated.
|
|||
load_placeholder_product: function(){ |
|||
var self = this; |
|||
var fields = _.find(this.models,function(model){ |
|||
return model.model === 'product.product'; |
|||
}).fields; |
|||
var domain = [['barcode', '=', 'CONTAINER'], ['active', '=', false]]; |
|||
// no need to load it when active because it is already done in standard
|
|||
return rpc.query({ |
|||
model: 'product.product', |
|||
method: 'search_read', |
|||
args: [domain, fields], |
|||
}).then(function(products){ |
|||
self.db.add_products(_.map(products, function (product) { |
|||
return new models.Product({}, product); |
|||
})); |
|||
}); |
|||
}, |
|||
|
|||
// saves the container locally and try to send it to the backend.
|
|||
// it returns a deferred that succeeds after having tried to send the container and all the other pending containers.
|
|||
push_container: function(container, opts) { |
|||
opts = opts || {}; |
|||
var self = this; |
|||
|
|||
if(container){ |
|||
this.db.add_containers([container]); |
|||
} |
|||
|
|||
var pushed = new $.Deferred(); |
|||
|
|||
this.flush_mutex.exec(function(){ |
|||
var flushed = self._save_containers_to_server(self.db.get_containers_sorted(), opts); |
|||
|
|||
flushed.always(function(ids){ |
|||
pushed.resolve(); |
|||
}); |
|||
|
|||
return flushed; |
|||
}); |
|||
return pushed; |
|||
}, |
|||
|
|||
// send an array of containers to the server
|
|||
// available options:
|
|||
// - timeout: timeout for the rpc call in ms
|
|||
// returns a deferred that resolves with the list of
|
|||
// server generated ids for the sent containers
|
|||
_save_containers_to_server: function (containers, options) { |
|||
var self = this; |
|||
var containers= containers.filter(container => !( "id" in container)) |
|||
if (!containers || !containers.length) { |
|||
var result = $.Deferred(); |
|||
result.resolve([]); |
|||
return result; |
|||
} |
|||
|
|||
options = options || {}; |
|||
var timeout = typeof options.timeout === 'number' ? options.timeout : 7500 * containers.length; |
|||
|
|||
return rpc.query({ |
|||
model: 'pos.container', |
|||
method: 'create_from_ui', |
|||
args: containers, |
|||
}, { |
|||
timeout: timeout, |
|||
}) |
|||
.then(function (server_ids) { |
|||
//self.db.remove_containers(containers);
|
|||
_.each(containers, function(container, key){ |
|||
container["id"] = server_ids[key] |
|||
}); |
|||
//self.db.add_containers(containers)
|
|||
self.set('failed',false); |
|||
return server_ids; |
|||
}).fail(function (type, error){ |
|||
if(error.code === 200 ){ // Business Logic Error, not a connection problem
|
|||
//if warning do not need to display traceback!!
|
|||
if (error.data.exception_type == 'warning') { |
|||
delete error.data.debug; |
|||
} |
|||
|
|||
// Hide error if already shown before ...
|
|||
if ((!self.get('failed') || options.show_error) && !options.to_invoice) { |
|||
self.gui.show_popup('error-traceback',{ |
|||
'title': error.data.message, |
|||
'body': error.data.debug |
|||
}); |
|||
} |
|||
self.set('failed',error); |
|||
} |
|||
console.error('Failed to send containers:', containers); |
|||
}); |
|||
}, |
|||
|
|||
// wrapper around the _save_to_server that updates the synch status widget
|
|||
// it is modified to send containers before orders
|
|||
_flush_orders: function(orders, options) { |
|||
var self = this; |
|||
this.set('synch',{ state: 'connecting', pending: orders.length}); |
|||
|
|||
return self._save_containers_to_server(self.db.get_containers_sorted()) |
|||
.then(function(container_ids) { |
|||
for (var i=0; i < orders.length; i++){ |
|||
if (orders[i].data.lines) { |
|||
for (var j=0; j < orders[i].data.lines[0].length; j++){ |
|||
var orderline = orders[i].data.lines[0][j] |
|||
if ( !orderline.container_id && orderline.container_barcode) { |
|||
orderline.container_id = self.db.get_container_by_barcode(orderline.container_barcode).id; |
|||
delete orderline["container_barcode"] |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return self._save_to_server(orders, options) |
|||
}).done(function (server_ids) { |
|||
var pending = self.db.get_orders().length; |
|||
|
|||
self.set('synch', { |
|||
state: pending ? 'connecting' : 'connected', |
|||
pending: pending |
|||
}); |
|||
|
|||
return server_ids; |
|||
}).fail(function(error, event){ |
|||
var pending = self.db.get_orders().length; |
|||
if (self.get('failed')) { |
|||
self.set('synch', { state: 'error', pending: pending }); |
|||
} else { |
|||
self.set('synch', { state: 'disconnected', pending: pending }); |
|||
} |
|||
}); |
|||
}, |
|||
}); |
|||
|
|||
models.Order = models.Order.extend({ |
|||
add_container: function(container, options){ |
|||
if(this._printed){ |
|||
this.destroy(); |
|||
return this.pos.get_order().add_container(container, options); |
|||
} |
|||
options = options || {}; |
|||
var attr = JSON.parse(JSON.stringify(container)); |
|||
attr.pos = this.pos; |
|||
attr.order = this; |
|||
var product = this.pos.get_container_product(); |
|||
var line = new models.Orderline({}, { |
|||
pos: this.pos, order: this, product: product}); |
|||
|
|||
line.set_container(container); |
|||
line.set_quantity(0); |
|||
this.orderlines.add(line); |
|||
|
|||
this.select_orderline(this.get_last_orderline()); |
|||
}, |
|||
has_tare_line: function(mode){ |
|||
var orderlines = this.orderlines.models |
|||
for(var i=0; i < orderlines.length; i++){ |
|||
var line = orderlines[i]; |
|||
if(line && line.get_tare_mode() === mode){ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
}, |
|||
export_for_printing: function(){ |
|||
var orderlines = []; |
|||
var self = this; |
|||
|
|||
this.orderlines.each(function(orderline){ |
|||
orderlines.push(orderline.export_for_printing()); |
|||
}); |
|||
|
|||
var paymentlines = []; |
|||
this.paymentlines.each(function(paymentline){ |
|||
paymentlines.push(paymentline.export_for_printing()); |
|||
}); |
|||
var client = this.get('client'); |
|||
var cashier = this.pos.get_cashier(); |
|||
var company = this.pos.company; |
|||
var shop = this.pos.shop; |
|||
var date = new Date(); |
|||
|
|||
function is_xml(subreceipt){ |
|||
return subreceipt ? (subreceipt.split('\n')[0].indexOf('<!DOCTYPE QWEB') >= 0) : false; |
|||
} |
|||
|
|||
function render_xml(subreceipt){ |
|||
if (!is_xml(subreceipt)) { |
|||
return subreceipt; |
|||
} else { |
|||
subreceipt = subreceipt.split('\n').slice(1).join('\n'); |
|||
var qweb = new QWeb2.Engine(); |
|||
qweb.debug = config.debug; |
|||
qweb.default_dict = _.clone(QWeb.default_dict); |
|||
qweb.add_template('<templates><t t-name="subreceipt">'+subreceipt+'</t></templates>'); |
|||
|
|||
return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.chrome,'order':self, 'receipt': receipt}) ; |
|||
} |
|||
} |
|||
|
|||
var receipt = { |
|||
orderlines: orderlines, |
|||
paymentlines: paymentlines, |
|||
subtotal: this.get_subtotal(), |
|||
total_with_tax: this.get_total_with_tax(), |
|||
total_without_tax: this.get_total_without_tax(), |
|||
total_tax: this.get_total_tax(), |
|||
total_paid: this.get_total_paid(), |
|||
total_discount: this.get_total_discount(), |
|||
tax_details: this.get_tax_details(), |
|||
change: this.get_change(), |
|||
name : this.get_name(), |
|||
client: client ? client.name : null , |
|||
invoice_id: null, //TODO
|
|||
cashier: cashier ? cashier.name : null, |
|||
precision: { |
|||
price: 2, |
|||
money: 2, |
|||
quantity: 3, |
|||
}, |
|||
date: { |
|||
year: date.getFullYear(), |
|||
month: date.getMonth(), |
|||
date: date.getDate(), // day of the month
|
|||
day: date.getDay(), // day of the week
|
|||
hour: date.getHours(), |
|||
minute: date.getMinutes() , |
|||
isostring: date.toISOString(), |
|||
localestring: date.toLocaleString(), |
|||
}, |
|||
company:{ |
|||
email: company.email, |
|||
website: company.website, |
|||
company_registry: company.company_registry, |
|||
contact_address: company.partner_id[1], |
|||
vat: company.vat, |
|||
vat_label: company.country && company.country.vat_label || '', |
|||
name: company.name, |
|||
phone: company.phone, |
|||
logo: this.pos.company_logo_base64, |
|||
}, |
|||
shop:{ |
|||
name: shop.name, |
|||
}, |
|||
currency: this.pos.currency, |
|||
//custom here
|
|||
has_tare_mode: { |
|||
auto: this.has_tare_line('AUTO'), |
|||
manual: this.has_tare_line('MAN'), |
|||
} |
|||
//custom end
|
|||
}; |
|||
|
|||
if (is_xml(this.pos.config.receipt_header)){ |
|||
receipt.header = ''; |
|||
receipt.header_xml = render_xml(this.pos.config.receipt_header); |
|||
} else { |
|||
receipt.header = this.pos.config.receipt_header || ''; |
|||
} |
|||
|
|||
if (is_xml(this.pos.config.receipt_footer)){ |
|||
receipt.footer = ''; |
|||
receipt.footer_xml = render_xml(this.pos.config.receipt_footer); |
|||
} else { |
|||
receipt.footer = this.pos.config.receipt_footer || ''; |
|||
} |
|||
|
|||
return receipt; |
|||
}, |
|||
}); |
|||
|
|||
// Add container to order line
|
|||
models.Orderline = models.Orderline.extend({ |
|||
get_container: function(){ |
|||
return this.container; |
|||
}, |
|||
set_container: function(container){ |
|||
this.container = container; |
|||
}, |
|||
set_tare_mode: function(mode){ |
|||
if (['MAN', 'AUTO'].indexOf(mode) != -1){ |
|||
this.tare_mode = mode; |
|||
this.trigger('change', this); |
|||
} |
|||
}, |
|||
get_tare_mode: function() { |
|||
return this.tare_mode; |
|||
}, |
|||
set_tare: function(tare){ |
|||
this.tare = this.get_value_rounded(tare).toFixed(3); |
|||
this.container = null; |
|||
if (this.gross_weight && this.gross_weight != 'NaN'){ |
|||
this.set_quantity(this.gross_weight - parseFloat(this.tare)); |
|||
} |
|||
else{ |
|||
this.set_gross_weight(this.quantity); |
|||
this.set_quantity(this.quantity - parseFloat(this.tare)); |
|||
} |
|||
this.trigger('change', this); |
|||
}, |
|||
get_tare: function(){ |
|||
return this.tare || 0; |
|||
}, |
|||
get_gross_weight: function(){ |
|||
return this.gross_weight; |
|||
}, |
|||
set_gross_weight: function(weight){ |
|||
this.gross_weight = this.get_value_rounded(weight).toFixed(3); |
|||
this.trigger('change', this); |
|||
}, |
|||
set_quantity: function(quantity, keep_price){ |
|||
// copied from odoo core
|
|||
this.order.assert_editable(); |
|||
if(quantity === 'remove'){ |
|||
this.order.remove_orderline(this); |
|||
return; |
|||
}else{ |
|||
var quant = parseFloat(quantity) || 0; |
|||
var unit = this.get_unit(); |
|||
if(unit){ |
|||
if (unit.rounding) { |
|||
this.quantity = round_pr(quant, unit.rounding); |
|||
var decimals = this.pos.dp['Product Unit of Measure']; |
|||
this.quantity = round_di(this.quantity, decimals) |
|||
this.quantityStr = field_utils.format.float(this.quantity, {digits: [69, decimals]}); |
|||
} else { |
|||
this.quantity = round_pr(quant, 1); |
|||
this.quantityStr = this.quantity.toFixed(0); |
|||
} |
|||
}else{ |
|||
this.quantity = quant; |
|||
this.quantityStr = '' + this.quantity; |
|||
} |
|||
} |
|||
// just like in sale.order changing the quantity will recompute the unit price
|
|||
if(! keep_price && ! this.price_manually_set){ |
|||
this.set_unit_price(this.product.get_price(this.order.pricelist, this.get_quantity())); |
|||
this.order.fix_tax_included_price(this); |
|||
} |
|||
|
|||
// surcharge starts here
|
|||
if (this.tare){ |
|||
this.set_gross_weight(this.quantity + parseFloat(this.tare)); |
|||
} |
|||
this.trigger('change', this); |
|||
}, |
|||
get_value_rounded: function(value){ |
|||
var value = parseFloat(value) || 0; |
|||
var unit = this.get_unit(); |
|||
if(unit){ |
|||
if (unit.rounding) { |
|||
value = round_pr(value, unit.rounding); |
|||
var decimals = this.pos.dp['Product Unit of Measure']; |
|||
value = round_di(value, decimals) |
|||
} else { |
|||
value = round_pr(value, 1); |
|||
} |
|||
} |
|||
return value; |
|||
}, |
|||
export_as_JSON: function(){ |
|||
var pack_lot_ids = []; |
|||
if (this.has_product_lot){ |
|||
this.pack_lot_lines.each(_.bind( function(item) { |
|||
return pack_lot_ids.push([0, 0, item.export_as_JSON()]); |
|||
}, this)); |
|||
} |
|||
return { |
|||
qty: this.get_quantity(), |
|||
price_unit: this.get_unit_price(), |
|||
price_subtotal: this.get_price_without_tax(), |
|||
price_subtotal_incl: this.get_price_with_tax(), |
|||
discount: this.get_discount(), |
|||
product_id: this.get_product().id, |
|||
tax_ids: [[6, false, _.map(this.get_applicable_taxes(), function(tax){ return tax.id; })]], |
|||
id: this.id, |
|||
pack_lot_ids: pack_lot_ids, |
|||
//custom starts here
|
|||
tare: this.get_tare() ? this.get_tare() : null, |
|||
container_id: this.get_container() ? this.get_container().id : null, |
|||
container_barcode: this.get_container() ? this.get_container().barcode : null, |
|||
container_weight: this.get_container() ? this.get_container().weight : null, |
|||
}; |
|||
}, |
|||
//used to create a json of the ticket, to be sent to the printer
|
|||
export_for_printing: function(){ |
|||
return { |
|||
quantity: this.get_quantity(), |
|||
unit_name: this.get_unit().name, |
|||
price: this.get_unit_price(), |
|||
discount: this.get_discount(), |
|||
product_name: this.get_product().display_name, |
|||
product_name_wrapped: this.generate_wrapped_product_name(), |
|||
price_display : this.get_display_price(), |
|||
price_with_tax : this.get_price_with_tax(), |
|||
price_without_tax: this.get_price_without_tax(), |
|||
tax: this.get_tax(), |
|||
product_description: this.get_product().description, |
|||
product_description_sale: this.get_product().description_sale, |
|||
// extension starts here
|
|||
container: this.get_container(), |
|||
tare: this.get_tare(), |
|||
tare_mode: this.get_tare_mode(), |
|||
gross_weight: this.get_gross_weight(), |
|||
product_barcode: this.get_product().barcode, |
|||
}; |
|||
}, |
|||
}); |
|||
|
|||
PosDB.include({ |
|||
init: function(parent, options) { |
|||
this._super(parent, options); |
|||
|
|||
this.container_sorted = []; |
|||
this.container_by_id = {}; |
|||
this.container_by_barcode = {}; |
|||
this.container_search_string = ""; |
|||
this.container_write_date = null; |
|||
}, |
|||
_container_search_string: function(container){ |
|||
|
|||
var str = ''; |
|||
|
|||
if(container.barcode){ |
|||
str += '|' + container.barcode; |
|||
} |
|||
if(container.name) { |
|||
str += '|' + container.name; |
|||
} |
|||
var id = container.id || 0; |
|||
str = '' + id + ':' + str.replace(':','') + '\n'; |
|||
|
|||
return str; |
|||
}, |
|||
add_containers: function(containers) { |
|||
var updated_count = 0; |
|||
var new_write_date = ''; |
|||
for(var i = 0, len = containers.length; i < len; i++) { |
|||
var container = containers[i]; |
|||
|
|||
if (this.container_write_date && |
|||
this.container_by_barcode[container.barcode] && |
|||
new Date(this.container_write_date).getTime() + 1000 >= |
|||
new Date(container.write_date).getTime() ) { |
|||
// FIXME: The write_date is stored with milisec precision in the database
|
|||
// but the dates we get back are only precise to the second. This means when
|
|||
// you read containers modified strictly after time X, you get back containers that were
|
|||
// modified X - 1 sec ago.
|
|||
continue; |
|||
} else if ( new_write_date < container.write_date ) { |
|||
new_write_date = container.write_date; |
|||
} |
|||
if (!this.container_by_barcode[container.barcode]) { |
|||
this.container_sorted.push(container.barcode); |
|||
} |
|||
this.container_by_barcode[container.barcode] = container; |
|||
|
|||
updated_count += 1; |
|||
} |
|||
|
|||
this.container_write_date = new_write_date || this.container_write_date; |
|||
|
|||
if (updated_count) { |
|||
// If there were updates, we need to completely
|
|||
// rebuild the search string and the id indexing
|
|||
|
|||
this.container_search_string = ""; |
|||
this.container_by_id = {}; |
|||
|
|||
for (var barcode in this.container_by_barcode) { |
|||
var container = this.container_by_barcode[barcode]; |
|||
|
|||
if(container.id){ |
|||
this.container_by_id[container.id] = container; |
|||
} |
|||
this.container_search_string += this._container_search_string(container); |
|||
} |
|||
} |
|||
return updated_count; |
|||
}, |
|||
remove_containers: function(barcodes){ |
|||
for(var i = 0; i < barcodes.length; i++) { |
|||
var container = this.container_by_barcode[barcodes[i]]; |
|||
if (container){ |
|||
var index_s = this.container_sorted.indexOf(container.barcode); |
|||
this.container_sorted.splice(index_s, 1); |
|||
delete this.container_by_id[container.id]; |
|||
delete this.container_by_barcode[container.barcode]; |
|||
} |
|||
} |
|||
}, |
|||
get_container_write_date: function(){ |
|||
return this.container_write_date; |
|||
}, |
|||
get_container_by_id: function(id){ |
|||
return this.container_by_id[id]; |
|||
}, |
|||
get_container_by_barcode: function(barcode){ |
|||
return this.container_by_barcode[barcode]; |
|||
}, |
|||
get_containers_sorted: function(max_count){ |
|||
max_count = max_count ? Math.min(this.container_sorted.length, max_count) : this.container_sorted.length; |
|||
var containers = []; |
|||
for (var i = 0; i < max_count; i++) { |
|||
containers.push(this.container_by_barcode[this.container_sorted[i]]); |
|||
} |
|||
|
|||
return containers; |
|||
}, |
|||
search_container: function(query) { |
|||
try { |
|||
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.'); |
|||
query = query.replace(' ','.+'); |
|||
var re = RegExp("([0-9]+):\\|([0-9]*"+query+"[0-9]*\\|\|[0-9]*\\|.*?"+query+")","gi"); |
|||
} catch(e) { |
|||
return []; |
|||
} |
|||
var results = []; |
|||
for(var i = 0; i < this.limit; i++) { |
|||
var r = re.exec(this.container_search_string); |
|||
if(r) { |
|||
// r[1] = id, r[2] = barcode
|
|||
var barcode = r[2].substring(0, r[2].indexOf("\|")); |
|||
results.push(this.get_container_by_barcode(barcode)); |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return results; |
|||
}, |
|||
|
|||
}); |
|||
|
|||
models.load_models({ |
|||
model: 'pos.container', |
|||
fields: ['name','barcode', 'weight'], |
|||
loaded: function(self, containers){ |
|||
self.db.add_containers(containers); |
|||
return self.load_placeholder_product(); |
|||
}, |
|||
}); |
|||
|
|||
}); |
@ -0,0 +1,244 @@ |
|||
odoo.define('pos_container.tour.tare', function (require) { |
|||
"use strict"; |
|||
|
|||
var Tour = require('web_tour.tour'); |
|||
|
|||
function click_numpad(num) { |
|||
return { |
|||
content: "click on numpad button '" + num + "'", |
|||
trigger: ".input-button.number-char:contains('"+num+"')" |
|||
} |
|||
} |
|||
|
|||
function scan(barcode) { |
|||
return { |
|||
content: "Scanning barcode " + barcode, |
|||
trigger: "input.ean", |
|||
run: "text " + barcode |
|||
} |
|||
} |
|||
|
|||
function confirm_scan() { |
|||
return { |
|||
content: "Confirm barcode", |
|||
trigger: ".button.barcode", |
|||
} |
|||
} |
|||
|
|||
function set_weight(weight) { |
|||
return { |
|||
content: "Setting weight " + weight, |
|||
trigger: "input.weight", |
|||
run: "text " + weight |
|||
} |
|||
} |
|||
|
|||
function confirm_weight() { |
|||
return { |
|||
content: "Confirm weight", |
|||
trigger: ".button.set_weight", |
|||
} |
|||
} |
|||
|
|||
function check_selected_orderline(message, check) { |
|||
return { |
|||
content: message, |
|||
trigger: ".orderline.selected " + check, |
|||
run: function () {}, // it's a check
|
|||
} |
|||
} |
|||
|
|||
|
|||
var steps = [{ |
|||
content: 'waiting for loading to finish', |
|||
trigger: '.o_main_content:has(.loader:hidden)', |
|||
run: function () {}, |
|||
}, |
|||
scan('0499999999998'), |
|||
confirm_scan(), |
|||
set_weight(0.1), |
|||
confirm_weight(), |
|||
{ |
|||
content: "Click on save", |
|||
trigger: ".add-container", |
|||
}, |
|||
// Test a second time with a custom name
|
|||
scan('0499999999981'), |
|||
confirm_scan(), |
|||
set_weight(0.2), |
|||
confirm_weight(), |
|||
{ |
|||
content: "Set a custom name", |
|||
trigger:"input.container-name", |
|||
run:"text TOTO", |
|||
}, { |
|||
content: "Click on save", |
|||
trigger: ".add-container", |
|||
}, |
|||
// Scan du premier contenant
|
|||
scan('0499999999998'), |
|||
confirm_scan(), |
|||
check_selected_orderline("Check: empty container in the orderline", ".product-name:contains('Container without product')"), |
|||
check_selected_orderline("Check: the name is 'Container'", ".info:contains('Container')"), |
|||
check_selected_orderline("Check: the quantity is 0", ".info em:contains('0.000')"), |
|||
{ |
|||
content: "select product", |
|||
trigger: ".product:contains('Whiteboard Pen')", //UoM = kg
|
|||
}, |
|||
set_weight(0.2), |
|||
confirm_weight(), |
|||
{ |
|||
content: "validate weight", |
|||
trigger: ".buy-product", |
|||
}, |
|||
check_selected_orderline("Check: the name is 'Container'", ".info:contains('Container')"), |
|||
check_selected_orderline("Check: orderline in AUTO tare mode", ".pos-right-align:contains('AUTO')"), |
|||
check_selected_orderline("Check: orderline's product is the Pen", ".product-name:contains('Whiteboard Pen')"), |
|||
check_selected_orderline("Check: the quantity is the tared weight", ".info:contains('0.200')"), |
|||
{ |
|||
content: "click container button", |
|||
trigger: ".control-button.o_container_button", |
|||
}, { |
|||
content: "Search Container TOTO", |
|||
trigger: ".searchbox input", |
|||
run: "text TOTO", |
|||
}, { |
|||
content: "Select container TOTO", |
|||
trigger: ".container-line:contains('TOTO')", |
|||
}, { |
|||
content: "Click delete", |
|||
trigger: ".button.delete-container", |
|||
}, { |
|||
content: "Click cancel", |
|||
trigger: ".button.cancel", |
|||
}, { |
|||
content: "Click delete", |
|||
trigger: ".button.delete-container", |
|||
}, { |
|||
content: "Click confirm", |
|||
trigger: ".button.confirm", |
|||
}, { |
|||
content: "Search by barcode", |
|||
trigger: ".searchbox input", |
|||
run: "text 0499999999998", |
|||
}, { |
|||
content: "select the searched container", |
|||
trigger: ".container-line:contains('Container')", |
|||
}, { |
|||
content: "confirm selection", |
|||
trigger: ".containerlist-screen .next", |
|||
}, { |
|||
content: "remove orderline quantity", |
|||
trigger: ".input-button.numpad-backspace", |
|||
}, { |
|||
content: "delete orderline", |
|||
trigger: ".input-button.numpad-backspace", |
|||
}, { |
|||
content: "select another product", |
|||
trigger: ".product:contains('Desk Organizer')", //UoM = kg
|
|||
}, |
|||
set_weight(0.5), |
|||
confirm_weight(), |
|||
{ |
|||
content: "confirm purchase", |
|||
trigger: ".buy-product", |
|||
}, { |
|||
content: "switch numpad to tare mode", |
|||
trigger: ".control-button.o_tare_button", |
|||
}, |
|||
click_numpad(0), |
|||
click_numpad('.'), |
|||
click_numpad(2), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
check_selected_orderline("Check: orderline's product is the Organizer", ".product-name:contains('Desk Organizer')"), |
|||
check_selected_orderline("Check: the quantity is the tared weight", ".info:contains('0.300')"), |
|||
{ |
|||
content: "click orderline auto", |
|||
trigger: ".orderline .pos-right-align:contains('AUTO')", |
|||
}, { |
|||
content: "switch numpad to tare mode", |
|||
trigger: ".control-button.o_tare_button", |
|||
}, |
|||
click_numpad(0), |
|||
click_numpad('.'), |
|||
click_numpad(2), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
check_selected_orderline("Check: orderline's product is the Pen", ".product-name:contains('Whiteboard Pen')"), |
|||
check_selected_orderline("Check: the quantity is the tared weight", ".info em:contains('0.100')"), |
|||
{ |
|||
content: "switch numpad to quantity mode", |
|||
trigger: ".mode-button[data-mode='quantity']", |
|||
}, |
|||
click_numpad(0), |
|||
click_numpad('.'), |
|||
click_numpad(6), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
check_selected_orderline("Check: orderline's product is the Pen", ".product-name:contains('Whiteboard Pen')"), |
|||
check_selected_orderline("Check: the quantity is 0.6", ".info em:contains('0.600')"), |
|||
check_selected_orderline("Check: the tare is unchanged", ".info:contains('0.2')"), |
|||
check_selected_orderline("Check: the gross weight is the tare + the quantity", ".info:contains('Gross : 0.8 kg')"), |
|||
{ |
|||
content: "Add a unit product", |
|||
trigger: ".product:contains('Large Cabinet')", |
|||
}, { |
|||
content: "click discount", |
|||
trigger: ".mode-button[data-mode='discount']", |
|||
}, |
|||
click_numpad(1), |
|||
click_numpad(0), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
check_selected_orderline("Check: the undiscounted price is still 320", ".info:contains('320.00')"), |
|||
{ |
|||
content: "Add a unit product", |
|||
trigger: ".product:contains('Large Cabinet')", |
|||
}, { |
|||
content: "click price change", |
|||
trigger: ".mode-button[data-mode='price']", |
|||
}, |
|||
click_numpad(2), |
|||
click_numpad(0), |
|||
click_numpad(0), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
// Ajouter une ligne en AUTO
|
|||
scan('0499999999998'), |
|||
confirm_scan(), |
|||
{ |
|||
content: "select product", |
|||
trigger: ".product:contains('Whiteboard Pen')", //UoM = kg
|
|||
}, |
|||
set_weight(0.3), |
|||
confirm_weight(), |
|||
{ |
|||
content: "validate weight", |
|||
trigger: ".buy-product", |
|||
}, |
|||
{ |
|||
content: "pay", |
|||
trigger: ".button.pay", |
|||
}, |
|||
click_numpad(2), |
|||
click_numpad(0), |
|||
click_numpad(0), |
|||
click_numpad(0), |
|||
{ |
|||
content: "validate", |
|||
trigger: ".button.next", |
|||
}]; |
|||
|
|||
|
|||
var autre = [{ |
|||
content: "relancer POS", |
|||
trigger: "", |
|||
}, { |
|||
content: "click commandes", |
|||
trigger: "", |
|||
}, { |
|||
content: "sélectionner dernière commande", |
|||
trigger: "", |
|||
}, { |
|||
content: "click reprint", |
|||
trigger: "", |
|||
}]; |
|||
|
|||
Tour.register('pos_container', { test: true, url: '/pos/web' }, steps); |
|||
}); |
@ -0,0 +1,442 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<templates id="template" xml:space="preserve"> |
|||
|
|||
<t t-name="ContainerButton"> |
|||
<button class="control-button main o_container_button"> |
|||
<i class="fa fa-beer" role="img" aria-label="Container" title="Container"/> |
|||
Container |
|||
</button> |
|||
</t> |
|||
|
|||
<t t-name="TareButton"> |
|||
<button class="control-button second mode-button o_tare_button" data-mode="tare"> |
|||
Tare |
|||
</button> |
|||
</t> |
|||
|
|||
<t t-name="ContainerListScreenWidget"> |
|||
<div class="containerlist-screen screen"> |
|||
<div class="screen-content"> |
|||
<section class="top-content"> |
|||
<span class="button back"> |
|||
<i class="fa fa-angle-double-left"/> |
|||
Cancel |
|||
</span> |
|||
<span class="searchbox"> |
|||
<input placeholder="Search Containers"/> |
|||
<span class="search-clear"/> |
|||
</span> |
|||
<span class="searchbox"/> |
|||
<span class="button delete-container oe_hidden" role="img" aria-label="Delete container" title="Delete container"> |
|||
<i class="fa fa-beer"/> |
|||
<i class="fa fa-trash"/> |
|||
</span> |
|||
<span class="button next oe_hidden highlight"> |
|||
Select a container |
|||
<i class="fa fa-angle-double-right"/> |
|||
</span> |
|||
</section> |
|||
<section class="full-content"> |
|||
<div class="window"> |
|||
<section class="subwindow"> |
|||
<div class="subwindow-container"> |
|||
<div class="subwindow-container-fix touch-scrollable scrollable-y"> |
|||
<table class="container-list"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<th>Barcode</th> |
|||
<th>Weight</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody class="container-list-contents"> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
|
|||
<t t-name="ContainerLine"> |
|||
<tr class="container-line" t-att-data-id="container.barcode"> |
|||
<td> |
|||
<t t-esc="container.name"/> |
|||
</td> |
|||
<td> |
|||
<t t-esc="container.barcode"/> |
|||
</td> |
|||
<td> |
|||
<t t-esc="container.weight"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
|
|||
<t t-extend='Orderline'> |
|||
<t t-jquery='.info-list:last-child' t-operation='prepend'> |
|||
<t t-if="line.get_container()"> |
|||
<ul class="info-list"> |
|||
<ul class="info-list"> |
|||
<i><t t-if="line.get_gross_weight()"> |
|||
<li class="info"> |
|||
Gross : <t t-esc="line.get_gross_weight()"/> kg |
|||
</li> |
|||
</t> |
|||
<li class="info"> |
|||
<i class='fa fa-beer'/> : |
|||
<t t-esc="line.get_container().weight"/> kg |
|||
- |
|||
<t t-esc="line.get_container().name"/> |
|||
</li></i> |
|||
</ul> |
|||
</ul> |
|||
</t> |
|||
<t t-if="line.get_tare()"> |
|||
<ul class="info-list"> |
|||
<ul class="info-list"> |
|||
<i><t t-if="line.get_gross_weight()"> |
|||
<li class="info"> |
|||
Gross : <t t-esc="line.get_gross_weight()"/> kg |
|||
</li> |
|||
</t> |
|||
<li class="info"> |
|||
<i class='fa fa-beer'/> : |
|||
<t t-esc="line.get_tare()"/> kg |
|||
- Manual tare |
|||
</li></i> |
|||
</ul> |
|||
</ul> |
|||
</t> |
|||
<div class="pos-right-align"> |
|||
<t t-esc="line.get_tare_mode()"/> |
|||
</div> |
|||
</t> |
|||
</t> |
|||
|
|||
<t t-name="ContainerScaleScreenWidget"> |
|||
<div class="scale-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 class='product-name'>Add a container</h1> |
|||
</div> |
|||
<div class="centered-content"> |
|||
<div class='weight js-weight'> |
|||
<t t-esc="widget.get_product_weight_string()" /> |
|||
</div> |
|||
<div class='container-name'> |
|||
<span class='label'>Container name: </span><br/> |
|||
<input class='detail container-name' name='container_name' placeholder="Container"></input> |
|||
</div> |
|||
<div class='add-container'> |
|||
Save |
|||
<i class='fa fa-angle-double-right'></i> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
|
|||
<t t-extend="PosTicket"> |
|||
<t t-jquery='.receipt-orderlines' t-operation='inner'> |
|||
<table> |
|||
<colgroup> |
|||
<col width='50%' /> |
|||
<col width='25%' /> |
|||
<col width='25%' /> |
|||
</colgroup> |
|||
<tr t-foreach="orderlines" t-as="orderline"> |
|||
<t t-if="orderline.get_tare_mode() == undefined |
|||
and orderline.product.barcode != 'CONTAINER'"> |
|||
<td> |
|||
<t t-esc="orderline.get_product().display_name"/> |
|||
<t t-if="orderline.get_discount() > 0"> |
|||
<div class="pos-disc-font"> |
|||
With a <t t-esc="orderline.get_discount()"/>% discount |
|||
</div> |
|||
</t> |
|||
</td> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="orderline.get_quantity_str_with_unit()"/> |
|||
</td> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="widget.format_currency(orderline.get_display_price())"/> |
|||
</td> |
|||
</t> |
|||
</tr> |
|||
</table> |
|||
<t t-if="order.has_tare_line('AUTO')"> |
|||
<table> |
|||
<colgroup> |
|||
<col width='20%' /> |
|||
<col width='60%' /> |
|||
<col width='20%' /> |
|||
</colgroup> |
|||
<td></td> |
|||
<td> |
|||
<center>------------------------</center> |
|||
<center>Automatic Weighing</center> |
|||
<center>------------------------</center> |
|||
</td> |
|||
</table> |
|||
</t> |
|||
<table> |
|||
<colgroup> |
|||
<col width='50%' /> |
|||
<col width='25%' /> |
|||
<col width='25%' /> |
|||
</colgroup> |
|||
<tr t-foreach="orderlines" t-as="orderline"> |
|||
<t t-if="orderline.get_tare_mode() == 'AUTO'"> |
|||
<tr> |
|||
<td> |
|||
<t t-esc="orderline.get_product().display_name"/> |
|||
<t t-if="orderline.get_discount() > 0"> |
|||
<div class="pos-disc-font"> |
|||
With a <t t-esc="orderline.get_discount()"/>% discount |
|||
</div> |
|||
</t> |
|||
</td> |
|||
<td/> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="widget.format_currency(orderline.get_display_price())"/> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td> |
|||
<div style="margin-left:5%"> |
|||
<t t-esc="orderline.get_quantity_str_with_unit()"/> |
|||
x |
|||
<t t-esc="widget.format_currency(orderline.get_unit_display_price())"/>/<t t-esc="orderline.get_unit().name"/> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
<t t-if="orderline.get_container()"> |
|||
<tr> |
|||
<td> |
|||
<div style="margin-left:5%"> |
|||
Tare : <t t-esc="orderline.get_container().weight"/> |
|||
<t t-esc="orderline.get_unit().name"/> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
<t t-if="orderline.get_tare()"> |
|||
<tr> |
|||
<td> |
|||
<div style="margin-left:5%"> |
|||
Tare : <t t-esc="orderline.get_tare()"/> |
|||
<t t-esc="orderline.get_unit().name"/> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</t> |
|||
</tr> |
|||
</table> |
|||
<t t-if="order.has_tare_line('MAN')"> |
|||
<table> |
|||
<colgroup> |
|||
<col width='20%' /> |
|||
<col width='60%' /> |
|||
<col width='20%' /> |
|||
</colgroup> |
|||
<td></td> |
|||
<td> |
|||
<center>------------------------</center> |
|||
<center>Manual Input</center> |
|||
<center>------------------------</center> |
|||
</td> |
|||
</table> |
|||
</t> |
|||
<table> |
|||
<colgroup> |
|||
<col width='50%' /> |
|||
<col width='25%' /> |
|||
<col width='25%' /> |
|||
</colgroup> |
|||
<tr t-foreach="orderlines" t-as="orderline"> |
|||
<t t-if="orderline.get_tare_mode() == 'MAN'"> |
|||
<tr> |
|||
<td> |
|||
<t t-esc="orderline.get_product().display_name"/> |
|||
<t t-if="orderline.get_discount() > 0"> |
|||
<div class="pos-disc-font"> |
|||
With a <t t-esc="orderline.get_discount()"/>% discount |
|||
</div> |
|||
</t> |
|||
</td> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="orderline.get_quantity_str_with_unit()"/> |
|||
</td> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="widget.format_currency(orderline.get_display_price())"/> |
|||
</td> |
|||
</tr> |
|||
<t t-if="orderline.get_tare()"> |
|||
<tr> |
|||
<td> |
|||
<div style="margin-left:5%"> |
|||
Tare : <t t-esc="orderline.tare"/> |
|||
<t t-esc="orderline.get_unit().name"/> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</t> |
|||
</tr> |
|||
</table> |
|||
</t> |
|||
</t> |
|||
|
|||
<t t-extend="XmlReceipt"> |
|||
<t t-jquery='.orderlines' t-operation='inner'> |
|||
<t t-foreach='receipt.orderlines' t-as='line'> |
|||
<t t-if="line.tare_mode == undefined and line.product_barcode != 'CONTAINER'"> |
|||
<t t-set='simple' t-value='line.discount === 0 and line.unit_name === "Unit(s)" and line.quantity === 1' /> |
|||
<t t-if='simple'> |
|||
<line> |
|||
<left><t t-esc='line.product_name_wrapped[0]' /></left> |
|||
<right><value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value></right> |
|||
</line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
</t> |
|||
<t t-if='!simple'> |
|||
<line><left><t t-esc='line.product_name_wrapped[0]' /></left></line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
<t t-if='line.discount !== 0'> |
|||
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line> |
|||
</t> |
|||
<line indent='1'> |
|||
<left> |
|||
<value t-att-value-decimals='pos.dp["Product Unit of Measure"]' value-autoint='on'> |
|||
<t t-esc='line.quantity' /> |
|||
</value> |
|||
<t t-if='line.unit_name !== "Unit(s)"'> |
|||
<t t-esc='line.unit_name' /> |
|||
</t> |
|||
x |
|||
<value t-att-value-decimals='pos.dp["Product Price"]'> |
|||
<t t-esc='line.price' /> |
|||
</value> |
|||
</left> |
|||
<right> |
|||
<value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value> |
|||
</right> |
|||
</line> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
<div>------------------</div> |
|||
<t t-if="receipt.has_tare_mode.auto"> |
|||
Automatic Weighing |
|||
<div>------------------</div> |
|||
<t t-foreach='receipt.orderlines' t-as='line'> |
|||
<t t-if="line.tare_mode == 'AUTO'"> |
|||
<t t-set='simple' t-value='line.discount === 0 and line.unit_name === "Unit(s)" and line.quantity === 1' /> |
|||
<t t-if='simple'> |
|||
<line> |
|||
<left><t t-esc='line.product_name_wrapped[0]' /></left> |
|||
<right><value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value></right> |
|||
</line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
</t> |
|||
<t t-if='!simple'> |
|||
<line><left><t t-esc='line.product_name_wrapped[0]' /></left></line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
<t t-if='line.discount !== 0'> |
|||
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line> |
|||
</t> |
|||
<line indent='1'> |
|||
<left> |
|||
<value t-att-value-decimals='pos.dp["Product Unit of Measure"]' value-autoint='on'> |
|||
<t t-esc='line.quantity' /> |
|||
</value> |
|||
<t t-if='line.unit_name !== "Unit(s)"'> |
|||
<t t-esc='line.unit_name' /> |
|||
</t> |
|||
x |
|||
<value t-att-value-decimals='pos.dp["Product Price"]'> |
|||
<t t-esc='line.price' /> |
|||
</value> |
|||
</left> |
|||
<right> |
|||
<value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value> |
|||
</right> |
|||
</line> |
|||
<t t-if='line.container'> |
|||
<line indent='1'> |
|||
<left> |
|||
Tare : <t t-esc='line.container.weight' /><t t-esc='line.unit_name' /> |
|||
</left> |
|||
</line> |
|||
</t> |
|||
<t t-if='line.tare'> |
|||
<line indent='1'> |
|||
<left> |
|||
Tare : <t t-esc='line.tare' /><t t-esc='line.unit_name' /> |
|||
</left> |
|||
</line> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
<div>------------------</div> |
|||
</t> |
|||
<t t-if="receipt.has_tare_mode.manual"> |
|||
Manual Input |
|||
<div>------------------</div> |
|||
<t t-foreach='receipt.orderlines' t-as='line'> |
|||
<t t-if="line.tare_mode == 'MAN'"> |
|||
<t t-set='simple' t-value='line.discount === 0 and line.unit_name === "Unit(s)" and line.quantity === 1' /> |
|||
<t t-if='simple'> |
|||
<line> |
|||
<left><t t-esc='line.product_name_wrapped[0]' /></left> |
|||
<right><value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value></right> |
|||
</line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
</t> |
|||
<t t-if='!simple'> |
|||
<line><left><t t-esc='line.product_name_wrapped[0]' /></left></line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
<t t-if='line.discount !== 0'> |
|||
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line> |
|||
</t> |
|||
<line indent='1'> |
|||
<left> |
|||
<value t-att-value-decimals='pos.dp["Product Unit of Measure"]' value-autoint='on'> |
|||
<t t-esc='line.quantity' /> |
|||
</value> |
|||
<t t-if='line.unit_name !== "Unit(s)"'> |
|||
<t t-esc='line.unit_name' /> |
|||
</t> |
|||
x |
|||
<value t-att-value-decimals='pos.dp["Product Price"]'> |
|||
<t t-esc='line.price' /> |
|||
</value> |
|||
</left> |
|||
<right> |
|||
<value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value> |
|||
</right> |
|||
</line> |
|||
<t t-if='line.tare'> |
|||
<line indent='1'> |
|||
<left> |
|||
Tare : <t t-esc='line.tare' /><t t-esc='line.unit_name' /> |
|||
</left> |
|||
</line> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
|
|||
</templates> |
@ -0,0 +1,17 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
Copyright 2019 Coop IT Easy SCRLfs |
|||
Robin Keunen <robin@coopiteasy.be> |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
--> |
|||
|
|||
<odoo> |
|||
<template id="assets_frontend" inherit_id="point_of_sale.assets"> |
|||
<xpath expr="." position="inside"> |
|||
<link href="/pos_container/static/src/css/container.css" rel="stylesheet"/> |
|||
<script type="text/javascript" src="/pos_container/static/src/js/models_and_db.js"/> |
|||
<script type="text/javascript" src="/pos_container/static/src/js/container.js"/> |
|||
<script type="text/javascript" src="/pos_container/static/src/js/tests.js"/> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |
@ -0,0 +1 @@ |
|||
from . import test_pos_container |
@ -0,0 +1,14 @@ |
|||
import odoo.tests |
|||
|
|||
|
|||
@odoo.tests.tagged('pos_install', '-at-install') |
|||
class TestUi(odoo.tests.HttpCase): |
|||
|
|||
def test_01_pos_container_tour(self): |
|||
self.phantom_js( |
|||
"/web", |
|||
"odoo.__DEBUG__.services['web_tour.tour']" + |
|||
".run('pos_container_tour')", |
|||
"odoo.__DEBUG__.services['web_tour.tour']" + |
|||
".tours.pos_container_tour.ready", |
|||
login="admin") |
@ -0,0 +1,43 @@ |
|||
<odoo> |
|||
<record id="pos_container_tree_view" model="ir.ui.view"> |
|||
<field name="name">pos.container.tree</field> |
|||
<field name="model">pos.container</field> |
|||
<field name="arch" type="xml"> |
|||
<tree> |
|||
<field name="name"/> |
|||
<field name="barcode"/> |
|||
<field name="weight"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="pos_container_form_view" model="ir.ui.view"> |
|||
<field name="name">pos.container.form</field> |
|||
<field name="model">pos.container</field> |
|||
<field name="arch" type="xml"> |
|||
<form> |
|||
<sheet> |
|||
<group> |
|||
<field name="name"/> |
|||
<field name="barcode"/> |
|||
<field name="weight"/> |
|||
</group> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="pos_container_action_window" model="ir.actions.act_window" > |
|||
<field name="name">Containers</field> |
|||
<field name="res_model">pos.container</field> |
|||
<field name="view_mode">tree,form</field> |
|||
</record> |
|||
|
|||
<menuitem id="pos_container_menu" |
|||
name="Containers" |
|||
parent="point_of_sale.pos_config_menu_catalog" |
|||
sequence="13" |
|||
action="pos_container_action_window" |
|||
groups="point_of_sale.group_pos_manager,point_of_sale.group_pos_user" |
|||
/> |
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue