Browse Source

[MIG][10.0] pos_picking_load.

[PORT] Apply new 10.0 syntax ;
[REF] Apply recent OCA convention (new readme, etc.) ;
[PORT] make the module depend on 'sale' module
[REF] remove obsolete file
[IMP] point of sale UI
[ADD] prepare function for picking_line
pull/365/head
Sylvain LE GAL 5 years ago
parent
commit
a82c60561f
  1. 176
      pos_picking_load/README.rst
  2. 2
      pos_picking_load/__init__.py
  3. 11
      pos_picking_load/__manifest__.py
  4. 12
      pos_picking_load/demo/product_template.xml
  5. 12
      pos_picking_load/demo/sale_order.xml
  6. 181
      pos_picking_load/i18n/fr.po
  7. 2
      pos_picking_load/models/__init__.py
  8. 4
      pos_picking_load/models/pos_config.py
  9. 4
      pos_picking_load/models/pos_order.py
  10. 4
      pos_picking_load/models/sale_order.py
  11. 54
      pos_picking_load/models/stock_picking.py
  12. 4
      pos_picking_load/models/stock_picking_type.py
  13. 41
      pos_picking_load/readme/CONFIGURE.rst
  14. 2
      pos_picking_load/readme/CONTRIBUTORS.rst
  15. 16
      pos_picking_load/readme/DESCRIPTION.rst
  16. 8
      pos_picking_load/readme/ROADMAP.rst
  17. 69
      pos_picking_load/readme/USAGE.rst
  18. 562
      pos_picking_load/static/description/index.html
  19. BIN
      pos_picking_load/static/description/load_picking_01.png
  20. BIN
      pos_picking_load/static/description/load_picking_01_load_button.png
  21. BIN
      pos_picking_load/static/description/load_picking_02.png
  22. BIN
      pos_picking_load/static/description/load_picking_02_picking_list.png
  23. BIN
      pos_picking_load/static/description/load_picking_03.png
  24. BIN
      pos_picking_load/static/description/load_picking_03_confirm.png
  25. BIN
      pos_picking_load/static/description/load_picking_04.png
  26. BIN
      pos_picking_load/static/description/load_picking_04_pos_order.png
  27. BIN
      pos_picking_load/static/description/load_picking_05.png
  28. BIN
      pos_picking_load/static/description/load_picking_06.png
  29. BIN
      pos_picking_load/static/description/load_picking_sale_order.png
  30. BIN
      pos_picking_load/static/description/load_picking_stock_picking.png
  31. BIN
      pos_picking_load/static/description/load_picking_warning_partner.png
  32. BIN
      pos_picking_load/static/description/load_picking_warning_picking_still_loaded.png
  33. BIN
      pos_picking_load/static/description/load_picking_warning_product.png
  34. BIN
      pos_picking_load/static/description/pos_config_form.png
  35. 30
      pos_picking_load/static/src/css/pos_picking_load.css
  36. 77
      pos_picking_load/static/src/js/model.js
  37. 329
      pos_picking_load/static/src/js/pos_picking_load.js
  38. 289
      pos_picking_load/static/src/js/widget.js
  39. 23
      pos_picking_load/static/src/xml/pos_picking_load.xml
  40. 17
      pos_picking_load/tests/test_pos_picking_load.py
  41. 32
      pos_picking_load/views/pos_picking_load.xml
  42. 5
      pos_picking_load/views/view_pos_config.xml
  43. 9
      pos_picking_load/views/view_sale_order.xml
  44. 11
      pos_picking_load/views/view_stock_picking.xml
  45. 5
      pos_picking_load/views/view_stock_picking_type.xml

176
pos_picking_load/README.rst

@ -1,30 +1,45 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
================
POS Picking Load
================
============================
Point Of Sale - Picking Load
============================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-legalsylvain%2Fpos-lightgray.png?logo=github
:target: https://github.com/legalsylvain/pos/tree/10.0-mig-pos_picking_load/pos_picking_load
:alt: legalsylvain/pos
|badge1| |badge2| |badge3|
This module extends the functionality of point of sale to allow you to
load your pickings in the Point of Sale, in order to add / remove products
and so create a PoS Order and mark it as paid.
Detailled Use Case
==================
**Detailled Use Case**
This module is usefull for the following use case
* You have many Sale Orders that have generated pickings. Typically if you have
connected your Odoo instance to an online store like Shop Invader,
Prestashop, Magento, or if you use light Odoo shop (```website_sale```
Prestashop, Magento, or if you use light Odoo shop (``website_sale``
module).
* Once the order validated, you prepare your pickings
* The customer come in your shop to recover his order
* the customer add (or remove) some products
* the customer pay his order, based on the real delivered products list.
* the customer pay his order, based on the real delivered products list
**Table of contents**
.. contents::
:local:
Configuration
=============
@ -35,7 +50,7 @@ To configure this module, you need to:
#. Select the picking type(s) you want to see in the point of sale
#. Check the box 'Available in Point of Sale'
.. figure:: static/description/stock_picking_type_form.png
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/stock_picking_type_form.png
:width: 800 px
Note: This box is NOT enabled by default except in demo data for the type
@ -43,13 +58,34 @@ Note: This box is NOT enabled by default except in demo data for the type
#. Go to Point of Sale / Configuration / Point of Sales
#. Select the Point(s) of Sales witch those you want to enable the feature
#. Check the box 'Load Pickings'.
#. Check the box 'Load Pickings'
#. Set the max quantity of pickings you want to load
.. figure:: static/description/pos_config_form.png
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/pos_config_form.png
:width: 800 px
Note: This box is enabled by default
**Technical Notes**
* By default, the Point of Sale will display only the pickings if the state is
in 'Waiting Availability', 'Partially Available' or 'Ready to Transfer'.
You can change this filter by overloading the ``_prepare_filter_for_pos``
function of the model ``stock.picking``.
* By default, the search of pickings will be done on the fields ``name``,
``origin`` and ``partner_id`` of the picking.
You can change this feature by overloading the
``_prepare_filter_query_for_pos`` function of the model ``stock.picking``.
* By default, when the PoS order is confirmed, the original picking is
cancelled and the sale order is set to the state 'Done'.
You can change this behaviour by overloading
``_handle_orders_with_original_picking`` function of the model ``pos.order``.
Usage
=====
@ -58,36 +94,42 @@ To use this module, you need to:
* Launch the point of sale
* On a new order (without lines), click on the 'Load Picking' button.
.. figure:: static/description/load_picking_01.png
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_01_load_button.png
:width: 800 px
* Point of sale will load available pickings. (About displayed pickings, see
'Technical Notes' below).
'Technical Notes' section).
.. figure:: static/description/load_picking_02.png
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_02_picking_list.png
:width: 800 px
* Click on a picking will display the content of the moves (as PoS Order Lines)
* Click on a picking will check if the picking is loadable and if yes, will
display a 'Select' button. (See 'Possible Warnings' Section)
.. figure:: static/description/load_picking_03.png
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_03_confirm.png
:width: 800 px
* Confirm the selection, by clicking on 'Select' button. It will display
the content of the moves (as PoS Order Lines)
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_04_pos_order.png
:width: 800 px
The price and the discount will be the sale price and the discount set in
the according Sale Order Line, if it was found. Otherwise, discount will be to
0, and unit price will be the unit price of the product when it has been
the according Sale Order Line, if it was found. Otherwise, discount will be
set to 0, and unit price will be the unit price of the product when it has been
loaded in the Point of Sale.
**Related Sale Order:**
.. figure:: static/description/load_picking_04.png
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_sale_order.png
:width: 800 px
**Related Picking:**
.. figure:: static/description/load_picking_05.png
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_stock_picking.png
:width: 800 px
* Confirm the selection, by clicking on 'Select' button.
* Finally, you can add / remove products or change quantity and collect the
payment.
@ -96,83 +138,77 @@ When, the order is marked as paid, the original picking will be cancelled,
because Point Of Sale generates a new picking related to the real delivered
products and the original Sale Order will pass to the state 'Done'. (Delivery
exception is ignored).
(See 'Technical Notes' below).
(See 'Technical Notes' section).
**Possible Warnings**
Some warning messages can appear:
* if some products are not available in the Point of Sale.
* if some products are not available in the Point of Sale
.. figure:: static/description/load_picking_06.png
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_product.png
:width: 800 px
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/184/8.0
* if the partner is not available in the Point of Sale
Technical Notes
===============
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_partner.png
:width: 800 px
* By default, the following filters are applied on the pickings displayed:
* 'State' should be 'Waiting Availability', 'Partially Available' or
'Ready to Transfer'.
* 'Invoice Control' should be 'To be invoiced'.
You can change this filter by overloading the ```_prepare_filter_for_pos```
function of the model ```stock.picking```
* if the picking has been still loaded in another PoS order
* By default, original Picking is cancelled and the Sale order is set to the
state 'Done'. You can change this behaviour by overloading
```_handle_orders_with_original_picking``` function of the model
```pos.order```.
.. figure:: https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_picking_still_loaded.png
:width: 800 px
Known issues / Roadmap
======================
* This module will try to get original unit price from the sale order and not
use the Current unit price of the product. (The price at which you pledged
to sell the product).
use the Current unit price of the product.
(The price at which you pledged to sell the product).
Some VAT troubles will occure if a product is set with VAT marked as
'VAT included' and if in the sale order line, there are some VAT marked as
'VAT excluded'. **The VAT settings should be consistent**.
'VAT excluded' for exemple.
**The VAT settings should be consistent.**
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 smash it by providing detailed and welcomed feedback.
Bugs are tracked on `GitHub Issues <https://github.com/legalsylvain/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/legalsylvain/pos/issues/new?body=module:%20pos_picking_load%0Aversion:%2010.0-mig-pos_picking_load%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
=======
Images
------
Authors
~~~~~~~
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
* GRAP
Contributors
------------
~~~~~~~~~~~~
* Sylvain LE GAL (https://twitter.com/legalsylvain)
* Stefan Rijnhart <stefan@opener.am>
* Sylvain Le Gal (https://twitter.com/legalsylvain)
Maintainers
~~~~~~~~~~~
Funders
-------
The development of this module has been financially supported by:
* GRAP, Groupement Régional Alimentaire de Proximité (http://www.grap.coop)
.. |maintainer-legalsylvain| image:: https://github.com/legalsylvain.png?size=40px
:target: https://github.com/legalsylvain
:alt: legalsylvain
Maintainer
----------
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
|maintainer-legalsylvain|
This module is maintained by the OCA.
This module is part of the `legalsylvain/pos <https://github.com/legalsylvain/pos/tree/10.0-mig-pos_picking_load/pos_picking_load>`_ project on GitHub.
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.
To contribute to this module, please visit https://odoo-community.org.
You are welcome to contribute.

2
pos_picking_load/__init__.py

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

11
pos_picking_load/__openerp__.py → pos_picking_load/__manifest__.py

@ -1,15 +1,19 @@
# -*- coding: utf-8 -*-
# coding: utf-8
# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'POS Picking Load',
'version': '8.0.1.2.0',
'name': 'Point Of Sale - Picking Load',
'summary': "Load and confirm stock pickings via Point Of Sale",
'version': '10.0.1.0.0',
'author': 'GRAP,Odoo Community Association (OCA)',
'category': 'Point Of Sale',
'license': 'AGPL-3',
'maintainers': ['legalsylvain'],
'development_status': 'Beta',
'depends': [
'sale',
'point_of_sale',
],
'website': 'https://odoo-community.org/',
@ -29,4 +33,5 @@
'qweb': [
'static/src/xml/pos_picking_load.xml',
],
'installable': True,
}

12
pos_picking_load/demo/product_template.xml

@ -1,5 +1,7 @@
<openerp><data>
<!-- for demo pickings created by this module -->
<record id="product.product_product_4_product_template" model="product.template">
<field name="available_in_pos" eval="True"/>
</record>
@ -16,4 +18,14 @@
<field name="available_in_pos" eval="True"/>
</record>
<!-- For demo pickings created before the installation of this module -->
<record id="product.product_product_27_product_template" model="product.template">
<field name="available_in_pos" eval="True"/>
</record>
<record id="product.product_product_6_product_template" model="product.template">
<field name="available_in_pos" eval="True"/>
</record>
</data></openerp>

12
pos_picking_load/demo/sale_order.xml

@ -3,13 +3,15 @@
<!-- Sale Order 1-->
<record id="sale_order_1" model="sale.order">
<field name="partner_id" ref="base.res_partner_2"/>
<field name="order_policy">picking</field>
<!-- <field name="order_policy">picking</field> -->
</record>
<record id="sale_order_line_11" model="sale.order.line">
<field name="order_id" ref="sale_order_1"/>
<field name="product_id" ref="product.product_product_7"/>
<field name="product_uom_qty">5</field>
<field name="price_unit">100</field>
<field name="discount">20</field>
</record>
<record id="sale_order_line_12" model="sale.order.line">
@ -18,12 +20,12 @@
<field name="product_uom_qty">3</field>
</record>
<workflow action="order_confirm" model="sale.order" ref="sale_order_1"/>
<function model="sale.order" name="action_confirm" eval="[[ref('sale_order_1')]]"/>
<!-- Sale Order 2-->
<record id="sale_order_2" model="sale.order">
<field name="partner_id" ref="base.res_partner_13"/>
<field name="order_policy">picking</field>
<field name="partner_id" ref="base.res_partner_18"/>
<!-- <field name="order_policy">picking</field> -->
</record>
<record id="sale_order_line_21" model="sale.order.line">
@ -46,6 +48,6 @@
<field name="price_unit">555</field>
</record>
<workflow action="order_confirm" model="sale.order" ref="sale_order_2"/>
<function model="sale.order" name="action_confirm" eval="[[ref('sale_order_2')]]"/>
</data></openerp>

181
pos_picking_load/i18n/fr.po

@ -1,39 +1,35 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_picking_load
# * pos_picking_load
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-13 03:00+0000\n"
"PO-Revision-Date: 2017-07-13 03:00+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>, 2017\n"
"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n"
"Language: fr\n"
"POT-Creation-Date: 2019-01-06 18:33+0000\n"
"PO-Revision-Date: 2019-01-06 18: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: nplurals=2; plural=(n > 1);\n"
"Plural-Forms: \n"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:70
#: code:addons/pos_picking_load/static/src/js/widget.js:242
#, python-format
msgid "--------------------------------"
msgstr "--------------------------------"
msgid "A picking is still loaded. You can not load another picking. Please create a new order."
msgstr "Un BL est déjà chargé. Vous ne pouvez pas en charger un autre. Veuillez créer une nouvelle commande."
#. module: pos_picking_load
#: field:stock.picking.type,available_in_pos:0
#: model:ir.model.fields,field_description:pos_picking_load.field_stock_picking_type_available_in_pos
msgid "Available in Point Of Sale"
msgstr "Disponible dans le point de vente"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/pos_picking_load.js:264
#: code:addons/pos_picking_load/static/src/js/pos_picking_load.js:290
#: code:addons/pos_picking_load/static/src/js/widget.js:211
#, python-format
msgid "Can not execute this action because the POS is currently offline"
msgstr ""
@ -42,113 +38,129 @@ msgstr ""
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:22
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:27
#, python-format
msgid "Cancel"
msgstr "Annuler"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/pos_picking_load.js:263
#: code:addons/pos_picking_load/static/src/js/pos_picking_load.js:289
#: code:addons/pos_picking_load/static/src/js/widget.js:210
#, python-format
msgid "Connection error"
msgstr "Erreur de connexion"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:50
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:53
#, python-format
msgid "Customer"
msgstr "Client"
#. module: pos_picking_load
#: field:sale.order,final_pos_order_id:0
#: field:stock.picking,final_pos_order_id:0
#: model:ir.model.fields,field_description:pos_picking_load.field_sale_order_final_pos_order_id
#: model:ir.model.fields,field_description:pos_picking_load.field_stock_picking_final_pos_order_id
msgid "Final PoS Order"
msgstr "Commande Finale"
#. module: pos_picking_load
#: help:stock.picking.type,available_in_pos:0
msgid ""
"If checked, associated pickings will be available in the point of sale, to "
"be changed and paid in it"
msgstr ""
"Si la case est cochée, toutes les bons de livraisons associés seront "
"disponible dans le point de vente, pour ere changé et payé au sein de celui-"
"ci"
#: model:ir.model.fields,help:pos_picking_load.field_stock_picking_type_available_in_pos
msgid "If checked, associated pickings will be available in the point of sale, to be changed and paid in it"
msgstr "Si la case est cochée, toutes les bons de livraisons associés seront disponible dans le point de vente, pour ere changé et payé au sein de celui-ci"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:5
#: code:addons/pos_picking_load/static/src/js/widget.js:253
#, python-format
msgid "Load Picking"
msgstr "Charger un bon de livraison"
#. module: pos_picking_load
#: field:pos.config,iface_load_picking:0
#: model:ir.model.fields,field_description:pos_picking_load.field_pos_config_iface_load_picking
msgid "Load Pickings"
msgstr "Charger des bons de livraison"
#. module: pos_picking_load
#: model:ir.model.fields,field_description:pos_picking_load.field_pos_config_iface_load_picking_max_qty
msgid "Max Picking Quantity To Load"
msgstr "Quantité maximum de BL à charger"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:49
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:52
#, python-format
msgid "Name"
msgstr "Nom"
#. module: pos_picking_load
#: field:pos.order,origin_picking_id:0
#: model:ir.model.fields,field_description:pos_picking_load.field_pos_order_origin_picking_id
msgid "Origin Picking"
msgstr "Livraison d'origine"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:71
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:79
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:74
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:82
#, python-format
msgid "Origin Picking:"
msgstr "BL d'origine:"
#. module: pos_picking_load
#: model:ir.model,name:pos_picking_load.model_stock_picking
msgid "Picking List"
msgstr "Bon de livraison"
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/widget.js:241
#, python-format
msgid "Pending Picking"
msgstr "BL en cours
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/widget.js:131
#: code:addons/pos_picking_load/static/src/js/widget.js:147
#, python-format
msgid "Picking Still Loaded"
msgstr "Bon de livraison déjà chargé"
#. module: pos_picking_load
#: model:ir.model,name:pos_picking_load.model_pos_order
msgid "Point of Sale"
msgstr "Point de Vente"
msgid "Point of Sale Orders"
msgstr "Commandes du point de vente"
#. module: pos_picking_load
#: model:ir.model,name:pos_picking_load.model_sale_order
msgid "Sales Order"
msgstr "Commande de ventes"
msgstr "Bon de commande"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:51
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:54
#, python-format
msgid "Scheduled Date"
msgstr "Date prévue"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:30
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:35
#, python-format
msgid "Search Picking"
msgstr "Chercher un bon de livraison"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:26
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:31
#, python-format
msgid "Select"
msgstr "Sélectionner"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:52
#: code:addons/pos_picking_load/static/src/js/widget.js:203
#, python-format
msgid "Server Error"
msgstr "Erreur serveur"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/xml/pos_picking_load.xml:55
#, python-format
msgid "Source Document"
msgstr "Document d'origine"
@ -159,33 +171,37 @@ msgid "The picking type determines the picking view"
msgstr "Le type de préparation détermine la vue de préparation"
#. module: pos_picking_load
#: help:sale.order,final_pos_order_id:0
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/widget.js:204
#, python-format
msgid "The server encountered an error while receiving your order."
msgstr "Le serveur a rencontré une erreur lors de la réception de votre commande."
#. module: pos_picking_load
#: model:ir.model.fields,help:pos_picking_load.field_sale_order_final_pos_order_id
msgid "This Sale Order has beend replaced by this PoS Order"
msgstr ""
"Ce bon de commande a été remplacé par cette commande via le point de vente"
msgstr "Ce bon de commande a été remplacé par cette commande via le point de vente"
#. module: pos_picking_load
#: help:stock.picking,final_pos_order_id:0
msgid ""
"This picking has been canceled, because it has been replaced by this PoS "
"Order"
msgstr ""
"Ce bon de livraison a été annulé, car il a été remplacé par cette commande "
"via le point de vente"
#: model:ir.model.fields,help:pos_picking_load.field_stock_picking_final_pos_order_id
msgid "This picking has been canceled, because it has been replaced by this PoS Order"
msgstr "Ce bon de livraison a été annulé, car il a été remplacé par cette commande via le point de vente"
#. module: pos_picking_load
#: model:ir.model,name:pos_picking_load.model_stock_picking
msgid "Transfer"
msgstr "Transfert"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/pos_picking_load.js:228
#: code:addons/pos_picking_load/static/src/js/widget.js:100
#, python-format
msgid ""
"Unable to load some picking lines because the products are not available in "
"the POS cache.\n"
msgid "Unable to load some picking lines because the products are not available in the POS cache.\n"
"\n"
"Please check that lines :\n"
"\n"
" * "
msgstr ""
"Impossible de charger certaines lignes du bon de livraison car certains "
msgstr "Impossible de charger certaines lignes du bon de livraison car certains "
"produits ne sont pas disponibles dans le point de vente.\n"
"\n"
"Merci de vérifier les lignes suivantes :\n"
@ -194,25 +210,50 @@ msgstr ""
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/pos_picking_load.js:239
#: code:addons/pos_picking_load/static/src/js/widget.js:148
#, python-format
msgid ""
"Unable to load this picking because the partner is not known in the Point Of "
"Sale as a customer"
msgstr ""
"Impossible de charger ce bon de livraison car ce partenaire n'est pas "
msgid "Unable to load this picking because it has been loaded in another confirmed PoS Order :\n"
"\n"
""
msgstr "Impossible de charger ce bon de livraison car il a déjà été chargé dans une autre commande confirmée :\n"
"\n"
""
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/widget.js:132
#, python-format
msgid "Unable to load this picking because it has been loaded in another draft PoS Order :\n"
"\n"
""
msgstr "Impossible de charger ce bon de livraison car il a déjà été chargé dans une autre commande en cours :\n"
"\n"
""
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/widget.js:118
#, python-format
msgid "Unable to load this picking because the partner is not known in the Point Of Sale as a customer"
msgstr "Impossible de charger ce bon de livraison car ce partenaire n'est pas "
"reconnu dans le point de vente comme un client"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/pos_picking_load.js:238
#: code:addons/pos_picking_load/static/src/js/widget.js:117
#, python-format
msgid "Unknown Partner"
msgstr "Partenaire inconnu"
#. module: pos_picking_load
#. openerp-web
#: code:addons/pos_picking_load/static/src/js/pos_picking_load.js:227
#: code:addons/pos_picking_load/static/src/js/widget.js:99
#, python-format
msgid "Unknown Products"
msgstr "Produits inconnus"
#. module: pos_picking_load
#: model:ir.model,name:pos_picking_load.model_pos_config
msgid "pos.config"
msgstr "pos.config"

2
pos_picking_load/models/__init__.py

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
# coding: utf-8
from . import sale_order
from . import stock_picking
from . import stock_picking_type

4
pos_picking_load/models/pos_config.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# coding: utf-8
# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
from odoo import models, fields
class PosConfig(models.Model):

4
pos_picking_load/models/pos_order.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# coding: utf-8
# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, api, fields
from odoo import models, api, fields
class PosOrder(models.Model):

4
pos_picking_load/models/sale_order.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# coding: utf-8
# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
from odoo import models, fields
class SaleOrder(models.Model):

54
pos_picking_load/models/stock_picking.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# coding: utf-8
# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, api, fields
from odoo import models, api, fields
class StockPicking(models.Model):
@ -15,7 +15,7 @@ class StockPicking(models.Model):
help="This picking has been canceled, because it has been replaced by"
" this PoS Order")
# Custom Section
# Custom Section - Picking List Part
@api.model
def _prepare_filter_for_pos(self, pos_session_id):
picking_type_obj = self.env['stock.picking.type']
@ -24,7 +24,6 @@ class StockPicking(models.Model):
return [
('picking_type_id', 'in', picking_types.ids),
('state', 'in', ['confirmed', 'partially_available', 'assigned']),
('invoice_state', '=', '2binvoiced'),
]
@api.model
@ -50,28 +49,33 @@ class StockPicking(models.Model):
return self.search_read(
condition, fields, limit=config.iface_load_picking_max_qty)
@api.multi
def load_picking_for_pos(self):
self.ensure_one()
pickinglines = []
for line in self.move_lines.filtered(lambda x: x.state != 'cancel'):
picking_line = {
'name': line.name,
'product_id': line.product_id.id,
'quantity': line.product_uom_qty,
}
sale_order_line =\
line.procurement_id and line.procurement_id.sale_line_id
if sale_order_line:
# Get price and discount of the order if available
picking_line['price_unit'] = sale_order_line.price_unit
picking_line['discount'] = sale_order_line.discount
pickinglines.append(picking_line)
# Custom Section - Load Picking Part
@api.model
def _prepare_line_data_from_stock_move(self, move):
picking_line_data = {
'name': move.name,
'product_id': move.product_id.id,
'quantity': move.product_uom_qty,
}
sale_order_line =\
move.procurement_id and move.procurement_id.sale_line_id
if sale_order_line:
# Get price and discount of the order if available
picking_line_data['price_unit'] = sale_order_line.price_unit
picking_line_data['discount'] = sale_order_line.discount
return picking_line_data
@api.model
def load_picking_for_pos(self, picking_id):
picking = self.browse(picking_id)
picking_lines = []
for move in picking.move_lines.filtered(lambda x: x.state != 'cancel'):
picking_lines.append(self._prepare_line_data_from_stock_move(move))
return {
'id': self.id,
'name': self.name,
'partner_id': self.partner_id.id,
'line_ids': pickinglines,
'id': picking.id,
'name': picking.name,
'partner_id': picking.partner_id.id,
'line_ids': picking_lines,
}
@api.multi

4
pos_picking_load/models/stock_picking_type.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# coding: utf-8
# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
from odoo import models, fields
class StockPickingType(models.Model):

41
pos_picking_load/readme/CONFIGURE.rst

@ -0,0 +1,41 @@
To configure this module, you need to:
#. Go to Warehouse / Configuration / Types of Operation
#. Select the picking type(s) you want to see in the point of sale
#. Check the box 'Available in Point of Sale'
.. figure:: ../static/description/stock_picking_type_form.png
:width: 800 px
Note: This box is NOT enabled by default except in demo data for the type
'Delivery Orders' of the demo company 'YourCompany'.
#. Go to Point of Sale / Configuration / Point of Sales
#. Select the Point(s) of Sales witch those you want to enable the feature
#. Check the box 'Load Pickings'
#. Set the max quantity of pickings you want to load
.. figure:: ../static/description/pos_config_form.png
:width: 800 px
Note: This box is enabled by default
**Technical Notes**
* By default, the Point of Sale will display only the pickings if the state is
in 'Waiting Availability', 'Partially Available' or 'Ready to Transfer'.
You can change this filter by overloading the ``_prepare_filter_for_pos``
function of the model ``stock.picking``.
* By default, the search of pickings will be done on the fields ``name``,
``origin`` and ``partner_id`` of the picking.
You can change this feature by overloading the
``_prepare_filter_query_for_pos`` function of the model ``stock.picking``.
* By default, when the PoS order is confirmed, the original picking is
cancelled and the sale order is set to the state 'Done'.
You can change this behaviour by overloading
``_handle_orders_with_original_picking`` function of the model ``pos.order``.

2
pos_picking_load/readme/CONTRIBUTORS.rst

@ -0,0 +1,2 @@
* Sylvain LE GAL (https://twitter.com/legalsylvain)
* Stefan Rijnhart <stefan@opener.am>

16
pos_picking_load/readme/DESCRIPTION.rst

@ -0,0 +1,16 @@
This module extends the functionality of point of sale to allow you to
load your pickings in the Point of Sale, in order to add / remove products
and so create a PoS Order and mark it as paid.
**Detailled Use Case**
This module is usefull for the following use case
* You have many Sale Orders that have generated pickings. Typically if you have
connected your Odoo instance to an online store like Shop Invader,
Prestashop, Magento, or if you use light Odoo shop (``website_sale``
module).
* Once the order validated, you prepare your pickings
* The customer come in your shop to recover his order
* the customer add (or remove) some products
* the customer pay his order, based on the real delivered products list

8
pos_picking_load/readme/ROADMAP.rst

@ -0,0 +1,8 @@
* This module will try to get original unit price from the sale order and not
use the Current unit price of the product.
(The price at which you pledged to sell the product).
Some VAT troubles will occure if a product is set with VAT marked as
'VAT included' and if in the sale order line, there are some VAT marked as
'VAT excluded' for exemple.
**The VAT settings should be consistent.**

69
pos_picking_load/readme/USAGE.rst

@ -0,0 +1,69 @@
To use this module, you need to:
* Launch the point of sale
* On a new order (without lines), click on the 'Load Picking' button.
.. figure:: ../static/description/load_picking_01_load_button.png
:width: 800 px
* Point of sale will load available pickings. (About displayed pickings, see
'Technical Notes' section).
.. figure:: ../static/description/load_picking_02_picking_list.png
:width: 800 px
* Click on a picking will check if the picking is loadable and if yes, will
display a 'Select' button. (See 'Possible Warnings' Section)
.. figure:: ../static/description/load_picking_03_confirm.png
:width: 800 px
* Confirm the selection, by clicking on 'Select' button. It will display
the content of the moves (as PoS Order Lines)
.. figure:: ../static/description/load_picking_04_pos_order.png
:width: 800 px
The price and the discount will be the sale price and the discount set in
the according Sale Order Line, if it was found. Otherwise, discount will be
set to 0, and unit price will be the unit price of the product when it has been
loaded in the Point of Sale.
**Related Sale Order:**
.. figure:: ../static/description/load_picking_sale_order.png
:width: 800 px
**Related Picking:**
.. figure:: ../static/description/load_picking_stock_picking.png
:width: 800 px
* Finally, you can add / remove products or change quantity and collect the
payment.
When, the order is marked as paid, the original picking will be cancelled,
because Point Of Sale generates a new picking related to the real delivered
products and the original Sale Order will pass to the state 'Done'. (Delivery
exception is ignored).
(See 'Technical Notes' section).
**Possible Warnings**
Some warning messages can appear:
* if some products are not available in the Point of Sale
.. figure:: ../static/description/load_picking_warning_product.png
:width: 800 px
* if the partner is not available in the Point of Sale
.. figure:: ../static/description/load_picking_warning_partner.png
:width: 800 px
* if the picking has been still loaded in another PoS order
.. figure:: ../static/description/load_picking_warning_picking_still_loaded.png
:width: 800 px

562
pos_picking_load/static/description/index.html

@ -0,0 +1,562 @@
<?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>Point Of Sale - Picking Load</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="point-of-sale-picking-load">
<h1 class="title">Point Of Sale - Picking Load</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/legalsylvain/pos/tree/10.0-mig-pos_picking_load/pos_picking_load"><img alt="legalsylvain/pos" src="https://img.shields.io/badge/github-legalsylvain%2Fpos-lightgray.png?logo=github" /></a></p>
<p>This module extends the functionality of point of sale to allow you to
load your pickings in the Point of Sale, in order to add / remove products
and so create a PoS Order and mark it as paid.</p>
<p><strong>Detailled Use Case</strong></p>
<p>This module is usefull for the following use case</p>
<ul class="simple">
<li>You have many Sale Orders that have generated pickings. Typically if you have
connected your Odoo instance to an online store like Shop Invader,
Prestashop, Magento, or if you use light Odoo shop (<tt class="docutils literal">website_sale</tt>
module).</li>
<li>Once the order validated, you prepare your pickings</li>
<li>The customer come in your shop to recover his order</li>
<li>the customer add (or remove) some products</li>
<li>the customer pay his order, based on the real delivered products list</li>
</ul>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="id1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id3">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id4">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id5">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id6">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id7">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id8">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id1">Configuration</a></h1>
<p>To configure this module, you need to:</p>
<ol class="arabic simple">
<li>Go to Warehouse / Configuration / Types of Operation</li>
<li>Select the picking type(s) you want to see in the point of sale</li>
<li>Check the box ‘Available in Point of Sale’</li>
</ol>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/stock_picking_type_form.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/stock_picking_type_form.png" style="width: 800px;" />
</div>
<p>Note: This box is NOT enabled by default except in demo data for the type
‘Delivery Orders’ of the demo company ‘YourCompany’.</p>
<ol class="arabic simple">
<li>Go to Point of Sale / Configuration / Point of Sales</li>
<li>Select the Point(s) of Sales witch those you want to enable the feature</li>
<li>Check the box ‘Load Pickings’</li>
<li>Set the max quantity of pickings you want to load</li>
</ol>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/pos_config_form.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/pos_config_form.png" style="width: 800px;" />
</div>
<p>Note: This box is enabled by default</p>
<p><strong>Technical Notes</strong></p>
<ul class="simple">
<li>By default, the Point of Sale will display only the pickings if the state is
in ‘Waiting Availability’, ‘Partially Available’ or ‘Ready to Transfer’.</li>
</ul>
<p>You can change this filter by overloading the <tt class="docutils literal">_prepare_filter_for_pos</tt>
function of the model <tt class="docutils literal">stock.picking</tt>.</p>
<ul class="simple">
<li>By default, the search of pickings will be done on the fields <tt class="docutils literal">name</tt>,
<tt class="docutils literal">origin</tt> and <tt class="docutils literal">partner_id</tt> of the picking.</li>
</ul>
<p>You can change this feature by overloading the
<tt class="docutils literal">_prepare_filter_query_for_pos</tt> function of the model <tt class="docutils literal">stock.picking</tt>.</p>
<ul class="simple">
<li>By default, when the PoS order is confirmed, the original picking is
cancelled and the sale order is set to the state ‘Done’.</li>
</ul>
<p>You can change this behaviour by overloading
<tt class="docutils literal">_handle_orders_with_original_picking</tt> function of the model <tt class="docutils literal">pos.order</tt>.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id2">Usage</a></h1>
<p>To use this module, you need to:</p>
<ul class="simple">
<li>Launch the point of sale</li>
<li>On a new order (without lines), click on the ‘Load Picking’ button.</li>
</ul>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_01_load_button.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_01_load_button.png" style="width: 800px;" />
</div>
<ul class="simple">
<li>Point of sale will load available pickings. (About displayed pickings, see
‘Technical Notes’ section).</li>
</ul>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_02_picking_list.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_02_picking_list.png" style="width: 800px;" />
</div>
<ul class="simple">
<li>Click on a picking will check if the picking is loadable and if yes, will
display a ‘Select’ button. (See ‘Possible Warnings’ Section)</li>
</ul>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_03_confirm.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_03_confirm.png" style="width: 800px;" />
</div>
<ul class="simple">
<li>Confirm the selection, by clicking on ‘Select’ button. It will display
the content of the moves (as PoS Order Lines)</li>
</ul>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_04_pos_order.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_04_pos_order.png" style="width: 800px;" />
</div>
<p>The price and the discount will be the sale price and the discount set in
the according Sale Order Line, if it was found. Otherwise, discount will be
set to 0, and unit price will be the unit price of the product when it has been
loaded in the Point of Sale.</p>
<p><strong>Related Sale Order:</strong></p>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_sale_order.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_sale_order.png" style="width: 800px;" />
</div>
<p><strong>Related Picking:</strong></p>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_stock_picking.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_stock_picking.png" style="width: 800px;" />
</div>
<ul class="simple">
<li>Finally, you can add / remove products or change quantity and collect the
payment.</li>
</ul>
<p>When, the order is marked as paid, the original picking will be cancelled,
because Point Of Sale generates a new picking related to the real delivered
products and the original Sale Order will pass to the state ‘Done’. (Delivery
exception is ignored).
(See ‘Technical Notes’ section).</p>
<p><strong>Possible Warnings</strong></p>
<p>Some warning messages can appear:</p>
<ul class="simple">
<li>if some products are not available in the Point of Sale</li>
</ul>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_product.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_product.png" style="width: 800px;" />
</div>
<ul class="simple">
<li>if the partner is not available in the Point of Sale</li>
</ul>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_partner.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_partner.png" style="width: 800px;" />
</div>
<ul class="simple">
<li>if the picking has been still loaded in another PoS order</li>
</ul>
<div class="figure">
<img alt="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_picking_still_loaded.png" src="https://raw.githubusercontent.com/legalsylvain/pos/10.0-mig-pos_picking_load/pos_picking_load/static/description/load_picking_warning_picking_still_loaded.png" style="width: 800px;" />
</div>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id3">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>This module will try to get original unit price from the sale order and not
use the Current unit price of the product.
(The price at which you pledged to sell the product).
Some VAT troubles will occure if a product is set with VAT marked as
‘VAT included’ and if in the sale order line, there are some VAT marked as
‘VAT excluded’ for exemple.</li>
</ul>
<p><strong>The VAT settings should be consistent.</strong></p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id4">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/legalsylvain/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/legalsylvain/pos/issues/new?body=module:%20pos_picking_load%0Aversion:%2010.0-mig-pos_picking_load%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id5">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
<ul class="simple">
<li>GRAP</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id7">Contributors</a></h2>
<ul class="simple">
<li>Sylvain LE GAL (<a class="reference external" href="https://twitter.com/legalsylvain">https://twitter.com/legalsylvain</a>)</li>
<li>Stefan Rijnhart &lt;<a class="reference external" href="mailto:stefan&#64;opener.am">stefan&#64;opener.am</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id8">Maintainers</a></h2>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external" href="https://github.com/legalsylvain"><img alt="legalsylvain" src="https://github.com/legalsylvain.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/legalsylvain/pos/tree/10.0-mig-pos_picking_load/pos_picking_load">legalsylvain/pos</a> project on GitHub.</p>
<p>You are welcome to contribute.</p>
</div>
</div>
</div>
</body>
</html>

BIN
pos_picking_load/static/description/load_picking_01.png

Before

Width: 421  |  Height: 205  |  Size: 8.9 KiB

BIN
pos_picking_load/static/description/load_picking_01_load_button.png

After

Width: 428  |  Height: 494  |  Size: 22 KiB

BIN
pos_picking_load/static/description/load_picking_02.png

Before

Width: 855  |  Height: 222  |  Size: 26 KiB

BIN
pos_picking_load/static/description/load_picking_02_picking_list.png

After

Width: 976  |  Height: 285  |  Size: 33 KiB

BIN
pos_picking_load/static/description/load_picking_03.png

Before

Width: 415  |  Height: 306  |  Size: 26 KiB

BIN
pos_picking_load/static/description/load_picking_03_confirm.png

After

Width: 1025  |  Height: 267  |  Size: 35 KiB

BIN
pos_picking_load/static/description/load_picking_04.png

Before

Width: 837  |  Height: 495  |  Size: 43 KiB

BIN
pos_picking_load/static/description/load_picking_04_pos_order.png

After

Width: 430  |  Height: 637  |  Size: 38 KiB

BIN
pos_picking_load/static/description/load_picking_05.png

Before

Width: 839  |  Height: 298  |  Size: 33 KiB

BIN
pos_picking_load/static/description/load_picking_06.png

Before

Width: 511  |  Height: 426  |  Size: 14 KiB

BIN
pos_picking_load/static/description/load_picking_sale_order.png

After

Width: 844  |  Height: 384  |  Size: 35 KiB

BIN
pos_picking_load/static/description/load_picking_stock_picking.png

After

Width: 854  |  Height: 360  |  Size: 26 KiB

BIN
pos_picking_load/static/description/load_picking_warning_partner.png

After

Width: 492  |  Height: 392  |  Size: 10 KiB

BIN
pos_picking_load/static/description/load_picking_warning_picking_still_loaded.png

After

Width: 492  |  Height: 394  |  Size: 12 KiB

BIN
pos_picking_load/static/description/load_picking_warning_product.png

After

Width: 496  |  Height: 394  |  Size: 19 KiB

BIN
pos_picking_load/static/description/pos_config_form.png

Before

Width: 637  |  Height: 160  |  Size: 12 KiB

After

Width: 634  |  Height: 178  |  Size: 17 KiB

30
pos_picking_load/static/src/css/pos_picking_load.css

@ -1,11 +1,24 @@
/* LoadPickingScreenWidget - Header Part*/
.pos .pickinglist-screen .button.picking-button {
width: 150px;
}
.pos .pickinglist-screen .button.cancel {
margin-left: -180px;
.pos .pickinglist-screen .searchbox{
top: 4px;
position: relative;
}
.pos .pickinglist-screen .searchbox input{
width: 300px;
}
.pos .pickinglist-screen .button.validate.picking-button{
right: 0px;
}
/* LoadPickingScreenWidget - List Part*/
.pos .pickinglist-screen .picking-list{
font-size: 16px;
width: 100%;
@ -27,14 +40,11 @@
background: rgb(247,247,247);
}
.pos .pickinglist-screen .picking-list tbody > tr:hover {
background: rgb(220,220,220);
}
.pos .pickinglist-screen .searchbox{
margin-top:8px;
.pos .pickinglist-screen .picking-list .highlight {
background-color: rgb(110, 200, 155) !important;
color: rgb(255, 255, 255);
}
.pos .pickinglist-screen .searchbox input{
width: 150px;
.pos .pickinglist-screen .picking-list tbody > tr:hover {
background: rgb(220,220,220);
}

77
pos_picking_load/static/src/js/model.js

@ -0,0 +1,77 @@
/** ***************************************************************************
Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
******************************************************************************/
odoo.define('pos_picking_load.model', function (require) {
"use strict";
var models = require('point_of_sale.models');
/** **********************************************************************
Extend Model Order:
* Add getter and setter function for field 'origin_picking_id';
*/
var moduleOrderParent = models.Order;
models.Order = models.Order.extend({
load_from_picking_data: function (picking_data) {
var self = this;
var partner = this.pos.db.get_partner_by_id(
picking_data.partner_id);
this.set({
'origin_picking_id': picking_data.id,
'origin_picking_name': picking_data.name,
});
this.set_client(partner);
picking_data.line_ids.forEach(function (picking_line_data) {
// Create new line and add it to the current order
var product = self.pos.db.get_product_by_id(
picking_line_data.product_id);
var order_line_data =
self.prepare_order_line_from_picking_line_data(
product, picking_line_data);
self.add_product(product, order_line_data);
});
},
prepare_order_line_from_picking_line_data: function (
product, picking_line_data) {
return {
quantity: picking_line_data.quantity,
price: picking_line_data.price_unit || product.price,
discount: picking_line_data.discount || 0.0,
};
},
export_for_printing: function () {
var order = moduleOrderParent.prototype.export_for_printing.apply(
this, arguments);
order.origin_picking_name = this.get('origin_picking_name');
return order;
},
export_as_JSON: function () {
var order = moduleOrderParent.prototype.export_as_JSON.apply(
this, arguments);
order.origin_picking_id = this.get('origin_picking_id');
order.origin_picking_name = this.get('origin_picking_name');
return order;
},
init_from_JSON: function (json) {
moduleOrderParent.prototype.init_from_JSON.apply(this, arguments);
this.set({
'origin_picking_id': json.origin_picking_id,
'origin_picking_name': json.origin_picking_name,
});
},
});
});

329
pos_picking_load/static/src/js/pos_picking_load.js

@ -1,329 +0,0 @@
/******************************************************************************
Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
*****************************************************************************/
openerp.pos_picking_load = function(instance, local) {
module = instance.point_of_sale;
var QWeb = instance.web.qweb;
var _t = instance.web._t;
var round_pr = instance.web.round_precision;
/*************************************************************************
Extend Model Order:
* Add getter and setter function for field 'origin_picking_id';
*/
var moduleOrderParent = module.Order;
module.Order = module.Order.extend({
set_origin_picking_id: function(id) {
this.set({
origin_picking_id: id,
});
},
set_origin_picking_name: function(name) {
this.set({
origin_picking_name: name,
});
},
export_for_printing: function(attributes){
var order = moduleOrderParent.prototype.export_for_printing.apply(this, arguments);
order['origin_picking_name'] = this.get('origin_picking_name');
return order;
},
export_as_JSON: function() {
var order = moduleOrderParent.prototype.export_as_JSON.apply(this, arguments);
order['origin_picking_id'] = this.get('origin_picking_id');
return order;
},
});
/*************************************************************************
New Widget LoadPickingButtonWidget:
* On click, display a new screen to select a picking;
*/
module.LoadPickingButtonWidget = module.PosBaseWidget.extend({
template: 'LoadPickingButtonWidget',
renderElement: function() {
var self = this;
this._super();
this.$el.click(function(){
var ss = self.pos.pos_widget.screen_selector;
ss.set_current_screen('pickinglist');
});
},
});
/*************************************************************************
Extend PosWidget:
* Create new screen;
* Add load and save button;
*/
module.PosWidget = module.PosWidget.extend({
build_widgets: function() {
this._super();
if (this.pos.config.iface_load_picking){
// New Screen to select a picking
this.pickinglist_screen = new module.PickingListScreenWidget(this, {});
this.pickinglist_screen.appendTo(this.$('.screens'));
this.pickinglist_screen.hide();
this.screen_selector.screen_set.pickinglist = this.pickinglist_screen;
// Add button
this.search_picking_button = new module.LoadPickingButtonWidget(this,{});
this.search_picking_button.appendTo(this.pos_widget.$('li.orderline.empty'));
}
},
});
/*************************************************************************
Extend OrderWidget:
*/
module.OrderWidget = module.OrderWidget.extend({
renderElement: function(scrollbottom){
this._super(scrollbottom);
if (this.pos_widget.search_picking_button) {
this.pos_widget.search_picking_button.appendTo(
this.pos_widget.$('li.orderline.empty')
);
}
}
});
/*************************************************************************
New ScreenWidget PickingListScreenWidget:
* On show, display all pickings;
* on click on a picking, display the content;
* on click on 'validate', allow to use this picking;
* on click on 'cancel', display the preview screen;
*/
module.PickingListScreenWidget = module.ScreenWidget.extend({
template: 'PickingListScreenWidget',
show_leftpane: true,
model: 'stock.picking',
current_picking_id: false,
current_picking_name: false,
// Base functions
init: function(parent, options){
this._super(parent, options);
},
start: function() {
var self = this;
this._super();
// Bind click buttons
this.$el.find('span.button.cancel').click(_.bind(this.clickCancelButton, this));
this.$el.find('span.button.validate').click(_.bind(this.clickValidateButton, this));
// manage Search Box
var search_timeout = null;
this.$('.searchbox input').on('keyup',function(event){
clearTimeout(search_timeout);
var query = this.value;
search_timeout = setTimeout(function(){
self.perform_search(query);
},70);
});
this.$('.searchbox .search-clear').click(function(){
self.clear_search();
});
},
show: function() {
this._super();
var ss = this.pos.pos_widget.screen_selector;
this.pos_widget.numpad.hide();
this.pos_widget.paypad.hide();
this.search_pickings();
this.$el.find('span.button.validate').hide();
},
prepare_order: function(order, picking) {
var partner = this.pos.db.get_partner_by_id(picking.partner_id);
order.set_client(partner || undefined);
return order;
},
prepare_orderline: function(product, pickingline) {
return {
quantity: pickingline.quantity,
price: pickingline.price_unit || product.price,
discount: pickingline.discount || 0.0,
};
},
// User Event
clickCancelButton: function(event) {
order = this.pos.get('selectedOrder');
order.set_client(undefined);
order.set_origin_picking_id(undefined);
order.set_origin_picking_name(undefined);
order.get('orderLines').reset();
this.pos_widget.order_widget.change_selected_order();
var ss = this.pos.pos_widget.screen_selector;
ss.set_current_screen('products');
this.pos_widget.numpad.show();
this.pos_widget.paypad.show();
},
clickValidateButton: function(event) {
order = this.pos.get('selectedOrder');
order.set_origin_picking_id(this.current_picking_id);
order.set_origin_picking_name(this.current_picking_name);
var ss = this.pos.pos_widget.screen_selector;
ss.set_current_screen('products');
this.pos_widget.numpad.show();
this.pos_widget.paypad.show();
},
load_picking: function(origin_picking_id) {
var self = this;
var pickingModel = new instance.web.Model(this.model);
return pickingModel.call('load_picking_for_pos', [[origin_picking_id]])
.then(function (picking) {
self.current_picking_id = origin_picking_id;
self.current_picking_name = picking.name;
var picking_selectable = true;
var order = self.pos.get('selectedOrder');
order = self.prepare_order(order, picking);
order.get('orderLines').reset();
var pickinglines = picking.line_ids || [];
var unknown_products = [];
for (var i=0, len=pickinglines.length; i<len; i++) {
// check if product are available in pos
var pickingline = pickinglines[i];
var line_name = pickingline.name;
var product = self.pos.db.get_product_by_id(pickingline.product_id);
if (_.isUndefined(product)) {
unknown_products.push(line_name);
continue;
}
// Create new line and add it to the current order
orderline = self.prepare_orderline(product, pickingline);
order.addProduct(product, orderline);
last_orderline = order.getLastOrderline();
last_orderline = jQuery.extend(last_orderline, orderline);
}
// Forbid POS Order loading if some products are unknown
if (unknown_products.length > 0){
self.pos_widget.screen_selector.show_popup(
'error-traceback', {
message: _t('Unknown Products'),
comment: _t('Unable to load some picking lines because the ' +
'products are not available in the POS cache.\n\n' +
'Please check that lines :\n\n * ') + unknown_products.join("; \n *")
});
picking_selectable = false;
}
// Check if the partner is unknown
if (_.isUndefined(order.get_client)) {
self.pos_widget.screen_selector.show_popup(
'error-traceback', {
message: _t('Unknown Partner'),
comment: _t('Unable to load this picking because the partner' +
' is not known in the Point Of Sale as a customer')
});
picking_selectable = false;
}
if (picking_selectable){
self.$el.find('span.button.validate').show();
}
else{
self.$el.find('span.button.validate').hide();
}
}).fail(function (error, event){
if (parseInt(error.code) === 200) {
// Business Logic Error, not a connection problem
self.pos_widget.screen_selector.show_popup(
'error-traceback', {
message: error.data.message,
comment: error.data.debug
});
}
else{
self.pos_widget.screen_selector.show_popup('error',{
message: _t('Connection error'),
comment: _t('Can not execute this action because the POS is currently offline'),
});
}
event.preventDefault();
});
},
search_pickings: function(query) {
var self = this;
var pickingModel = new instance.web.Model(this.model);
return pickingModel.call('search_pickings_for_pos', [query || '', this.pos.pos_session.id])
.then(function (result) {
self.render_list(result);
}).fail(function (error, event){
if (parseInt(error.code) === 200) {
// Business Logic Error, not a connection problem
self.pos_widget.screen_selector.show_popup(
'error-traceback', {
message: error.data.message,
comment: error.data.debug
}
);
}
else{
self.pos_widget.screen_selector.show_popup('error',{
message: _t('Connection error'),
comment: _t('Can not execute this action because the POS is currently offline'),
});
}
event.preventDefault();
});
},
on_click_picking: function(event){
this.load_picking(parseInt(event.target.parentNode.dataset.pickingId, 10));
},
render_list: function(pickings){
var self = this;
var contents = this.$el[0].querySelector('.picking-list-contents');
contents.innerHTML = "";
var line_list = document.createDocumentFragment();
_.each(pickings, function(picking){
var picking_line_html = QWeb.render('LoadPickingLine',{widget: this, picking:picking});
var picking_line = document.createElement('tbody');
picking_line.innerHTML = picking_line_html;
picking_line = picking_line.childNodes[1];
picking_line.addEventListener('click', self.on_click_picking);
line_list.appendChild(picking_line);
});
contents.appendChild(line_list);
},
perform_search: function(query){
this.search_pickings(query);
},
clear_search: function(){
this.search_pickings();
this.$('.searchbox input')[0].value = '';
this.$('.searchbox input').focus();
},
});
};

289
pos_picking_load/static/src/js/widget.js

@ -0,0 +1,289 @@
/** ***************************************************************************
Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
******************************************************************************/
odoo.define('pos_picking_load.widget', function (require) {
"use strict";
var core = require('web.core');
var framework = require('web.framework');
var Model = require('web.DataModel');
var gui = require('point_of_sale.gui');
var screens = require('point_of_sale.screens');
var QWeb = core.qweb;
var _t = core._t;
/** **********************************************************************
New ScreenWidget LoadPickingScreenWidget:
* On show, display all pickings;
* on click on a picking, display the content;
* on click on 'validate', allow to use this picking;
* on click on 'cancel', display the preview screen;
*/
var LoadPickingScreenWidget = screens.ScreenWidget.extend({
template: 'LoadPickingScreenWidget',
auto_back: true,
current_picking_id: false,
current_picking_name: false,
show: function () {
var self = this;
this._super();
this.renderElement();
// Bind functions
this.$('.back').click(_.bind(this.clickBackButton, this));
this.$('.validate').click(_.bind(this.clickValidateButton, this));
// Initialize display
this.$('.validate').hide();
this.search_pickings();
this.$('.picking-list-contents').delegate(
'.picking-line', 'click', function (event) {
self.select_picking(event);
});
},
select_picking: function (event) {
var origin_picking_id = parseInt(
event.target.parentNode.dataset.pickingId, 10);
var self = this;
this.current_picking_data = false;
this.$('span.button.validate').hide();
this.$('.picking-list .highlight').removeClass('highlight');
framework.blockUI();
new Model('stock.picking').call(
'load_picking_for_pos', [origin_picking_id])
.then(function (picking_data) {
framework.unblockUI();
if (self.check_picking(picking_data)) {
self.current_picking_data = picking_data;
$(event.target.parentNode).addClass('highlight');
self.$('span.button.validate').show();
}
}).fail(function (error, event) {
framework.unblockUI();
self.handle_errors(error, event);
});
},
check_picking: function (picking_data) {
var self = this;
var picking_selectable = true;
// Forbid POS Order loading if some products are unknown
var unknown_products = [];
picking_data.line_ids.forEach(function (picking_line_data) {
var line_name = picking_line_data.name.replace('\n', ' ');
var product = self.pos.db.get_product_by_id(
picking_line_data.product_id);
if (_.isUndefined(product)) {
unknown_products.push(line_name);
}
});
if (unknown_products.length > 0) {
self.gui.show_popup(
'error-traceback', {
'title': _t('Unknown Products'),
'body': _t(
"Unable to load some picking lines because the" +
" products are not available in the POS" +
" cache.\n\n" +
"Please check that lines :\n\n * ") +
unknown_products.join("; \n *"),
});
picking_selectable = false;
}
// Check if the partner is unknown
var partner = self.pos.db.get_partner_by_id(
picking_data.partner_id);
if (_.isUndefined(partner)) {
self.gui.show_popup(
'error-traceback', {
'title': _t('Unknown Partner'),
'body': _t(
"Unable to load this picking because the partner" +
" is not known in the Point Of Sale" +
" as a customer"),
});
picking_selectable = false;
}
// Check if the picking is still loaded in another PoS tab
self.pos.db.get_unpaid_orders().forEach(function (order) {
if (order.origin_picking_id === picking_data.id) {
self.gui.show_popup(
'error-traceback', {
'title': _t('Picking Still Loaded'),
'body': _t(
"Unable to load this picking because it has" +
" been loaded in another draft" +
" PoS Order :\n\n") +
order.name,
});
picking_selectable = false;
}
});
// Check if the picking has still been handled in another PoS Order
self.pos.db.get_orders().forEach(function (order) {
if (order.origin_picking_id === picking_data.id) {
self.gui.show_popup(
'error-traceback', {
'title': _t('Picking Still Loaded'),
'body': _t(
"Unable to load this picking because it has" +
" been loaded in another confirmed" +
" PoS Order :\n\n") +
order.name,
});
picking_selectable = false;
}
});
return picking_selectable;
},
search_pickings: function (query) {
var self = this;
return new Model('stock.picking').call(
'search_pickings_for_pos',
[query || '', this.pos.pos_session.id])
.then(function (result) {
self.render_list(result);
}).fail(function (error, event) {
self.handle_errors(error, event);
});
},
render_list: function (pickings) {
var contents = this.$el[0].querySelector('.picking-list-contents');
contents.innerHTML = "";
var line_list = document.createDocumentFragment();
_.each(pickings, function (picking) {
var picking_line_html = QWeb.render(
'LoadPickingLine', {widget: this, picking:picking});
var picking_line = document.createElement('tbody');
picking_line.innerHTML = picking_line_html;
picking_line = picking_line.childNodes[1];
line_list.appendChild(picking_line);
});
contents.appendChild(line_list);
},
// User Event
clickBackButton: function () {
this.gui.back();
},
clickValidateButton: function () {
var order = this.pos.get_order();
order.load_from_picking_data(this.current_picking_data);
this.gui.show_screen('products');
},
handle_errors: function (error, event) {
var self = this;
if (parseInt(error.code, 10) === 200) {
// Business Logic Error, not a connection problem
self.gui.show_popup('error-traceback', {
'title': error.data.message || _t("Server Error"),
'body': error.data.debug || _t(
"The server encountered an error while" +
" receiving your order."),
});
} else {
self.gui.show_popup('error', {
'title': _t('Connection error'),
'body': _t(
"Can not execute this action because the POS" +
" is currently offline"),
});
}
event.preventDefault();
},
});
gui.define_screen({
'name': 'load_picking',
'widget': LoadPickingScreenWidget,
'condition': function () {
return this.pos.config.iface_load_picking;
},
});
/** **********************************************************************
New Widget LoadPickingButtonWidget:
* On click, display a new screen to select a picking;
*/
var LoadPickingButtonWidget = screens.ActionButtonWidget.extend({
template: 'LoadPickingButtonWidget',
button_click: function () {
if (_.isUndefined(this.pos.get_order().get('origin_picking_id'))) {
this.gui.show_screen('load_picking');
} else {
this.gui.show_popup('error', {
'title': _t('Pending Picking'),
'body': _t(
"A picking is still loaded. You can not load" +
" another picking. Please create a new order."),
});
}
},
button_text: function () {
if (! this.pos.get_order() ||
_.isUndefined(
this.pos.get_order().get('origin_picking_id'))) {
return _t("Load Picking");
}
return this.pos.get_order().get('origin_picking_name');
},
is_visible: function () {
if (this.pos.get_order()) {
return (
this.pos.get_order().get_orderlines().length === 0 ||
! _.isUndefined(
this.pos.get_order().get('origin_picking_id')));
}
return false;
},
});
screens.define_action_button({
'name': 'load_picking',
'widget': LoadPickingButtonWidget,
'condition': function () {
return this.pos.config.iface_load_picking;
},
});
screens.OrderWidget.include({
update_summary: function () {
this._super();
if (this.getParent().action_buttons &&
this.getParent().action_buttons.load_picking) {
this.getParent().action_buttons.load_picking.renderElement();
}
},
});
});

23
pos_picking_load/static/src/xml/pos_picking_load.xml

@ -2,35 +2,38 @@
<template>
<t t-name="LoadPickingButtonWidget">
<br/><button class="input-button">Load Picking</button>
<span class="control-button order-split" t-att-style='widget.is_visible() ? "display: block;" : "display: none;"'>
<i class="fa fa-upload"></i>
<t t-esc="widget.button_text()"/>
</span>
</t>
<!-- t-att-style= -->
<t t-name="LoadPickingLine">
<tr class="picking-line" t-att-data-picking-id="picking.id">
<td><t t-esc="picking.name"/></td>
<td><t t-esc="picking.partner_id[1]"/></td>
<td><t t-esc="picking.min_date"/></td>
<td><t t-esc="picking.origin"/></td>
<td><t t-esc="picking.partner_id[1] || ''"/></td>
<td><t t-esc="picking.min_date || ''"/></td>
<td><t t-esc="picking.origin || ''"/></td>
</tr>
</t>
<t t-name="PickingListScreenWidget">
<t t-name="LoadPickingScreenWidget">
<div class="pickinglist-screen screen">
<div class="screen-content">
<section class="top-content">
<span class="button cancel picking-button">
<span class="button back picking-button">
<i class="fa fa-times"></i>
Cancel
</span>
<span class="button validate picking-button">
<i class="fa fa-arrow-circle-o-left"></i>
<span class="button validate picking-button highlight">
<i class="fa fa-arrow-circle-o-right"></i>
Select
</span>
<span class="searchbox">
<input placeholder="Search Picking" />
<span class="search-clear"></span>
</span>
<span class="searchbox"></span>
</section>
<section class="full-content">
<div class="window">

17
pos_picking_load/tests/test_pos_picking_load.py

@ -2,12 +2,14 @@
# Copyright (C) 2017: Opener B.V. (https://opener.amsterdam)
# @author: Stefan Rijnhart <stefan@opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase
from odoo import fields
from odoo.tests.common import TransactionCase
class TestPosPickingLoad(TransactionCase):
def test_pos_picking_load(self):
product = self.env.ref('product.product_product_24')
# Create a Sale order and confirm it to generate a picking to load
sale_order = self.env['sale.order'].create({
'partner_id': self.env.ref('base.res_partner_1').id,
'order_line': [(0, 0, {
@ -15,24 +17,31 @@ class TestPosPickingLoad(TransactionCase):
'price_unit': 6,
'product_uom_qty': 2,
})],
'order_policy': 'picking',
})
sale_order.action_button_confirm()
sale_order.action_confirm()
self.assertTrue(sale_order.picking_ids)
sale_order.picking_ids.picking_type_id.write({
'available_in_pos': True})
# Configure Point of sale
config = self.env.ref('point_of_sale.pos_config_main').copy()
config.write({
'picking_type_id': sale_order.picking_ids.picking_type_id.id})
# Open a new session
session = self.env['pos.session'].create({
'user_id': self.env.user.id,
'config_id': config.id})
session.signal_workflow('open')
date_now = fields.Datetime.now()
# Create a pos order
self.env['pos.order'].create_from_ui([{
'to_invoice': False,
'data': {
'user_id': self.env.user.id,
'creation_date': date_now,
'fiscal_position_id': False,
'name': 'Order 00017-002-0003',
'partner_id': sale_order.partner_id.id,
'amount_paid': 12,
@ -48,7 +57,7 @@ class TestPosPickingLoad(TransactionCase):
'statement_ids': [[0, 0, {
'journal_id': False,
'amount': 12,
'name': '2017-07-20 13:08:37',
'name': date_now,
'account_id': config.journal_ids[
0].default_debit_account_id.id,
'statement_id': session.statement_ids[0].id,

32
pos_picking_load/views/pos_picking_load.xml

@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- vim:fdn=3:
-->
<openerp>
<data>
<template id="assets_backend" name="pos_picking_load assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/pos_picking_load/static/src/js/pos_picking_load.js"></script>
</xpath>
</template>
<template id="index_pos_picking_load" name="POS Index Picking Load" inherit_id="point_of_sale.index">
<xpath expr="//head" position="inside">
<link rel="stylesheet" href="/pos_picking_load/static/src/css/pos_picking_load.css"/>
</xpath>
</template>
</data>
</openerp>
<odoo>
<template id="assets" name="pos_picking_load assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/pos_picking_load/static/src/js/widget.js"></script>
<script type="text/javascript" src="/pos_picking_load/static/src/js/model.js"></script>
</xpath>
</template>
<template id="index_pos_picking_load" name="POS Index Picking Load" inherit_id="point_of_sale.index">
<xpath expr="//head" position="inside">
<link rel="stylesheet" href="/pos_picking_load/static/src/css/pos_picking_load.css"/>
</xpath>
</template>
</odoo>

5
pos_picking_load/views/view_pos_config.xml

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp><data>
<odoo>
<record id="view_pos_config_form" model="ir.ui.view">
<field name="model">pos.config</field>
@ -12,4 +13,4 @@
</field>
</record>
</data></openerp>
</odoo>

9
pos_picking_load/views/view_sale_order.xml

@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp><data>
<odoo>
<record id="view_sale_order_form" model="ir.ui.view">
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale_stock.view_order_form_inherit"/>
<field name="inherit_id" ref="sale_stock.view_order_form_inherit_sale_stock"/>
<field name="arch" type="xml">
<field name="picking_policy" position="before">
<field name="final_pos_order_id" />
<field name="final_pos_order_id" attrs="{'invisible': [('final_pos_order_id', '=', False)]}"/>
</field>
</field>
</record>
</data></openerp>
</odoo>

11
pos_picking_load/views/view_stock_picking.xml

@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp><data>
<odoo>
<record id="view_stock_picking_form" model="ir.ui.view">
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock_account.view_picking_inherit_form2"/>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<field name="invoice_state" position="after">
<field name="final_pos_order_id" />
<field name="picking_type_id" position="after">
<field name="final_pos_order_id" attrs="{'invisible': [('final_pos_order_id', '=', False)]}"/>
</field>
</field>
</record>
</data></openerp>
</odoo>

5
pos_picking_load/views/view_stock_picking_type.xml

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp><data>
<odoo>
<record id="view_stock_picking_type_form" model="ir.ui.view">
<field name="model">stock.picking.type</field>
@ -11,4 +12,4 @@
</field>
</record>
</data></openerp>
</odoo>
Loading…
Cancel
Save