Merge PR #1207 into 13.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2021-08-20 06:28:43 +00:00
23 changed files with 2228 additions and 0 deletions

View File

@@ -0,0 +1 @@
../../../../stock_reserve_sale

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View File

@@ -0,0 +1,114 @@
===================
Stock Reserve Sales
===================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github
:target: https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_reserve_sale
:alt: OCA/stock-logistics-warehouse
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-0-stock_reserve_sale
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/153/13.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
Allows to create stock reservations for quotation lines before the
confirmation of the quotation. The reservations might have a validity
date and in any case they are lifted when the quotation is canceled or
confirmed.
Reservations can be done only on "make to stock" and stockable products.
The reserved products are subtracted from the virtual stock. It means
that if you reserved a quantity of products which bring the virtual
stock below the minimum, the orderpoint will be triggered and new
purchase orders will be generated. It also implies that the max may be
exceeded if the reservations are canceled.
If you want to prevent sales orders to be confirmed when the stock is
insufficient at the order date, you may want to install the
`sale_exception_nostock` module.
Additionally, if the sale_owner_stock_sourcing module is installed, the owner
specified on the sale order line will be proposed as owner of the reservation.
If you try to make a reservation for an order whose lines have different, you
will get a message suggesting to reserve each line individually. There is no
module dependency: this modules is fully functional even without ownership
management.
**Table of contents**
.. contents::
:local:
Usage
=====
#. Create a new Sale Order
#. Add lines to the Order
Now you can reserve all the lines by clicking on *Reserve Stock* or you can reserve each
one by pressing on the lock icon at the lines.
Once they where reserved, you can release the reserves by clicking at *Cancell all*
button or by clicking the undo icon on the lines.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-warehouse/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_reserve_sale%0Aversion:%2013.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
~~~~~~~
* Camptocamp
Contributors
~~~~~~~~~~~~
* Leonardo Pistone <leonardo.pistone@camptocamp.com>
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* `Tecnativa <https://www.tecnativa.com>`_:
* Carlos Roca
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_reserve_sale>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,4 @@
# Copyright 2013 Camptocamp SA - Guewen Baconnier
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import model
from . import wizard

View File

@@ -0,0 +1,19 @@
# Copyright 2013 Camptocamp SA - Guewen Baconnier
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Stock Reserve Sales",
"version": "13.0.1.0.0",
"author": "Camptocamp, Odoo Community Association (OCA)",
"category": "Warehouse",
"license": "AGPL-3",
"complexity": "normal",
"website": "https://github.com/stock-logistics-warehouse",
"depends": ["sale_stock", "stock_reserve"],
"data": [
"wizard/sale_stock_reserve_view.xml",
"view/sale.xml",
"view/stock_reserve.xml",
],
"installable": True,
"auto_install": False,
}

View File

@@ -0,0 +1,237 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_reserve_sale
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: stock-logistics-warehouse (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-27 07:47+0000\n"
"PO-Revision-Date: 2021-07-27 09:58+0200\n"
"Last-Translator: Carlos <carlos.roca@tecnativa.com>\n"
"Language-Team: Spanish (http://www.transifex.com/oca/OCA-stock-logistics-"
"warehouse-8-0/language/es/)\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.6\n"
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid ""
"A stock reservation will be created for the products\n"
" of the selected quotation lines. If a validity date is "
"specified,\n"
" the reservation will be released once the date has "
"passed."
msgstr ""
"Una reserva de stock va a ser creada para los productos\n"
" de la orden de venta seleccionada. Si una fecha de "
"validez está\n"
"\t\t especificada, la reserva va a ser liberada cuando la fecha pase."
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_order__is_stock_reservable
msgid "Can Have Stock Reservations"
msgstr "Puede tener reservas de stock"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_order_line__is_stock_reservable
msgid "Can be reserved"
msgstr "Puede ser reservado"
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Cancel"
msgstr "Cancelar"
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_order_form_reserve
msgid "Cancell all"
msgstr "Cancelar todo"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__create_date
msgid "Created on"
msgstr "Creado el"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__display_name
msgid "Display Name"
msgstr "Nombre mostrado"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:0
#, python-format
msgid "Error"
msgstr "Error"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_order__has_stock_reservation
msgid "Has Stock Reservations"
msgstr "Tiene reservas de stock"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__id
msgid "ID"
msgstr "ID"
#. module: stock_reserve_sale
#: model:ir.model.fields,help:stock_reserve_sale.field_sale_stock_reserve__date_validity
msgid ""
"If a date is given, the reservations will be released at the end of the "
"validity."
msgstr ""
"Si la fecha de validez está asignada, la reserva va a ser liberada al final "
"de esta."
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve____last_update
msgid "Last Modified on"
msgstr "Última modificación el"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__write_uid
msgid "Last Updated by"
msgstr "Actualizado por última vez por"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__write_date
msgid "Last Updated on"
msgstr "Actualizado por última vez el"
#. module: stock_reserve_sale
#: model:ir.model.fields,help:stock_reserve_sale.field_sale_stock_reserve__location_dest_id
msgid "Location where the system will reserve the products."
msgstr "Localización donde el sistema va a reservar los productos."
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__note
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Notes"
msgstr "Notas"
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_order_form_reserve
msgid "Pre-book products from stock"
msgstr "Pre-reservar productos en stock"
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_order_form_reserve
msgid "Release Reservation"
msgstr "Liberar"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__location_dest_id
msgid "Reservation Location"
msgstr "Localización de la reserva"
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Reserve"
msgstr "Reservar"
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_order_form_reserve
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Reserve Stock"
msgstr "Reservar stock"
#. module: stock_reserve_sale
#: model:ir.actions.act_window,name:stock_reserve_sale.action_sale_stock_reserve
msgid "Reserve Stock for Quotation Lines"
msgstr "Reservar stock para las lineas del pedido"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_stock_reservation__sale_id
msgid "Sale Order"
msgstr "Pedido de venta"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_stock_reservation__sale_line_id
msgid "Sale Order Line"
msgstr "Linea de venta"
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_stock_reservation_form
msgid "Sales"
msgstr "Ventas"
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_order
msgid "Sales Order"
msgstr "Pedido"
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_order_line
msgid "Sales Order Line"
msgstr "Líneas de pedidos de venta"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:0
#, python-format
msgid ""
"Several stock reservations are linked with the line. Impossible to adjust "
"their quantity. Please release the reservation before changing the quantity."
msgstr ""
"Algunas reservas de stock están asociadas a la linea. Imposible ajustar la "
"cantidad. Por favor, libera la reserva antes de cambiar la cantidad."
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__location_id
msgid "Source Location"
msgstr "Ubicación origen"
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__owner_id
msgid "Stock Owner"
msgstr "Propietario"
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_stock_reservation
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_order_line__reservation_ids
msgid "Stock Reservation"
msgstr "Reserva de existencias"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/wizard/sale_stock_reserve.py:0
#, python-format
msgid ""
"The lines have different owners. Please reserve them\n"
" individually with the reserve button on each one."
msgstr ""
"Las lineas tienen distintos propietarios. Por favor, reservalas\n"
" individualmente con el botón de reserva de cada una."
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__date_validity
msgid "Validity Date"
msgstr "Fecha de validez"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:0
#, python-format
msgid ""
"You cannot change the product or unit of measure of lines with a stock "
"reservation. Release the reservation before changing the product."
msgstr ""
"No puedes cambiar el producto o la unidad de medida de las lineas con una "
"reserva de stock. Libera la reserva antes de cambiar el producto."
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "or"
msgstr "o"
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_stock_reserve
msgid "sale.stock.reserve"
msgstr ""

View File

@@ -0,0 +1,211 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_reserve_sale
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-04-23 15:55+0000\n"
"PO-Revision-Date: 2015-04-23 20:35+0200\n"
"Last-Translator: Miku Laitinen <miku.laitinen@ninjaware.fi>\n"
"Language-Team: Avoin.Systems <miku@avoin.systems>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.4\n"
"Language: Finnish\n"
"X-Poedit-SourceCharset: UTF-8\n"
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid ""
"A stock reservation will be created for the products\n"
" of the selected quotation lines. If a validity date "
"is specified,\n"
" the reservation will be released once the date has "
"passed."
msgstr ""
"Tuotevaraus luodaan valittujen rivien tuotteille.\n"
"Jos vapautuspäivä on määritetty, varaus peruutetaan automaattisesti "
"kyseisenä päivänä."
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:179
#, python-format
msgid ""
"As you changed the quantity of the line, the quantity of the stock "
"reservation will be automatically adjusted to %.2f."
msgstr ""
"Muuttaessasi rivin kappalemäärää, tuotevarauksen kappalemäärä päivitetään "
"automaattisesti arvoon %.2f."
#. module: stock_reserve_sale
#: field:sale.order,is_stock_reservable:0
msgid "Can Have Stock Reservations"
msgstr "Voi sisältää tuotevarauksia"
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Cancel"
msgstr "Peruuta"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:188
#, python-format
msgid "Configuration Error!"
msgstr "Konfiguraatiovirhe."
#. module: stock_reserve_sale
#: field:sale.stock.reserve,create_uid:0
msgid "Created by"
msgstr "Luonut"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,create_date:0
msgid "Created on"
msgstr "Luotu"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:210
#: code:addons/stock_reserve_sale/model/sale.py:222
#, python-format
msgid "Error"
msgstr "Virhe"
#. module: stock_reserve_sale
#: field:sale.order,has_stock_reservation:0
msgid "Has Stock Reservations"
msgstr "Sisältää tuotevarauksia"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,id:0
msgid "ID"
msgstr "ID"
#. module: stock_reserve_sale
#: help:sale.stock.reserve,date_validity:0
msgid ""
"If a date is given, the reservations will be released at the end of the "
"validity."
msgstr ""
"Jos vapautuspäivä on määritetty, varaukset perutaan kyseisenä päivänä "
"automaattisesti."
#. module: stock_reserve_sale
#: field:sale.stock.reserve,write_uid:0
msgid "Last Updated by"
msgstr "Viimeksi päivittänyt"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,write_date:0
msgid "Last Updated on"
msgstr "Viimeksi päivitetty"
#. module: stock_reserve_sale
#: help:sale.stock.reserve,location_dest_id:0
msgid "Location where the system will reserve the products."
msgstr "Sijainti, jonne tuotteet varataan."
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
#: field:sale.stock.reserve,note:0
msgid "Notes"
msgstr "Huomautukset"
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
msgid "Pre-book products from stock"
msgstr "Varaa tuotteita varastosta"
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
msgid "Release Reservation"
msgstr "Peru varaus"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,location_dest_id:0
msgid "Reservation Location"
msgstr "Varauspaikka"
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Reserve"
msgstr "Varaa"
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Reserve Stock"
msgstr "Varaa tuotteet"
#. module: stock_reserve_sale
#: model:ir.actions.act_window,name:stock_reserve_sale.action_sale_stock_reserve
msgid "Reserve Stock for Quotation Lines"
msgstr "Rivien tuotteiden varaus"
#. module: stock_reserve_sale
#: field:stock.reservation,sale_line_id:0
msgid "Sale Order Line"
msgstr "Myyntitilausrivi"
#. module: stock_reserve_sale
#: view:stock.reservation:stock_reserve_sale.view_stock_reservation_form
msgid "Sales"
msgstr "Myynti"
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_order
msgid "Sales Order"
msgstr "Myyntitilaus"
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_order_line
msgid "Sales Order Line"
msgstr "Myyntitilausrivi"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:223
#, python-format
msgid ""
"Several stock reservations are linked with the line. Impossible to adjust "
"their quantity. Please release the reservation before changing the quantity."
msgstr ""
"Riviin on linkitetty useita tuotevarauksia. Kappalemäärien muokkaaminen ei "
"ole mahdollista. Ole hyvä ja peruuta varaus ennen määrien muokkaamista."
#. module: stock_reserve_sale
#: field:sale.stock.reserve,location_id:0
msgid "Source Location"
msgstr "Lähtöpaikka"
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_stock_reservation
#: field:sale.order.line,reservation_ids:0
msgid "Stock Reservation"
msgstr "Tuotevaraus"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,date_validity:0
msgid "Validity Date"
msgstr "Vapautuspäivä"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:211
#, python-format
msgid ""
"You cannot change the product or unit of measure of lines with a stock "
"reservation. Release the reservation before changing the product."
msgstr ""
"Et voi vaihtaa tuotetta tai tuotteen yksikköä, jos rivillä on tuotevaraus. "
"Peruuta varaus ennen tuotteen vaihtamista."
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
msgid "cancel all"
msgstr "peru kaikki"
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
msgid "{\"reload_on_button\": 1}"
msgstr "{\"reload_on_button\": 1}"

View File

@@ -0,0 +1,209 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_reserve_sale
#
# Translators:
# Pierre Verkest <pverkest@anybox.fr>, 2015
msgid ""
msgstr ""
"Project-Id-Version: stock-logistics-warehouse (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-09-23 20:23+0000\n"
"PO-Revision-Date: 2015-09-27 21:09+0000\n"
"Last-Translator: Pierre Verkest <pverkest@anybox.fr>\n"
"Language-Team: French (http://www.transifex.com/oca/OCA-stock-logistics-warehouse-8-0/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid ""
"A stock reservation will be created for the products\n"
" of the selected quotation lines. If a validity date is specified,\n"
" the reservation will be released once the date has passed."
msgstr "Une réservation de stock sera créée pour les produits\ndes lignes de devis sélectionnées. Si une date de validité est\nspécifiée, la réservation sera annulée une fois cette date dépassée."
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:179
#, python-format
msgid ""
"As you changed the quantity of the line, the quantity of the stock "
"reservation will be automatically adjusted to %.2f."
msgstr "Suite au changement de quantité de la ligne, la quantité de stock réservée va être ajusté à %.2f."
#. module: stock_reserve_sale
#: field:sale.order,is_stock_reservable:0
msgid "Can Have Stock Reservations"
msgstr ""
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Cancel"
msgstr "Annuler"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:188
#, python-format
msgid "Configuration Error!"
msgstr "Erreur de configuration!"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,create_uid:0
msgid "Created by"
msgstr "Créé par"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,create_date:0
msgid "Created on"
msgstr "Créé le"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:210
#: code:addons/stock_reserve_sale/model/sale.py:222
#, python-format
msgid "Error"
msgstr "Erreur"
#. module: stock_reserve_sale
#: field:sale.order,has_stock_reservation:0
msgid "Has Stock Reservations"
msgstr ""
#. module: stock_reserve_sale
#: field:sale.stock.reserve,id:0
msgid "ID"
msgstr "ID"
#. module: stock_reserve_sale
#: help:sale.stock.reserve,date_validity:0
msgid ""
"If a date is given, the reservations will be released at the end of the "
"validity."
msgstr "Si une date est donnée, la réservation sera annulée à la fin de cette date de validité."
#. module: stock_reserve_sale
#: field:sale.stock.reserve,write_uid:0
msgid "Last Updated by"
msgstr "Dernière màj par"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,write_date:0
msgid "Last Updated on"
msgstr "Dernière màj le"
#. module: stock_reserve_sale
#: help:sale.stock.reserve,location_dest_id:0
msgid "Location where the system will reserve the products."
msgstr "Emplacement de stock où sont réservés les produits (pour le système)."
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
#: field:sale.stock.reserve,note:0
msgid "Notes"
msgstr "Notes"
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
msgid "Pre-book products from stock"
msgstr ""
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
msgid "Release Reservation"
msgstr ""
#. module: stock_reserve_sale
#: field:sale.stock.reserve,location_dest_id:0
msgid "Reservation Location"
msgstr ""
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Reserve"
msgstr "Réserver"
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Reserve Stock"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.actions.act_window,name:stock_reserve_sale.action_sale_stock_reserve
msgid "Reserve Stock for Quotation Lines"
msgstr ""
#. module: stock_reserve_sale
#: field:stock.reservation,sale_line_id:0
msgid "Sale Order Line"
msgstr ""
#. module: stock_reserve_sale
#: view:stock.reservation:stock_reserve_sale.view_stock_reservation_form
msgid "Sales"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_order
msgid "Sales Order"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_order_line
msgid "Sales Order Line"
msgstr ""
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:223
#, python-format
msgid ""
"Several stock reservations are linked with the line. Impossible to adjust "
"their quantity. Please release the reservation before changing the quantity."
msgstr ""
#. module: stock_reserve_sale
#: field:sale.stock.reserve,location_id:0
msgid "Source Location"
msgstr "Emplacement Source"
#. module: stock_reserve_sale
#: field:sale.stock.reserve,owner_id:0
msgid "Stock Owner"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_stock_reservation
#: field:sale.order.line,reservation_ids:0
msgid "Stock Reservation"
msgstr ""
#. module: stock_reserve_sale
#: field:sale.stock.reserve,date_validity:0
msgid "Validity Date"
msgstr "Date de validité"
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:211
#, python-format
msgid ""
"You cannot change the product or unit of measure of lines with a stock "
"reservation. Release the reservation before changing the product."
msgstr "Vous ne pouvez pas modifier le produit ou l'unité de mesure des lignes avec une réservation en stock. Veuillez annuler les réservations avant de modifier la ligne."
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
msgid "cancel all"
msgstr "tout annuler"
#. module: stock_reserve_sale
#: view:sale.stock.reserve:stock_reserve_sale.view_sale_stock_reserve_form
msgid "or"
msgstr "ou"
#. module: stock_reserve_sale
#: view:sale.order:stock_reserve_sale.view_order_form_reserve
msgid "{\"reload_on_button\": 1}"
msgstr ""

View File

@@ -0,0 +1,220 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_reserve_sale
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-27 07:47+0000\n"
"PO-Revision-Date: 2021-07-27 07:47+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: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid ""
"A stock reservation will be created for the products\n"
" of the selected quotation lines. If a validity date is specified,\n"
" the reservation will be released once the date has passed."
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_order__is_stock_reservable
msgid "Can Have Stock Reservations"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_order_line__is_stock_reservable
msgid "Can be reserved"
msgstr ""
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Cancel"
msgstr ""
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_order_form_reserve
msgid "Cancell all"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__create_uid
msgid "Created by"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__create_date
msgid "Created on"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__display_name
msgid "Display Name"
msgstr ""
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:0
#: code:addons/stock_reserve_sale/model/sale.py:0
#, python-format
msgid "Error"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_order__has_stock_reservation
msgid "Has Stock Reservations"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__id
msgid "ID"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,help:stock_reserve_sale.field_sale_stock_reserve__date_validity
msgid ""
"If a date is given, the reservations will be released at the end of the "
"validity."
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve____last_update
msgid "Last Modified on"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__write_uid
msgid "Last Updated by"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__write_date
msgid "Last Updated on"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,help:stock_reserve_sale.field_sale_stock_reserve__location_dest_id
msgid "Location where the system will reserve the products."
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__note
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Notes"
msgstr ""
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_order_form_reserve
msgid "Pre-book products from stock"
msgstr ""
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_order_form_reserve
msgid "Release Reservation"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__location_dest_id
msgid "Reservation Location"
msgstr ""
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Reserve"
msgstr ""
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_order_form_reserve
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "Reserve Stock"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.actions.act_window,name:stock_reserve_sale.action_sale_stock_reserve
msgid "Reserve Stock for Quotation Lines"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_stock_reservation__sale_id
msgid "Sale Order"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_stock_reservation__sale_line_id
msgid "Sale Order Line"
msgstr ""
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_stock_reservation_form
msgid "Sales"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_order
msgid "Sales Order"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_order_line
msgid "Sales Order Line"
msgstr ""
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:0
#, python-format
msgid ""
"Several stock reservations are linked with the line. Impossible to adjust "
"their quantity. Please release the reservation before changing the quantity."
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__location_id
msgid "Source Location"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__owner_id
msgid "Stock Owner"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_stock_reservation
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_order_line__reservation_ids
msgid "Stock Reservation"
msgstr ""
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/wizard/sale_stock_reserve.py:0
#, python-format
msgid ""
"The lines have different owners. Please reserve them\n"
" individually with the reserve button on each one."
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model.fields,field_description:stock_reserve_sale.field_sale_stock_reserve__date_validity
msgid "Validity Date"
msgstr ""
#. module: stock_reserve_sale
#: code:addons/stock_reserve_sale/model/sale.py:0
#, python-format
msgid ""
"You cannot change the product or unit of measure of lines with a stock "
"reservation. Release the reservation before changing the product."
msgstr ""
#. module: stock_reserve_sale
#: model_terms:ir.ui.view,arch_db:stock_reserve_sale.view_sale_stock_reserve_form
msgid "or"
msgstr ""
#. module: stock_reserve_sale
#: model:ir.model,name:stock_reserve_sale.model_sale_stock_reserve
msgid "sale.stock.reserve"
msgstr ""

View File

@@ -0,0 +1,4 @@
# Copyright 2013 Camptocamp SA - Guewen Baconnier
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import sale
from . import stock_reserve

View File

@@ -0,0 +1,245 @@
# Copyright 2013 Camptocamp SA - Guewen Baconnier
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo.exceptions import UserError, except_orm
from odoo.tools.translate import _
_LINE_KEYS = ["product_id", "product_uom_qty"]
class SaleOrder(models.Model):
_inherit = "sale.order"
@api.depends(
"state", "order_line.reservation_ids", "order_line.is_stock_reservable"
)
def _compute_stock_reservation(self):
for sale in self:
has_stock_reservation = False
is_stock_reservable = False
for line in sale.order_line:
if line.reservation_ids:
has_stock_reservation = True
if line.is_stock_reservable:
is_stock_reservable = True
if sale.state not in ("draft", "sent"):
is_stock_reservable = False
sale.is_stock_reservable = is_stock_reservable
sale.has_stock_reservation = has_stock_reservation
has_stock_reservation = fields.Boolean(
compute="_compute_stock_reservation",
readonly=True,
multi="stock_reservation",
store=True,
string="Has Stock Reservations",
)
is_stock_reservable = fields.Boolean(
compute="_compute_stock_reservation",
readonly=True,
multi="stock_reservation",
store=True,
string="Can Have Stock Reservations",
)
def release_all_stock_reservation(self):
line_ids = [line.id for order in self for line in order.order_line]
lines = self.env["sale.order.line"].browse(line_ids)
lines.release_stock_reservation()
return True
def action_confirm(self):
self.release_all_stock_reservation()
return super().action_confirm()
def action_cancel(self):
self.release_all_stock_reservation()
return super().action_cancel()
def write(self, vals):
old_lines = self.mapped("order_line")
dict_old_lines = {}
for line in old_lines:
dict_old_lines[line.id] = {
"product_id": line.product_id,
"product_uom_qty": line.product_uom_qty,
}
res = super().write(vals)
for order in self:
body = ""
for line in vals.get("order_line", []):
if line[0] == 1 and list(set(line[2].keys()).intersection(_LINE_KEYS)):
body += order.get_message(dict_old_lines.get(line[1]), line[2])
if body != "":
order.message_post(body=body)
return res
@api.model
def get_message(self, old_vals, new_vals):
ProductProduct = self.env["product.product"]
body = _("<p>Modified Order line data</p>")
if "product_id" in new_vals:
old_product = old_vals["product_id"].display_name
new_product = ProductProduct.browse(new_vals["product_id"]).display_name
body += _("<div> <b>Product</b>: ")
body += "{}{}</div>".format(old_product, new_product)
if "product_uom_qty" in new_vals:
if "product_id" not in new_vals:
body += _("<div> <b>Product</b>: %s") % (
old_vals["product_id"].display_name
)
body += _("<div> <b>Product qty.</b>: ")
body += "{}{}</div>".format(
old_vals["product_uom_qty"], float(new_vals["product_uom_qty"]),
)
body += "<br/>"
return body
def unlink(self):
for order in self:
if order.has_stock_reservation:
raise UserError(
_(
"Sale Order %s has some reserved lines.\n"
"Please unreserve this lines before delete the order."
)
% (order.name)
)
return super().unlink()
class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
def _get_line_rule(self):
""" Get applicable rule for this product
Reproduce get suitable rule from procurement
to predict source location """
StockRule = self.env["stock.rule"]
product = self.product_id
product_route_ids = [
x.id for x in product.route_ids + product.categ_id.total_route_ids
]
rules = StockRule.search(
[("route_id", "in", product_route_ids)],
order="route_sequence, sequence",
limit=1,
)
if not rules:
warehouse = self.order_id.warehouse_id
wh_routes = warehouse.route_ids
wh_route_ids = [route.id for route in wh_routes]
domain = [
"|",
("warehouse_id", "=", warehouse.id),
("warehouse_id", "=", False),
("route_id", "in", wh_route_ids),
]
rules = StockRule.search(domain, order="route_sequence, sequence")
if rules:
fields.first(rules)
return False
def _get_procure_method(self):
""" Get procure_method depending on product routes """
rule = self._get_line_rule()
if rule:
return rule.procure_method
return False
@api.depends("state", "product_id.route_ids", "product_id.type")
def _compute_is_stock_reservable(self):
for line in self:
reservable = False
if (
not (
line.state != "draft"
or line._get_procure_method() == "make_to_order"
or not line.product_id
or line.product_id.type == "service"
)
and not line.reservation_ids
):
reservable = True
line.is_stock_reservable = reservable
@api.depends("order_id.state", "reservation_ids")
def _compute_is_readonly(self):
for line in self:
line.is_readonly = (
len(line.reservation_ids) > 0 or line.order_id.state != "draft"
)
reservation_ids = fields.One2many(
"stock.reservation", "sale_line_id", string="Stock Reservation", copy=False
)
is_stock_reservable = fields.Boolean(
compute="_compute_is_stock_reservable", readonly=True, string="Can be reserved"
)
is_readonly = fields.Boolean(compute="_compute_is_readonly", store=False)
def release_stock_reservation(self):
reserv_ids = [reserv.id for line in self for reserv in line.reservation_ids]
reservations = self.env["stock.reservation"].browse(reserv_ids)
reservations.release_reserve()
return True
def write(self, vals):
block_on_reserve = ("product_id", "product_uom", "type")
update_on_reserve = ("price_unit", "product_uom_qty")
keys = set(vals.keys())
test_block = keys.intersection(block_on_reserve)
test_update = keys.intersection(update_on_reserve)
if test_block:
for line in self:
if not line.reservation_ids:
continue
raise except_orm(
_("Error"),
_(
"You cannot change the product or unit of measure "
"of lines with a stock reservation. "
"Release the reservation "
"before changing the product."
),
)
res = super().write(vals)
if test_update:
for line in self:
if not line.reservation_ids:
continue
if len(line.reservation_ids) > 1:
raise except_orm(
_("Error"),
_(
"Several stock reservations are linked with the "
"line. Impossible to adjust their quantity. "
"Please release the reservation "
"before changing the quantity."
),
)
line.reservation_ids.write(
{
"price_unit": line.price_unit,
"product_uom_qty": line.product_uom_qty,
}
)
return res
def unlink(self):
for line in self:
if line.reservation_ids:
raise UserError(
_(
'Sale order line "[%s] %s" has a related reservation.\n'
"Please unreserve this line before "
"delete the line"
)
% (line.order_id.name, line.name)
)
return super().unlink()

View File

@@ -0,0 +1,19 @@
# Copyright 2013 Camptocamp SA - Guewen Baconnier
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class StockReservation(models.Model):
_inherit = "stock.reservation"
sale_line_id = fields.Many2one(
"sale.order.line", string="Sale Order Line", ondelete="cascade", copy=False
)
sale_id = fields.Many2one(
"sale.order", string="Sale Order", related="sale_line_id.order_id"
)
def release_reserve(self):
for rec in self:
rec.sale_line_id = False
return super().release_reserve()

View File

@@ -0,0 +1,8 @@
* Leonardo Pistone <leonardo.pistone@camptocamp.com>
* Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* `Tecnativa <https://www.tecnativa.com>`_:
* Carlos Roca

View File

@@ -0,0 +1,23 @@
Allows to create stock reservations for quotation lines before the
confirmation of the quotation. The reservations might have a validity
date and in any case they are lifted when the quotation is canceled or
confirmed.
Reservations can be done only on "make to stock" and stockable products.
The reserved products are subtracted from the virtual stock. It means
that if you reserved a quantity of products which bring the virtual
stock below the minimum, the orderpoint will be triggered and new
purchase orders will be generated. It also implies that the max may be
exceeded if the reservations are canceled.
If you want to prevent sales orders to be confirmed when the stock is
insufficient at the order date, you may want to install the
`sale_exception_nostock` module.
Additionally, if the sale_owner_stock_sourcing module is installed, the owner
specified on the sale order line will be proposed as owner of the reservation.
If you try to make a reservation for an order whose lines have different, you
will get a message suggesting to reserve each line individually. There is no
module dependency: this modules is fully functional even without ownership
management.

View File

@@ -0,0 +1,8 @@
#. Create a new Sale Order
#. Add lines to the Order
Now you can reserve all the lines by clicking on *Reserve Stock* or you can reserve each
one by pressing on the lock icon at the lines.
Once they where reserved, you can release the reserves by clicking at *Cancell all*
button or by clicking the undo icon on the lines.

View File

@@ -0,0 +1,456 @@
<?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: http://docutils.sourceforge.net/" />
<title>Stock Reserve Sales</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="stock-reserve-sales">
<h1 class="title">Stock Reserve Sales</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/OCA/stock-logistics-warehouse/tree/13.0/stock_reserve_sale"><img alt="OCA/stock-logistics-warehouse" src="https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-0-stock_reserve_sale"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/153/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>Allows to create stock reservations for quotation lines before the
confirmation of the quotation. The reservations might have a validity
date and in any case they are lifted when the quotation is canceled or
confirmed.</p>
<p>Reservations can be done only on “make to stock” and stockable products.</p>
<p>The reserved products are subtracted from the virtual stock. It means
that if you reserved a quantity of products which bring the virtual
stock below the minimum, the orderpoint will be triggered and new
purchase orders will be generated. It also implies that the max may be
exceeded if the reservations are canceled.</p>
<p>If you want to prevent sales orders to be confirmed when the stock is
insufficient at the order date, you may want to install the
<cite>sale_exception_nostock</cite> module.</p>
<p>Additionally, if the sale_owner_stock_sourcing module is installed, the owner
specified on the sale order line will be proposed as owner of the reservation.
If you try to make a reservation for an order whose lines have different, you
will get a message suggesting to reserve each line individually. There is no
module dependency: this modules is fully functional even without ownership
management.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<ol class="arabic simple">
<li>Create a new Sale Order</li>
<li>Add lines to the Order</li>
</ol>
<p>Now you can reserve all the lines by clicking on <em>Reserve Stock</em> or you can reserve each
one by pressing on the lock icon at the lines.</p>
<p>Once they where reserved, you can release the reserves by clicking at <em>Cancell all</em>
button or by clicking the undo icon on the lines.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_reserve_sale%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id4">Authors</a></h2>
<ul class="simple">
<li>Camptocamp</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<ul class="simple">
<li>Leonardo Pistone &lt;<a class="reference external" href="mailto:leonardo.pistone&#64;camptocamp.com">leonardo.pistone&#64;camptocamp.com</a>&gt;</li>
<li>Alexandre Fayolle &lt;<a class="reference external" href="mailto:alexandre.fayolle&#64;camptocamp.com">alexandre.fayolle&#64;camptocamp.com</a>&gt;</li>
<li>Yannick Vaucher &lt;<a class="reference external" href="mailto:yannick.vaucher&#64;camptocamp.com">yannick.vaucher&#64;camptocamp.com</a>&gt;</li>
<li>Guewen Baconnier &lt;<a class="reference external" href="mailto:guewen.baconnier&#64;camptocamp.com">guewen.baconnier&#64;camptocamp.com</a>&gt;</li>
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a>:<ul>
<li>Carlos Roca</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_reserve_sale">OCA/stock-logistics-warehouse</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
from . import test_stock_reserve_sale

View File

@@ -0,0 +1,138 @@
# Copyright 2021 Tecnativa - Carlos Roca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.exceptions import UserError
from odoo.tests import Form, common
class TestStockReserveSale(common.SavepointCase):
def setUp(self):
super().setUp()
partner_form = Form(self.env["res.partner"])
partner_form.name = "Test partner"
partner_form.country_id = self.env.ref("base.es")
self.partner = partner_form.save()
warehouse_form = Form(self.env["stock.warehouse"])
warehouse_form.name = "Test warehouse"
warehouse_form.code = "TEST"
self.warehouse = warehouse_form.save()
product_form = Form(self.env["product.product"])
product_form.name = "Test Product 1"
product_form.type = "product"
self.product_1 = product_form.save()
product_form = Form(self.env["product.product"])
product_form.name = "Test Product 2"
product_form.type = "product"
self.product_2 = product_form.save()
self.env["stock.quant"].create(
{
"product_id": self.product_1.id,
"location_id": self.warehouse.lot_stock_id.id,
"quantity": 10.0,
}
)
self.env["stock.quant"].create(
{
"product_id": self.product_2.id,
"location_id": self.warehouse.lot_stock_id.id,
"quantity": 10.0,
}
)
def test_reserve_01_tree_reserve_release(self):
sale_order_form = Form(self.env["sale.order"])
sale_order_form.partner_id = self.partner
with sale_order_form.order_line.new() as order_line_form:
order_line_form.product_id = self.product_1
order_line_form.product_uom_qty = 3
so = sale_order_form.save()
wiz = Form(
self.env["sale.stock.reserve"].with_context(
active_model="sale.order.line", active_ids=so.order_line.ids
)
).save()
wiz.button_reserve()
self.assertEquals(self.product_1.virtual_available, 7)
so.order_line.release_stock_reservation()
self.assertEquals(self.product_1.virtual_available, 10)
def test_reserve_02_all_form_reserve_release(self):
sale_order_form = Form(self.env["sale.order"])
sale_order_form.partner_id = self.partner
with sale_order_form.order_line.new() as order_line_form:
order_line_form.product_id = self.product_1
order_line_form.product_uom_qty = 3
with sale_order_form.order_line.new() as order_line_form:
order_line_form.product_id = self.product_2
order_line_form.product_uom_qty = 5
so = sale_order_form.save()
wiz = Form(
self.env["sale.stock.reserve"].with_context(
active_model="sale.order", active_id=so.id, active_ids=so.ids
)
).save()
wiz.button_reserve()
self.assertEquals(self.product_1.virtual_available, 7)
self.assertEquals(self.product_2.virtual_available, 5)
so.release_all_stock_reservation()
self.assertEquals(self.product_1.virtual_available, 10)
self.assertEquals(self.product_2.virtual_available, 10)
def test_reserve_03_confirm_order_release(self):
sale_order_form = Form(self.env["sale.order"])
sale_order_form.partner_id = self.partner
with sale_order_form.order_line.new() as order_line_form:
order_line_form.product_id = self.product_1
order_line_form.product_uom_qty = 3
so = sale_order_form.save()
wiz = Form(
self.env["sale.stock.reserve"].with_context(
active_model="sale.order.line", active_ids=so.order_line.ids
)
).save()
wiz.button_reserve()
self.assertEquals(self.product_1.virtual_available, 7)
so.action_confirm()
cancelled_reservation = self.env["stock.reservation"].search(
[("product_id", "=", self.product_1.id), ("state", "=", "cancel")]
)
self.assertEquals(len(cancelled_reservation), 1)
self.assertEquals(self.product_1.virtual_available, 7)
def test_reserve_04_cancel_order_release(self):
sale_order_form = Form(self.env["sale.order"])
sale_order_form.partner_id = self.partner
with sale_order_form.order_line.new() as order_line_form:
order_line_form.product_id = self.product_1
order_line_form.product_uom_qty = 3
so = sale_order_form.save()
wiz = Form(
self.env["sale.stock.reserve"].with_context(
active_model="sale.order.line", active_ids=so.order_line.ids
)
).save()
wiz.button_reserve()
self.assertEquals(self.product_1.virtual_available, 7)
so.action_cancel()
cancelled_reservation = self.env["stock.reservation"].search(
[("product_id", "=", self.product_1.id), ("state", "=", "cancel")]
)
self.assertEquals(len(cancelled_reservation), 1)
self.assertEquals(self.product_1.virtual_available, 10)
def test_reserve_05_unlink_order(self):
sale_order_form = Form(self.env["sale.order"])
sale_order_form.partner_id = self.partner
with sale_order_form.order_line.new() as order_line_form:
order_line_form.product_id = self.product_1
order_line_form.product_uom_qty = 3
so = sale_order_form.save()
wiz = Form(
self.env["sale.stock.reserve"].with_context(
active_model="sale.order.line", active_ids=so.order_line.ids
)
).save()
wiz.button_reserve()
with self.assertRaises(UserError):
so.unlink()
with self.assertRaises(UserError):
so.order_line.unlink()

View File

@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_order_form_reserve" model="ir.ui.view">
<field name="name">sale.order.form.reserve</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale_stock.view_order_form_inherit_sale_stock" />
<field name="arch" type="xml">
<button name="action_quotation_send" position="before">
<field name="is_stock_reservable" invisible="1" />
<button
name="%(action_sale_stock_reserve)d"
type="action"
string="Reserve Stock"
help="Pre-book products from stock"
attrs="{'invisible': [('is_stock_reservable', '=', False)]}"
/>
</button>
<field name="order_line" position="attributes">
<attribute name="options">{"reload_on_button": 1}</attribute>
</field>
<xpath
expr="//field[@name='order_line']/form//field[@name='state']"
position="before"
>
<field name="reservation_ids" invisible="1" />
<button
name="%(action_sale_stock_reserve)d"
type="action"
string="Reserve Stock"
attrs="{'invisible': ['|', ('reservation_ids', '!=', []),
('state', '!=', 'draft')]}"
/>
<button
name="release_stock_reservation"
type="object"
string="Release Reservation"
attrs="{'invisible': ['|', ('reservation_ids', '=', []),
('state', '!=', 'draft')]}"
/>
</xpath>
<xpath
expr="//field[@name='order_line']/tree/field[@name='price_subtotal']"
position="after"
>
<field name="reservation_ids" invisible="1" />
<field name="is_stock_reservable" invisible="1" />
<button
name="%(action_sale_stock_reserve)d"
type="action"
string="Reserve Stock"
icon="fa-lock"
attrs="{'invisible': [('is_stock_reservable', '=', False)]}"
/>
<button
name="release_stock_reservation"
type="object"
string="Release Reservation"
icon="fa-undo"
attrs="{'invisible': [('reservation_ids', '=', [])]}"
/>
</xpath>
<field name="user_id" position="before">
<field name="has_stock_reservation" invisible="1" />
</field>
<field name="order_line" position="after">
<div class="text-right">
<button
name="release_all_stock_reservation"
string="Cancell all"
type="object"
attrs="{'invisible': [('has_stock_reservation', '=', False)]}"
/>
</div>
</field>
<!-- Make readonly the fields quantity and product_id when they are reserved -->
<xpath
expr="//field[@name='order_line']/form//field[@name='product_id']"
position="after"
>
<field name="is_readonly" invisible="True" />
</xpath>
<xpath
expr="//field[@name='order_line']/form//field[@name='product_uom_qty']"
position="attributes"
>
<attribute
name="attrs"
>{'readonly': [('is_readonly', '=', True)]}</attribute>
</xpath>
<xpath
expr="//field[@name='order_line']/form//field[@name='product_id']"
position="attributes"
>
<attribute
name="attrs"
>{'readonly': [('is_readonly', '=', True)]}</attribute>
</xpath>
<xpath
expr="//field[@name='order_line']/tree/field[@name='product_id']"
position="after"
>
<field name="is_readonly" invisible="True" />
</xpath>
<xpath
expr="//field[@name='order_line']/tree/field[@name='product_uom_qty']"
position="attributes"
>
<attribute
name="attrs"
>{'readonly': [('is_readonly', '=', True)]}</attribute>
</xpath>
<xpath
expr="//field[@name='order_line']/tree/field[@name='product_id']"
position="attributes"
>
<attribute
name="attrs"
>{'readonly': [('is_readonly', '=', True)]}</attribute>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_stock_reservation_form" model="ir.ui.view">
<field name="name">stock.reservation.form</field>
<field name="model">stock.reservation</field>
<field name="inherit_id" ref="stock_reserve.view_stock_reservation_form" />
<field name="arch" type="xml">
<group name="location" position="after">
<group name="sale" string="Sales">
<field name="sale_id" />
<field name="sale_line_id" />
</group>
</group>
</field>
</record>
<record id="view_stock_reservation_tree" model="ir.ui.view">
<field name="name">stock.reservation.tree</field>
<field name="model">stock.reservation</field>
<field name="inherit_id" ref="stock_reserve.view_stock_reservation_tree" />
<field name="arch" type="xml">
<field name="move_id" position="before">
<field name="sale_id" />
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,3 @@
# Copyright 2013 Camptocamp SA - Guewen Baconnier
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import sale_stock_reserve

View File

@@ -0,0 +1,112 @@
# Copyright 2013 Camptocamp SA - Guewen Baconnier
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, exceptions, fields, models
class SaleStockReserve(models.TransientModel):
_name = "sale.stock.reserve"
_description = "Sale Stock Reserve"
@api.model
def _default_location_id(self):
return self.env["stock.reservation"].get_location_from_ref(
"stock.stock_location_stock"
)
@api.model
def _default_location_dest_id(self):
return self.env["stock.reservation"]._default_location_dest_id()
def _default_owner(self):
"""If sale_owner_stock_sourcing is installed, it adds an owner field
on sale order lines. Use it.
"""
model = self.env[self.env.context["active_model"]]
if model._name == "sale.order":
lines = model.browse(self.env.context["active_id"]).order_line
else:
lines = model.browse(self.env.context["active_ids"])
try:
owners = {l.stock_owner_id for l in lines}
except AttributeError:
return self.env["res.partner"]
# module sale_owner_stock_sourcing not installed, fine
if len(owners) == 1:
return owners.pop()
elif len(owners) > 1:
raise exceptions.Warning(
_(
"""The lines have different owners. Please reserve them
individually with the reserve button on each one."""
)
)
return self.env["res.partner"]
location_id = fields.Many2one(
"stock.location", "Source Location", required=True, default=_default_location_id
)
location_dest_id = fields.Many2one(
"stock.location",
"Reservation Location",
required=True,
help="Location where the system will reserve the " "products.",
default=_default_location_dest_id,
)
date_validity = fields.Date(
"Validity Date",
help="If a date is given, the reservations will be released "
"at the end of the validity.",
)
note = fields.Text("Notes")
owner_id = fields.Many2one("res.partner", "Stock Owner", default=_default_owner)
def _prepare_stock_reservation(self, line):
self.ensure_one()
return {
"product_id": line.product_id.id,
"product_uom": line.product_uom.id,
"product_uom_qty": line.product_uom_qty,
"date_validity": self.date_validity,
"name": "{} ({})".format(line.order_id.name, line.name),
"location_id": self.location_id.id,
"location_dest_id": self.location_dest_id.id,
"note": self.note,
"price_unit": line.price_unit,
"sale_line_id": line.id,
"restrict_partner_id": self.owner_id.id,
}
def stock_reserve(self, line_ids):
self.ensure_one()
lines = self.env["sale.order.line"].browse(line_ids)
for line in lines:
if not line.is_stock_reservable:
continue
vals = self._prepare_stock_reservation(line)
reserv = self.env["stock.reservation"].create(vals)
reserv.reserve()
return True
def button_reserve(self):
env = self.env
self.ensure_one()
close = {"type": "ir.actions.act_window_close"}
active_model = env.context.get("active_model")
active_ids = env.context.get("active_ids")
if not (active_model and active_ids):
return close
if active_model == "sale.order":
sales = env["sale.order"].browse(active_ids)
line_ids = [line.id for sale in sales for line in sale.order_line]
if active_model == "sale.order.line":
line_ids = active_ids
self.stock_reserve(line_ids)
return close

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_sale_stock_reserve_form" model="ir.ui.view">
<field name="name">sale.stock.reserve.form</field>
<field name="model">sale.stock.reserve</field>
<field name="arch" type="xml">
<form string="Reserve Stock">
<p class="oe_grey">
A stock reservation will be created for the products
of the selected quotation lines. If a validity date is specified,
the reservation will be released once the date has passed.
</p>
<group>
<field name="location_id" />
<field name="location_dest_id" />
<field name="date_validity" />
<field name="owner_id" groups="stock.group_tracking_owner" />
</group>
<group name="note" string="Notes">
<field name="note" nolabel="1" />
</group>
<footer>
<button
string="Reserve"
name="button_reserve"
type="object"
class="oe_highlight"
/>
or
<button special="cancel" class="oe_link" string="Cancel" />
</footer>
</form>
</field>
</record>
<record id="action_sale_stock_reserve" model="ir.actions.act_window">
<field name="name">Reserve Stock for Quotation Lines</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.stock.reserve</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>