[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_linepull/365/head
-
170pos_picking_load/README.rst
-
2pos_picking_load/__init__.py
-
11pos_picking_load/__manifest__.py
-
12pos_picking_load/demo/product_template.xml
-
12pos_picking_load/demo/sale_order.xml
-
179pos_picking_load/i18n/fr.po
-
2pos_picking_load/models/__init__.py
-
4pos_picking_load/models/pos_config.py
-
4pos_picking_load/models/pos_order.py
-
4pos_picking_load/models/sale_order.py
-
46pos_picking_load/models/stock_picking.py
-
4pos_picking_load/models/stock_picking_type.py
-
41pos_picking_load/readme/CONFIGURE.rst
-
2pos_picking_load/readme/CONTRIBUTORS.rst
-
16pos_picking_load/readme/DESCRIPTION.rst
-
8pos_picking_load/readme/ROADMAP.rst
-
69pos_picking_load/readme/USAGE.rst
-
562pos_picking_load/static/description/index.html
-
BINpos_picking_load/static/description/load_picking_01.png
-
BINpos_picking_load/static/description/load_picking_01_load_button.png
-
BINpos_picking_load/static/description/load_picking_02.png
-
BINpos_picking_load/static/description/load_picking_02_picking_list.png
-
BINpos_picking_load/static/description/load_picking_03.png
-
BINpos_picking_load/static/description/load_picking_03_confirm.png
-
BINpos_picking_load/static/description/load_picking_04.png
-
BINpos_picking_load/static/description/load_picking_04_pos_order.png
-
BINpos_picking_load/static/description/load_picking_05.png
-
BINpos_picking_load/static/description/load_picking_06.png
-
BINpos_picking_load/static/description/load_picking_sale_order.png
-
BINpos_picking_load/static/description/load_picking_stock_picking.png
-
BINpos_picking_load/static/description/load_picking_warning_partner.png
-
BINpos_picking_load/static/description/load_picking_warning_picking_still_loaded.png
-
BINpos_picking_load/static/description/load_picking_warning_product.png
-
BINpos_picking_load/static/description/pos_config_form.png
-
30pos_picking_load/static/src/css/pos_picking_load.css
-
77pos_picking_load/static/src/js/model.js
-
329pos_picking_load/static/src/js/pos_picking_load.js
-
289pos_picking_load/static/src/js/widget.js
-
23pos_picking_load/static/src/xml/pos_picking_load.xml
-
17pos_picking_load/tests/test_pos_picking_load.py
-
16pos_picking_load/views/pos_picking_load.xml
-
5pos_picking_load/views/view_pos_config.xml
-
9pos_picking_load/views/view_sale_order.xml
-
11pos_picking_load/views/view_stock_picking.xml
-
5pos_picking_load/views/view_stock_picking_type.xml
@ -1,2 +1,2 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# coding: utf-8 |
|||
from . import models |
@ -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``. |
@ -0,0 +1,2 @@ |
|||
* Sylvain LE GAL (https://twitter.com/legalsylvain) |
|||
* Stefan Rijnhart <stefan@opener.am> |
@ -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 |
@ -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.** |
@ -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 |
@ -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 <<a class="reference external" href="mailto:stefan@opener.am">stefan@opener.am</a>></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> |
Before Width: 421 | Height: 205 | Size: 8.9 KiB |
After Width: 428 | Height: 494 | Size: 22 KiB |
Before Width: 855 | Height: 222 | Size: 26 KiB |
After Width: 976 | Height: 285 | Size: 33 KiB |
Before Width: 415 | Height: 306 | Size: 26 KiB |
After Width: 1025 | Height: 267 | Size: 35 KiB |
Before Width: 837 | Height: 495 | Size: 43 KiB |
After Width: 430 | Height: 637 | Size: 38 KiB |
Before Width: 839 | Height: 298 | Size: 33 KiB |
Before Width: 511 | Height: 426 | Size: 14 KiB |
After Width: 844 | Height: 384 | Size: 35 KiB |
After Width: 854 | Height: 360 | Size: 26 KiB |
After Width: 492 | Height: 392 | Size: 10 KiB |
After Width: 492 | Height: 394 | Size: 12 KiB |
After Width: 496 | Height: 394 | Size: 19 KiB |
Before Width: 637 | Height: 160 | Size: 12 KiB After Width: 634 | Height: 178 | Size: 17 KiB |
@ -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, |
|||
}); |
|||
}, |
|||
|
|||
}); |
|||
|
|||
}); |
@ -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(); |
|||
}, |
|||
|
|||
}); |
|||
|
|||
}; |
@ -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(); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
}); |
@ -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"> |
|||
|
|||
<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/pos_picking_load.js"></script> |
|||
<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> |
|||
</data> |
|||
</openerp> |
|||
|
|||
</odoo> |
@ -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> |
@ -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> |