diff --git a/pos_order_to_sale_order/README.rst b/pos_order_to_sale_order/README.rst
new file mode 100644
index 00000000..03c108e3
--- /dev/null
+++ b/pos_order_to_sale_order/README.rst
@@ -0,0 +1,8 @@
+=======================
+PoS Order To Sale Order
+=======================
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
diff --git a/pos_order_to_sale_order/__init__.py b/pos_order_to_sale_order/__init__.py
new file mode 100644
index 00000000..0650744f
--- /dev/null
+++ b/pos_order_to_sale_order/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/pos_order_to_sale_order/__manifest__.py b/pos_order_to_sale_order/__manifest__.py
new file mode 100644
index 00000000..26bb8aa2
--- /dev/null
+++ b/pos_order_to_sale_order/__manifest__.py
@@ -0,0 +1,20 @@
+# 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 Order To Sale Order",
+ "version": "12.0.1.0.0",
+ "author": "GRAP,Odoo Community Association (OCA)",
+ "category": "Point Of Sale",
+ "license": "AGPL-3",
+ "depends": ["point_of_sale"],
+ "maintainers": ["legalsylvain"],
+ "development_status": "Production/Stable",
+ "website": "https://github.com/OCA/pos",
+ "data": [
+ "views/view_pos_config.xml",
+ "views/assets.xml",
+ ],
+ "qweb": ["static/src/xml/pos_order_to_sale_order.xml"],
+}
diff --git a/pos_order_to_sale_order/i18n/fr.po b/pos_order_to_sale_order/i18n/fr.po
new file mode 100644
index 00000000..782e0889
--- /dev/null
+++ b/pos_order_to_sale_order/i18n/fr.po
@@ -0,0 +1,164 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * pos_order_to_sale_order
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-01-19 23:40+0000\n"
+"PO-Revision-Date: 2020-01-19 23:40+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:15
+#, python-format
+msgid "Back"
+msgstr "Retour"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:148
+#, python-format
+msgid "Check your internet connection and try again."
+msgstr "Veuillez vérifier votre connexion internet et essayer de nouveau."
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:26
+#, python-format
+msgid "Create Confirmed Sale Order"
+msgstr "Créer une vente confirmée"
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,field_description:pos_order_to_sale_order.field_pos_config__iface_create_confirmed_sale_order
+msgid "Create Confirmed Sale Orders"
+msgstr "Créer des ventes confirmées"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:29
+#, python-format
+msgid "Create Delivered Sale Order"
+msgstr "Créer une vente livrée"
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,field_description:pos_order_to_sale_order.field_pos_config__iface_create_delivered_sale_order
+msgid "Create Delivered Sale Orders"
+msgstr "Créer des ventes livrées"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:23
+#, python-format
+msgid "Create Draft Sale Order"
+msgstr "Créer un devis"
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,field_description:pos_order_to_sale_order.field_pos_config__iface_create_draft_sale_order
+msgid "Create Draft Sale Orders"
+msgstr "Créer des devis"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:6
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:18
+#, python-format
+msgid "Create Sale Order"
+msgstr "Créer une vente"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:100
+#, python-format
+msgid "Create Sale Order and discard the current PoS Order?"
+msgstr "Créer une vente, et supprimer le ticket de caisse en cours ?"
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,field_description:pos_order_to_sale_order.field_pos_config__iface_create_sale_order
+msgid "Create Sale Orders"
+msgstr "Créer des ventes"
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,help:pos_order_to_sale_order.field_pos_config__iface_create_confirmed_sale_order
+msgid "If checked, the cashier will have the possibility to create a confirmed Sale Order, based on the current draft PoS Order."
+msgstr "En cochant la case, le caissier aura la possibilité de créer une vente confirmée, à partir du ticket de caisse en cours."
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,help:pos_order_to_sale_order.field_pos_config__iface_create_delivered_sale_order
+msgid "If checked, the cashier will have the possibility to create a confirmed sale Order, based on the current draft PoS Order.\n"
+" the according picking will be marked as delivered. Only invoices process will be possible."
+msgstr ""
+"En cochant la case, le caissier aura la possibilité de créer une vente "
+"confirmée, à partir du ticket de caisse en cours.\n"
+" Le bon de livraison correspondant sera marqué comme livré. Seul le "
+"processus de facturation sera possible."
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,help:pos_order_to_sale_order.field_pos_config__iface_create_draft_sale_order
+msgid "If checked, the cashier will have the possibility to create a draft Sale Order, based on the current draft PoS Order."
+msgstr ""
+"En cochant la case, le caissier aura la possibilité de créer une vente en "
+"brouillon, à partir du ticket de caisse en cours."
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:29
+#, python-format
+msgid "No customer defined"
+msgstr "Pas de client défini"
+
+#. module: pos_order_to_sale_order
+#: code:addons/pos_order_to_sale_order/models/sale_order.py:17
+#, python-format
+msgid "Point of Sale %s"
+msgstr "Point de vente %s"
+
+#. module: pos_order_to_sale_order
+#: model:ir.model,name:pos_order_to_sale_order.model_pos_config
+msgid "Point of Sale Configuration"
+msgstr "Paramétrage du point de vente"
+
+#. module: pos_order_to_sale_order
+#: model:ir.model,name:pos_order_to_sale_order.model_sale_order
+msgid "Sale Order"
+msgstr "Bon de commande"
+
+#. module: pos_order_to_sale_order
+#: model_terms:ir.ui.view,arch_db:pos_order_to_sale_order.view_pos_config_form
+msgid "Sale Order Creations"
+msgstr "Créations de Vente"
+
+#. module: pos_order_to_sale_order
+#: model:ir.model,name:pos_order_to_sale_order.model_sale_order_line
+msgid "Sales Order Line"
+msgstr "Ligne de bons de commande"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:147
+#, python-format
+msgid "The order could not be sent"
+msgstr "La commande n'a pas pu être envoyée"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:103
+#, python-format
+msgid "This operation will permanently discard the current PoS Order and create a Sale Order, based on the current order lines."
+msgstr ""
+"Cette opération va supprimer définitivement le ticket de caisse en cours, et "
+"créer une vente, en se basant sur les lignes du ticket en cours."
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:30
+#, python-format
+msgid "You should select a customer in order to create a Sale Order."
+msgstr "Vous devez sélectionner un client, afin de créer une vente."
diff --git a/pos_order_to_sale_order/i18n/pos_order_to_sale_order.pot b/pos_order_to_sale_order/i18n/pos_order_to_sale_order.pot
new file mode 100644
index 00000000..1ec9191c
--- /dev/null
+++ b/pos_order_to_sale_order/i18n/pos_order_to_sale_order.pot
@@ -0,0 +1,156 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * pos_order_to_sale_order
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-01-19 23:45+0000\n"
+"PO-Revision-Date: 2020-01-19 23:45+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:15
+#, python-format
+msgid "Back"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:148
+#, python-format
+msgid "Check your internet connection and try again."
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:26
+#, python-format
+msgid "Create Confirmed Sale Order"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,field_description:pos_order_to_sale_order.field_pos_config__iface_create_confirmed_sale_order
+msgid "Create Confirmed Sale Orders"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:29
+#, python-format
+msgid "Create Delivered Sale Order"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,field_description:pos_order_to_sale_order.field_pos_config__iface_create_delivered_sale_order
+msgid "Create Delivered Sale Orders"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:23
+#, python-format
+msgid "Create Draft Sale Order"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,field_description:pos_order_to_sale_order.field_pos_config__iface_create_draft_sale_order
+msgid "Create Draft Sale Orders"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:6
+#: code:addons/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml:18
+#, python-format
+msgid "Create Sale Order"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:100
+#, python-format
+msgid "Create Sale Order and discard the current PoS Order?"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,field_description:pos_order_to_sale_order.field_pos_config__iface_create_sale_order
+msgid "Create Sale Orders"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,help:pos_order_to_sale_order.field_pos_config__iface_create_confirmed_sale_order
+msgid "If checked, the cashier will have the possibility to create a confirmed Sale Order, based on the current draft PoS Order."
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,help:pos_order_to_sale_order.field_pos_config__iface_create_delivered_sale_order
+msgid "If checked, the cashier will have the possibility to create a confirmed sale Order, based on the current draft PoS Order.\n"
+" the according picking will be marked as delivered. Only invoices process will be possible."
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model.fields,help:pos_order_to_sale_order.field_pos_config__iface_create_draft_sale_order
+msgid "If checked, the cashier will have the possibility to create a draft Sale Order, based on the current draft PoS Order."
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:29
+#, python-format
+msgid "No customer defined"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: code:addons/pos_order_to_sale_order/models/sale_order.py:17
+#, python-format
+msgid "Point of Sale %s"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model,name:pos_order_to_sale_order.model_pos_config
+msgid "Point of Sale Configuration"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model,name:pos_order_to_sale_order.model_sale_order
+msgid "Sale Order"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model_terms:ir.ui.view,arch_db:pos_order_to_sale_order.view_pos_config_form
+msgid "Sale Order Creations"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#: model:ir.model,name:pos_order_to_sale_order.model_sale_order_line
+msgid "Sales Order Line"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:147
+#, python-format
+msgid "The order could not be sent"
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:103
+#, python-format
+msgid "This operation will permanently discard the current PoS Order and create a Sale Order, based on the current order lines."
+msgstr ""
+
+#. module: pos_order_to_sale_order
+#. openerp-web
+#: code:addons/pos_order_to_sale_order/static/src/js/screens.js:30
+#, python-format
+msgid "You should select a customer in order to create a Sale Order."
+msgstr ""
diff --git a/pos_order_to_sale_order/models/__init__.py b/pos_order_to_sale_order/models/__init__.py
new file mode 100644
index 00000000..1109a47e
--- /dev/null
+++ b/pos_order_to_sale_order/models/__init__.py
@@ -0,0 +1,3 @@
+from . import sale_order
+from . import sale_order_line
+from . import pos_config
diff --git a/pos_order_to_sale_order/models/pos_config.py b/pos_order_to_sale_order/models/pos_config.py
new file mode 100644
index 00000000..1b8c8841
--- /dev/null
+++ b/pos_order_to_sale_order/models/pos_config.py
@@ -0,0 +1,50 @@
+# 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 odoo import api, fields, models
+
+
+class PosConfig(models.Model):
+ _inherit = "pos.config"
+
+ iface_create_sale_order = fields.Boolean(
+ string="Create Sale Orders",
+ compute="_compute_iface_create_sale_order",
+ store=True)
+
+ iface_create_draft_sale_order = fields.Boolean(
+ string="Create Draft Sale Orders",
+ default=True,
+ help="If checked, the cashier will have the possibility to create"
+ " a draft Sale Order, based on the current draft PoS Order.",
+ )
+
+ iface_create_confirmed_sale_order = fields.Boolean(
+ string="Create Confirmed Sale Orders",
+ default=True,
+ help="If checked, the cashier will have the possibility to create"
+ " a confirmed Sale Order, based on the current draft PoS Order.",
+ )
+
+ iface_create_delivered_sale_order = fields.Boolean(
+ string="Create Delivered Sale Orders",
+ default=True,
+ help="If checked, the cashier will have the possibility to create"
+ " a confirmed sale Order, based on the current draft PoS Order.\n"
+ " the according picking will be marked as delivered. Only invoices"
+ " process will be possible.",
+ )
+
+ @api.depends(
+ "iface_create_draft_sale_order",
+ "iface_create_confirmed_sale_order",
+ "iface_create_delivered_sale_order",
+ )
+ def _compute_iface_create_sale_order(self):
+ for config in self:
+ config.iface_create_sale_order = any([
+ config.iface_create_draft_sale_order,
+ config.iface_create_confirmed_sale_order,
+ config.iface_create_delivered_sale_order,
+ ])
diff --git a/pos_order_to_sale_order/models/sale_order.py b/pos_order_to_sale_order/models/sale_order.py
new file mode 100644
index 00000000..56e95731
--- /dev/null
+++ b/pos_order_to_sale_order/models/sale_order.py
@@ -0,0 +1,62 @@
+# 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 odoo import _, api, models
+
+
+class SaleOrder(models.Model):
+ _inherit = "sale.order"
+
+ @api.model
+ def _prepare_from_pos(self, order_data):
+ PosSession = self.env["pos.session"]
+ session = PosSession.browse(order_data["pos_session_id"])
+ return {
+ "partner_id": order_data["partner_id"],
+ "origin": _("Point of Sale %s") % (session.name),
+ "client_order_ref": order_data["name"],
+ "user_id": order_data["user_id"],
+ "pricelist_id": order_data["pricelist_id"],
+ "fiscal_position_id": order_data["fiscal_position_id"],
+ }
+
+ @api.model
+ def create_order_from_pos(self, order_data, action):
+ SaleOrderLine = self.env["sale.order.line"]
+
+ # Create Draft Sale order
+ order_vals = self._prepare_from_pos(order_data)
+ sale_order = self.create(order_vals.copy())
+ sale_order.onchange_partner_id()
+ # we rewrite data, because onchange could alter some
+ # custom data (like pricelist)
+ sale_order.write(order_vals)
+
+ # create Sale order lines
+ for order_line_data in order_data["lines"]:
+ # Create Sale order lines
+ order_line_vals = SaleOrderLine._prepare_from_pos(
+ sale_order, order_line_data[2])
+ sale_order_line = SaleOrderLine.create(
+ order_line_vals.copy())
+ sale_order_line.product_id_change()
+ # we rewrite data, because onchange could alter some
+ # data (like quantity, or price)
+ sale_order_line.write(order_line_vals)
+
+ # Confirm Sale Order
+ if action in ["confirmed", "delivered"]:
+ sale_order.action_confirm()
+
+ # mark picking as delivered
+ if action == "delivered":
+ # Mark all moves are delivered
+ for move in sale_order.mapped(
+ "picking_ids.move_ids_without_package"):
+ move.quantity_done = move.product_uom_qty
+ sale_order.mapped("picking_ids").button_validate()
+
+ return {
+ "sale_order_id": sale_order.id,
+ }
diff --git a/pos_order_to_sale_order/models/sale_order_line.py b/pos_order_to_sale_order/models/sale_order_line.py
new file mode 100644
index 00000000..aa65e6c4
--- /dev/null
+++ b/pos_order_to_sale_order/models/sale_order_line.py
@@ -0,0 +1,23 @@
+# 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 odoo import api, models
+
+
+class SaleOrderLine(models.Model):
+ _inherit = "sale.order.line"
+
+ @api.model
+ def _prepare_from_pos(self, sale_order, order_line_data):
+ ProductProduct = self.env["product.product"]
+ product = ProductProduct.browse(order_line_data["product_id"])
+ return {
+ "order_id": sale_order.id,
+ "product_id": order_line_data["product_id"],
+ "name": product.name,
+ "product_uom_qty": order_line_data["qty"],
+ "discount": order_line_data["discount"],
+ "price_unit": order_line_data["price_unit"],
+ "tax_id": order_line_data["tax_ids"],
+ }
diff --git a/pos_order_to_sale_order/readme/CONFIGURE.rst b/pos_order_to_sale_order/readme/CONFIGURE.rst
new file mode 100644
index 00000000..1b4c3c63
--- /dev/null
+++ b/pos_order_to_sale_order/readme/CONFIGURE.rst
@@ -0,0 +1,6 @@
+* Go to Point Of Sale / Configuration / Point of Sale
+* Check the box 'Create Sale Orders'
+* Select the desired default behaviour
+
+.. figure:: ../static/description/pos_config_form.png
+ :width: 800 px
diff --git a/pos_order_to_sale_order/readme/CONTRIBUTORS.rst b/pos_order_to_sale_order/readme/CONTRIBUTORS.rst
new file mode 100644
index 00000000..e1525ce0
--- /dev/null
+++ b/pos_order_to_sale_order/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Sylvain LE GAL (https://www.twitter.com/legalsylvain)
diff --git a/pos_order_to_sale_order/readme/DESCRIPTION.rst b/pos_order_to_sale_order/readme/DESCRIPTION.rst
new file mode 100644
index 00000000..aca0573c
--- /dev/null
+++ b/pos_order_to_sale_order/readme/DESCRIPTION.rst
@@ -0,0 +1,22 @@
+This module extends the functionality of point of sale to allow sale orders
+creation from the Point of Sale.
+
+In the POS UI, button has been added to create a sale order and discard
+the current POS order.
+
+This module is usefull in many cases, for exemple :
+
+* take orders with a very simple interface
+
+* if you have some customers that come every day in your shop, but want to
+ have a unique invoice at the end of the month. With that module, you can
+ create a sale order and deliver products every time to keep your stock value
+ correct, and to create a unique invoice, when you want.
+
+**Technical Notes**
+
+* Some hooks are defined in the JS file, to define custom behaviour after
+ having created the sale order.
+
+* Some prepare functions are available in the ``sale.order`` model and
+ ``sale.order.line`` models to overload the creation of the sale order.
diff --git a/pos_order_to_sale_order/readme/USAGE.rst b/pos_order_to_sale_order/readme/USAGE.rst
new file mode 100644
index 00000000..ef015587
--- /dev/null
+++ b/pos_order_to_sale_order/readme/USAGE.rst
@@ -0,0 +1,23 @@
+* Open your Point of sale
+* create a new order and select products
+* select a customer
+
+* then, click on the "Create Sale Order" button
+
+.. figure:: ../static/description/pos_frontend_order.png
+ :width: 800 px
+
+Three options are available:
+
+* **Create a draft Order**
+ A new sale order in a draft mode will be created that can be changed later.
+
+* **Create a Confirmed Order**
+ A new sale order will be created and confirmed.
+
+* **Create Delivered Picking** (by default)
+ A new sale order will be created and confirmed. the associated picking
+ will be marked as delivered.
+
+.. figure:: ../static/description/pos_frontend_buttons.png
+ :width: 800 px
diff --git a/pos_order_to_sale_order/static/description/icon.png b/pos_order_to_sale_order/static/description/icon.png
new file mode 100644
index 00000000..d547d9cb
Binary files /dev/null and b/pos_order_to_sale_order/static/description/icon.png differ
diff --git a/pos_order_to_sale_order/static/description/index.html b/pos_order_to_sale_order/static/description/index.html
new file mode 100644
index 00000000..1c635449
--- /dev/null
+++ b/pos_order_to_sale_order/static/description/index.html
@@ -0,0 +1,496 @@
+
+
+
+
+
+
+PoS Order To Sale Order
+
+
+
+
+
PoS Order To Sale Order
+
+
+
![Try me on Runbot](https://img.shields.io/badge/runbot-Try%20me-875A7B.png)
+
This module extends the functionality of point of sale to allow sale orders
+creation from the Point of Sale.
+
In the POS UI, buttons has been added to create a sale order and discard
+the current POS order.
+
This module is usefull in many cases, for exemple :
+
+- take orders with a very simple interface
+- if you have some customers that come every day in your shop, but want to
+have a unique invoice at the end of the month. With that module, you can
+create a sale order and deliver products every time to keep your stock value
+correct, and to create a unique invoice, when you want.
+
+
Three options are available:
+
+- Create a draft Order
+A new sale order in a draft mode will be created that can be changed later.
+
+
+
+- Create a Confirmed Order
+A new sale order will be created and confirmed.
+
+
+
+- Create Delivered Picking (by default)
+A new sale order will be created and confirmed. the associated picking
+will be marked as delivered.
+
+
+
Technical Notes
+
+- Some hooks are defined in the JS file, to define custom behaviour after
+having created the sale order (and the stock picking).
+- Some prepare functions are available in the sale.order model, to overload
+the creation of the sale order.
+- You could be interested by another module, pos_sale_order, that completely
+alter Point of Sale module, avoiding creating Pos Orders, and creating
+allways Sale Orders.
+This module is a WIP state, and is available here:
+https://github.com/OCA/pos/pull/35
+
+
Table of contents
+
+
+
+
To configure this module, you need to:
+
+- Go to Point Of Sale / Configuration / Point of Sale
+- Check the box ‘Create Sale Orders’
+- Select the desired default behaviour
+
+
+
+
+
+
+- Because of the poor design of the Odoo Point of Sale, some basic features
+are not available by default, like pricelist, fiscal position, etc …
+For that reason, unit price will be recomputed by default, when creating the
+sale order, and the unit price of the current bill will not be used.
+
+
Note that this problem is fixed if pos_pricelist is installed.
+(same repository) In that cases, the pricelist, the unit prices and the taxes
+will be the same in the order, as in the displayed bill.
+
+
+
+
+
Bugs are tracked on GitHub 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.
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+
+
This module is maintained by the OCA.
+
![Odoo Community Association](https://odoo-community.org/logo.png)
+
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.
+
Current maintainer:
+
![legalsylvain](https://github.com/legalsylvain.png?size=40px)
+
This module is part of the OCA/pos project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
+
+
+
+
+
diff --git a/pos_order_to_sale_order/static/description/pos_config_form.png b/pos_order_to_sale_order/static/description/pos_config_form.png
new file mode 100644
index 00000000..c108942f
Binary files /dev/null and b/pos_order_to_sale_order/static/description/pos_config_form.png differ
diff --git a/pos_order_to_sale_order/static/description/pos_frontend_buttons.png b/pos_order_to_sale_order/static/description/pos_frontend_buttons.png
new file mode 100644
index 00000000..fa0d9f08
Binary files /dev/null and b/pos_order_to_sale_order/static/description/pos_frontend_buttons.png differ
diff --git a/pos_order_to_sale_order/static/description/pos_frontend_order.png b/pos_order_to_sale_order/static/description/pos_frontend_order.png
new file mode 100644
index 00000000..fdbe05a8
Binary files /dev/null and b/pos_order_to_sale_order/static/description/pos_frontend_order.png differ
diff --git a/pos_order_to_sale_order/static/src/js/screens.js b/pos_order_to_sale_order/static/src/js/screens.js
new file mode 100644
index 00000000..98b84fa6
--- /dev/null
+++ b/pos_order_to_sale_order/static/src/js/screens.js
@@ -0,0 +1,161 @@
+/** ***************************************************************************
+ 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_order_to_sale_order.screens', function (require) {
+ "use strict";
+
+ var screens = require('point_of_sale.screens');
+ var gui = require('point_of_sale.gui');
+ var core = require('web.core');
+ var framework = require('web.framework');
+ var rpc = require('web.rpc');
+ var _t = core._t;
+
+ /** **********************************************************************
+ New Widget CreateSaleOrderButtonWidget:
+ * On click, display a new screen to select the action to do
+ */
+ var CreateSaleOrderButtonWidget = screens.ActionButtonWidget.extend({
+ template: 'CreateSaleOrderButtonWidget',
+
+ button_click: function () {
+ if (this.pos.get_order().get_client()) {
+ this.gui.show_screen('create_sale_order');
+ } else {
+ this.gui.show_popup('error', {
+ 'title': _t('No customer defined'),
+ 'body': _t(
+ 'You should select a customer in order to create' +
+ ' a Sale Order.'),
+ });
+ }
+ },
+
+ is_visible: function () {
+ return this.pos.get_order().orderlines.length > 0;
+ },
+
+ });
+
+ screens.define_action_button({
+ 'name': 'create_sale_order',
+ 'widget': CreateSaleOrderButtonWidget,
+ 'condition': function () {
+ return this.pos.config.iface_create_sale_order;
+ },
+ });
+
+ screens.OrderWidget.include({
+ update_summary: function () {
+ this._super();
+ if (this.getParent().action_buttons &&
+ this.getParent().action_buttons.create_sale_order) {
+ this.getParent().action_buttons.create_sale_order.renderElement();
+ }
+ },
+ });
+
+ /** **********************************************************************
+ New ScreenWidget CreateSaleOrderScreenWidget:
+ * On show, display all buttons, depending on the pos configuration
+ */
+ var CreateSaleOrderScreenWidget = screens.ScreenWidget.extend({
+ template: 'CreateSaleOrderScreenWidget',
+ auto_back: true,
+
+
+ show: function(){
+ var self = this;
+ this._super();
+
+ this.renderElement();
+
+ this.$('.back').click(function(){
+ self.gui.back();
+ });
+
+ if (!this.pos.config.iface_create_draft_sale_order) {
+ this.$('#button-create-draft-order').addClass('oe_hidden');
+ }
+ if (!this.pos.config.iface_create_confirmed_sale_order) {
+ this.$('#button-create-confirmed-order').addClass('oe_hidden');
+ }
+ if (!this.pos.config.iface_create_delivered_sale_order) {
+ this.$('#button-create-delivered-order').addClass('oe_hidden');
+ }
+
+ this.$('.paymentmethod').click(function(event){
+ self.click_sale_order_button(event.currentTarget.attributes.action.nodeValue);
+ });
+ },
+
+ click_sale_order_button: function(action) {
+ var self = this;
+ this.gui.show_popup('confirm', {
+ 'title': _t(
+ 'Create Sale Order and discard the current' +
+ ' PoS Order?'),
+ 'body': _t(
+ "This operation will permanently discard the current PoS" +
+ " Order and create a Sale Order, based on the" +
+ " current order lines."),
+ confirm: function(){
+ framework.blockUI();
+ rpc.query({
+ model: 'sale.order',
+ method: 'create_order_from_pos',
+ args: [self.pos.get('selectedOrder').export_as_JSON(), action],
+ }).then(function (result) {
+ self.hook_create_sale_order_success(result);
+ }).fail(function (error, event) {
+ self.hook_create_sale_order_error(error, event);
+ });
+ },
+ });
+ },
+
+ /**
+ * Overloadable function to make custom action after Sale order
+ * Creation succeeded
+ */
+ hook_create_sale_order_success: function (result) {
+ framework.unblockUI();
+ this.pos.get('selectedOrder').destroy();
+ },
+
+ /**
+ * Overloadable function to make custom action after Sale order
+ * Creation failed
+ */
+ hook_create_sale_order_error: function (error, event) {
+ framework.unblockUI();
+ event.preventDefault();
+ if (error.code === 200) {
+ // Business Logic Error, not a connection problem
+ this.gui.show_popup('error-traceback', {
+ 'title': error.data.message,
+ 'body': error.data.debug,
+ });
+ } else {
+ // Connexion problem
+ this.gui.show_popup('error', {
+ 'title': _t('The order could not be sent'),
+ 'body': _t('Check your internet connection and try again.'),
+ });
+ }
+ },
+
+ });
+
+ gui.define_screen({
+ 'name': 'create_sale_order',
+ 'widget': CreateSaleOrderScreenWidget,
+ 'condition': function () {
+ return this.pos.config.iface_create_sale_order;
+ },
+ });
+
+});
diff --git a/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml b/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml
new file mode 100644
index 00000000..72493d7e
--- /dev/null
+++ b/pos_order_to_sale_order/static/src/xml/pos_order_to_sale_order.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ Create Sale Order
+
+
+
+
+
+
+
+
+
+ Back
+
+
Create Sale Order
+
+
+
+
+ Create Draft Sale Order
+
+ Create Confirmed Sale Order
+
+ Create Delivered Sale Order
+
+
+
+
+
+
+
diff --git a/pos_order_to_sale_order/views/assets.xml b/pos_order_to_sale_order/views/assets.xml
new file mode 100644
index 00000000..c8420256
--- /dev/null
+++ b/pos_order_to_sale_order/views/assets.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pos_order_to_sale_order/views/view_pos_config.xml b/pos_order_to_sale_order/views/view_pos_config.xml
new file mode 100644
index 00000000..da0dfdeb
--- /dev/null
+++ b/pos_order_to_sale_order/views/view_pos_config.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ pos.config
+
+
+
+
+
+ Sale Order Creations
+
+
+
+
+