Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions pos_sale_picking_keep/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

===========================
Keep sale pickings from PoS
===========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:14008524ac2e970ae97eda09273f81e2fc25b02a6ccf008e85f4473f3d637815
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github
:target: https://github.com/OCA/pos/tree/19.0/pos_sale_picking_keep
:alt: OCA/pos
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/pos-19-0/pos-19-0-pos_sale_picking_keep
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/pos&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module inhibits the manipulation that the point of sale mades over
the sales orders pickings, and the creation of new pickings under the
PoS picking type.

When settling a sale order in the PoS, the ordered quantities are loaded
even if the products were already delivered through the sale order
pickings, deducting only the quantities already invoiced. Without this
module, the PoS deducts the delivered quantities, which makes no sense
here, as the PoS is only used to charge the order, not to deliver it.

**Table of contents**

.. contents::
:local:

Use Cases / Context
===================

In some scenarios, you may not want that the point of sale (PoS) handles
the pickings of the products you are paying:

- Using the PoS as a pure payment terminal.
- When complex stock flows are not supplied by the PoS.

In that cases, it's better to keep the original sales pickings.

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 to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/pos/issues/new?body=module:%20pos_sale_picking_keep%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Tecnativa

Contributors
------------

- Tecnativa:

- Pedro M. Baeza
- Víctor Martínez

- Jarsa:

- Jesús Alan Ramos Rodríguez <alan.ramos@jarsa.com>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-pedrobaeza| image:: https://github.com/pedrobaeza.png?size=40px
:target: https://github.com/pedrobaeza
:alt: pedrobaeza

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-pedrobaeza|

This module is part of the `OCA/pos <https://github.com/OCA/pos/tree/19.0/pos_sale_picking_keep>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions pos_sale_picking_keep/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import models
21 changes: 21 additions & 0 deletions pos_sale_picking_keep/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2025 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Keep sale pickings from PoS",
"version": "19.0.1.1.0",
"category": "Point Of Sale",
"website": "https://github.com/OCA/pos",
"author": "Tecnativa, Odoo Community Association (OCA)",
"maintainers": ["pedrobaeza"],
"license": "AGPL-3",
"installable": True,
"depends": ["pos_sale", "sale_stock"],
"assets": {
"point_of_sale._assets_pos": [
"pos_sale_picking_keep/static/src/js/**/*",
],
"web.assets_tests": [
"pos_sale_picking_keep/static/tests/tours/**/*",
],
},
}
32 changes: 32 additions & 0 deletions pos_sale_picking_keep/i18n/it.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_sale_picking_keep
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-03-13 16:45+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\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"
"X-Generator: Weblate 5.15.2\n"

#. module: pos_sale_picking_keep
#: model:ir.model,name:pos_sale_picking_keep.model_pos_order
msgid "Point of Sale Orders"
msgstr "Ordini punto vendita"

#. module: pos_sale_picking_keep
#: model:ir.model,name:pos_sale_picking_keep.model_pos_session
msgid "Point of Sale Session"
msgstr "Sessione punto vendita"

#. module: pos_sale_picking_keep
#: model:ir.model,name:pos_sale_picking_keep.model_sale_order_line
msgid "Sales Order Line"
msgstr "Riga ordine di vendita"
29 changes: 29 additions & 0 deletions pos_sale_picking_keep/i18n/pos_sale_picking_keep.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_sale_picking_keep
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0\n"
"Report-Msgid-Bugs-To: \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_sale_picking_keep
#: model:ir.model,name:pos_sale_picking_keep.model_pos_order
msgid "Point of Sale Orders"
msgstr ""

#. module: pos_sale_picking_keep
#: model:ir.model,name:pos_sale_picking_keep.model_pos_session
msgid "Point of Sale Session"
msgstr ""

#. module: pos_sale_picking_keep
#: model:ir.model,name:pos_sale_picking_keep.model_sale_order_line
msgid "Sales Order Line"
msgstr ""
4 changes: 4 additions & 0 deletions pos_sale_picking_keep/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import pos_order
from . import pos_session
from . import sale_order_line
47 changes: 47 additions & 0 deletions pos_sale_picking_keep/models/pos_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2025 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, models
from odoo.tools import config


class PosOrder(models.Model):
_inherit = "pos.order"

@api.model
def sync_from_ui(self, orders):
# Avoid the cancellation of the SO pickings
so_line_ids = []
for order_data in orders:
for command in order_data.get("lines", []):
if len(command) != 3:
continue # No create/update command
so_line_id = command[2].get("sale_order_line_id")
if so_line_id:
so_line_ids.append(so_line_id)
so_lines = self.env["sale.order.line"].browse(so_line_ids)
# confirm the unconfirmed sale orders that are linked to the sale order lines
# this is done also upstream, but we need to do it first for having already
# the pickings to make the trick
sale_orders = so_lines.order_id
for sale_order in sale_orders.filtered(lambda x: x.state in ["draft", "sent"]):
sale_order.action_confirm()
# Fake the pickings state before calling super for avoiding the move quantity
# reduction that is done upstream that effectively cancels the SO pickings
pickings = so_lines.move_ids.picking_id
pickings.state = "draft"
res = super().sync_from_ui(orders)
pickings._compute_state()
return res

def _create_order_picking(self):
# Nullify the creation of the pickings at this level
# We cannot use self.env.context.get("test_pos_sale_picking_keep") because
# the tours that run in the tests do not allow that context to be maintained.
# Therefore, we use self.config_id.name.
if (
config["test_enable"]
and self.config_id.name != "test_pos_sale_picking_keep"
):
# For not breaking tests of other modules
return super()._create_order_picking()
return True
21 changes: 21 additions & 0 deletions pos_sale_picking_keep/models/pos_session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2025 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
from odoo.tools import config


class PosSession(models.Model):
_inherit = "pos.session"

def _create_picking_at_end_of_session(self):
# Nullify the creation of the pickings at this level
# We cannot use self.env.context.get("test_pos_sale_picking_keep") because
# the tours that run in the tests do not allow that context to be maintained.
# Therefore, we use self.config_id.name.
if (
config["test_enable"]
and self.config_id.name != "test_pos_sale_picking_keep"
):
# For not breaking tests of other modules
return super()._create_picking_at_end_of_session()
return True
44 changes: 44 additions & 0 deletions pos_sale_picking_keep/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2026 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, models


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

# TODO: Delete if merged https://github.com/odoo/odoo/pull/253333
def _compute_qty_delivered(self):
self = self.with_context(from_qty_delivered=True)
return super()._compute_qty_delivered()

# TODO: Delete if merged https://github.com/odoo/odoo/pull/253333
@api.model
def _convert_qty(self, sale_line, qty, direction):
if self.env.context.get("from_qty_delivered"):
return 0
return super()._convert_qty(sale_line=sale_line, qty=qty, direction=direction)

def read_converted(self):
results = super().read_converted()
lines = {line.id: line for line in self}
for item in results:
line = lines.get(item.get("id"))
if not line or line.product_id.type == "service":
continue
product_uom = line.product_id.uom_id
if product_uom == line.product_uom_id:
continue
# The PoS charges product_uom_qty - qty_invoiced (see the JS
# override of setQuantityFromSOL) and rounds it with the Product
# Unit precision. When the quantity converted to the product UoM
# is not representable with that precision (e.g. 4 Units of a
# pack of 150 = 0.0267 packs, rounded to 0.03 by the PoS), the
# rounding would change the charged amount. Compensate on the
# price so rounded qty * price matches the sale order line
# remaining amount.
qty = item["product_uom_qty"] - item["qty_invoiced"]
qty_rounded = product_uom.round(qty)
if product_uom.is_zero(qty_rounded):
continue
item["price_unit"] = item["price_unit"] * qty / qty_rounded
return results
3 changes: 3 additions & 0 deletions pos_sale_picking_keep/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
7 changes: 7 additions & 0 deletions pos_sale_picking_keep/readme/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
In some scenarios, you may not want that the point of sale (PoS) handles the pickings
of the products you are paying:

- Using the PoS as a pure payment terminal.
- When complex stock flows are not supplied by the PoS.

In that cases, it's better to keep the original sales pickings.
6 changes: 6 additions & 0 deletions pos_sale_picking_keep/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Tecnativa:
- Pedro M. Baeza
- Víctor Martínez

- Jarsa:
- Jesús Alan Ramos Rodríguez \<alan.ramos@jarsa.com\>
8 changes: 8 additions & 0 deletions pos_sale_picking_keep/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
This module inhibits the manipulation that the point of sale mades over the sales orders
pickings, and the creation of new pickings under the PoS picking type.

When settling a sale order in the PoS, the ordered quantities are loaded
even if the products were already delivered through the sale order pickings,
deducting only the quantities already invoiced. Without this module, the PoS
deducts the delivered quantities, which makes no sense here, as the PoS is
only used to charge the order, not to deliver it.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading