Initial commit: OCA Workflow Process packages (456 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:00 +02:00
commit d366e42934
18799 changed files with 1284507 additions and 0 deletions

View file

@ -0,0 +1,132 @@
====================
Quick Purchase order
====================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:3a289ba9d1081fea8731b01b450c302a2c899b00f870830094fee828f192836a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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%2Fpurchase--workflow-lightgray.png?logo=github
:target: https://github.com/OCA/purchase-workflow/tree/16.0/purchase_quick
:alt: OCA/purchase-workflow
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/purchase-workflow-16-0/purchase-workflow-16-0-purchase_quick
: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/purchase-workflow&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows to add easily products into the purchase order (mass line add/update).
**Table of contents**
.. contents::
:local:
Usage
=====
Inside a purchase order, you can click on "Add products", to open a product tree view, then update "qty to purchase" field.
.. image:: https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_quick/static/description/01_purchase_order_create.png
:width: 800 px
:alt: Purchase order create
|
.. image:: https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_quick/static/description/02_purchase_order_add_product.png
:width: 800 px
:alt: Purchase order Add product
|
The update of "qty to purchase" will add new purchase line or update the existing line. If qty to purchase is 0 it purchase line will deleted if it exists.
|
.. image:: https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_quick/static/description/03_purchase_order_updated.png
:width: 800 px
:alt: Purchase order updated
|
.. image:: https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_quick/static/description/04_purchase_order_update_product_qty.png
:width: 800 px
:alt: Purchase order update product qty.
Known issues / Roadmap
======================
A note on dependencies: this module depends on stock. Mainly, for displaying qty_available of a product.
To avoid this dependency, this module could be split.
Compatibility note: purchase_order_type could be compatible as far as functionality goes, but not
for tests (adding a new required field breaks our usage of Form).
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/purchase-workflow/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/purchase-workflow/issues/new?body=module:%20purchase_quick%0Aversion:%2016.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
~~~~~~~
* Akretion
Contributors
~~~~~~~~~~~~
* Sébastien BEAU <sebastien.beau@akretion.com>
* Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
* Pierrick Brun <pierrick.brun@akretion.com>
* Kevin Khao <kevin.khao@akretion.com>
* `Sygel <https://www.sygel.es>`_:
* Ángel García de la Chica Herrera <angel.garcia@sygel.es>
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-legalsylvain| image:: https://github.com/legalsylvain.png?size=40px
:target: https://github.com/legalsylvain
:alt: legalsylvain
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-legalsylvain|
This module is part of the `OCA/purchase-workflow <https://github.com/OCA/purchase-workflow/tree/16.0/purchase_quick>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

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

View file

@ -0,0 +1,17 @@
# © 2014 Today Akretion
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Pierrick Brun <pierrick.brun@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Quick Purchase order",
"version": "16.0.1.0.0",
"author": "Akretion, Odoo Community Association (OCA)",
"maintainers": ["legalsylvain"],
"website": "https://github.com/OCA/purchase-workflow",
"license": "AGPL-3",
"category": "Purchase",
"depends": ["base_product_mass_addition", "purchase"],
"data": ["views/purchase_order_view.xml", "views/product_product_view.xml"],
"installable": True,
}

View file

@ -0,0 +1,85 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * purchase_quick
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.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: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.view_purchase_form
msgid "Add from supplier's product list"
msgstr "Dodaj sa liste proizvoda dobavljača"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "Filter products supplied by the supplier of the current parent object"
msgstr "Filtriraj proizvode koje isporučuje dobavljač trenutnog nadređenog objekta"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "For current supplier"
msgstr "Za trenutnog dobavljača"
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid ""
"Must have only 1 line per product for mass addition, but there are "
"%(nr_lines)s lines for the product %(product_name)s"
msgstr ""
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "Open"
msgstr "Otvori"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__po_line_ids
msgid "Po Line"
msgstr "Red narudžbenice"
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_product_product
msgid "Product Variant"
msgstr "Varijanta proizvoda"
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid "Product Variants"
msgstr "Varijante proizvoda"
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_purchase_order
msgid "Purchase Order"
msgstr "Nalog za nabavu"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__seller_price
msgid "Seller Price"
msgstr "Prodajna cijena"
#. module: purchase_quick
#: model:ir.model.fields,help:purchase_quick.field_product_product__po_line_ids
msgid "Technical: used to compute quantities to purchase."
msgstr "Tehnički: koristi se za izračunavanje količina za nabavku."
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "UoM"
msgstr "JMJ"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__variant_specific_seller_ids
msgid "Variant Specific Seller"
msgstr "Prodavač specifičan za varijantu"

View file

@ -0,0 +1,103 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * purchase_quick
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-04-12 10:07+0000\n"
"Last-Translator: luis-ron <luis.ron@sygel.es>\n"
"Language-Team: none\n"
"Language: es\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 4.14.1\n"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.view_purchase_form
msgid "Add from supplier's product list"
msgstr ""
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "Filter products supplied by the supplier of the current parent object"
msgstr ""
"Filtrar los productos suministrados por el proveedor del objeto padre actual"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "For current supplier"
msgstr "Para el proveedor actual"
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid ""
"Must have only 1 line per product for mass addition, but there are "
"%(nr_lines)s lines for the product %(product_name)s"
msgstr ""
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "Open"
msgstr "Abrir"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__po_line_ids
msgid "Po Line"
msgstr "Línea Po"
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_product_product
msgid "Product Variant"
msgstr ""
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid "Product Variants"
msgstr "Variantes de Productos"
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_purchase_order
msgid "Purchase Order"
msgstr "Orden de Compra"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__seller_price
msgid "Seller Price"
msgstr "Precio Vendedor"
#. module: purchase_quick
#: model:ir.model.fields,help:purchase_quick.field_product_product__po_line_ids
msgid "Technical: used to compute quantities to purchase."
msgstr "Técnico: utilizado para calcular las cantidades a comprar."
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "UoM"
msgstr "UdM"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__variant_specific_seller_ids
msgid "Variant Specific Seller"
msgstr "Vendedor Específico de Variantes"
#~ msgid "Add"
#~ msgstr "Añadir"
#, python-format
#~ msgid ""
#~ "Must have only 1 line per product for mass addition, but there are "
#~ "{nr_lines}s lines for the product %(product)s"
#~ msgstr ""
#~ "Sólo debe haber 1 línea por producto para la adición en masa, pero hay "
#~ "{nr_lines}s líneas para el producto %(product)s"
#~ msgid "Product"
#~ msgstr "Producto"

View file

@ -0,0 +1,92 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * purchase_quick
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-06 16:25+0000\n"
"PO-Revision-Date: 2025-01-06 16:25+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.view_purchase_form
msgid "Add from supplier's product list"
msgstr "Ajouter depuis la liste des produits de ce fournisseur"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "Filter products supplied by the supplier of the current parent object"
msgstr ""
"Afficher uniquement les articles du fournisseur de la commande d'achat en "
"cours"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "For current supplier"
msgstr "Ce Fournisseur"
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid ""
"Must have only 1 line per product for mass addition, but there are "
"%(nr_lines)s lines for the product %(product_name)s"
msgstr ""
"Il doit y avoir seulement une ligne par produit pour l'addition en masse, "
"mais il y a %(nr_lines)s lignes pour le produit %(product_name)s"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "Open"
msgstr "Ouvrir"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__po_line_ids
msgid "Po Line"
msgstr "Ligne de Commande"
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_product_product
msgid "Product Variant"
msgstr "Variante de produit"
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid "Product Variants"
msgstr "Articles"
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_purchase_order
msgid "Purchase Order"
msgstr "Bon de commande"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__seller_price
msgid "Seller Price"
msgstr "Prix fournisseur"
#. module: purchase_quick
#: model:ir.model.fields,help:purchase_quick.field_product_product__po_line_ids
msgid "Technical: used to compute quantities to purchase."
msgstr "Technique: utilisé pour calculer les quantités à acheter."
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "UoM"
msgstr "UdM"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__variant_specific_seller_ids
msgid "Variant Specific Seller"
msgstr "Fournisseur spécifique pour cette variante"

View file

@ -0,0 +1,116 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * purchase_quick
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-11 13:06+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.6.2\n"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.view_purchase_form
msgid "Add from supplier's product list"
msgstr "Aggiungi dall'elenco prodotti del fornitore"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "Filter products supplied by the supplier of the current parent object"
msgstr "Filtra prodotti forniti dal fornitore dell'oggetto genitore corrente"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "For current supplier"
msgstr "Per il fornitore selezionato"
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid ""
"Must have only 1 line per product for mass addition, but there are "
"%(nr_lines)s lines for the product %(product_name)s"
msgstr ""
"Bisogna avere solo una riga per prodotto per l'aggiunta di massa, ma ci sono "
"%(nr_lines)s righe per il prodotto %(product_name)s"
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "Open"
msgstr "Apri"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__po_line_ids
msgid "Po Line"
msgstr "Riga PO"
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_product_product
msgid "Product Variant"
msgstr "Variante prodotto"
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid "Product Variants"
msgstr "Varianti prodotto"
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_purchase_order
msgid "Purchase Order"
msgstr "Ordine di acquisto"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__seller_price
msgid "Seller Price"
msgstr "Prezzo Fornitore"
#. module: purchase_quick
#: model:ir.model.fields,help:purchase_quick.field_product_product__po_line_ids
msgid "Technical: used to compute quantities to purchase."
msgstr "Tecnico: utilizzato per calcolare quantità da acquistare."
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "UoM"
msgstr "UdM"
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__variant_specific_seller_ids
msgid "Variant Specific Seller"
msgstr "Fornitore specifica variante"
#~ msgid "Add"
#~ msgstr "Aggiungi"
#~ msgid "Product"
#~ msgstr "Prodotto"
#~ msgid "Display Name"
#~ msgstr "Nome visualizzato"
#~ msgid "ID"
#~ msgstr "ID"
#~ msgid "Last Modified on"
#~ msgstr "Ultima modifica il"
#, python-format
#~ msgid ""
#~ "Must have only 1 line per product for mass addition, but there are %s "
#~ "lines for the product %s"
#~ msgstr ""
#~ "Dev'esserci solo 1 riga per prodotto per aggiungerli massivamente, ma ci "
#~ "sono %s righe per il prodotto %s"
#~ msgid "Products for purchase"
#~ msgstr "Prodotti da acquistare"

View file

@ -0,0 +1,85 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * purchase_quick
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.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: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.view_purchase_form
msgid "Add from supplier's product list"
msgstr ""
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "Filter products supplied by the supplier of the current parent object"
msgstr ""
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_search_form_view
msgid "For current supplier"
msgstr ""
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid ""
"Must have only 1 line per product for mass addition, but there are "
"%(nr_lines)s lines for the product %(product_name)s"
msgstr ""
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "Open"
msgstr ""
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__po_line_ids
msgid "Po Line"
msgstr ""
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_product_product
msgid "Product Variant"
msgstr ""
#. module: purchase_quick
#. odoo-python
#: code:addons/purchase_quick/models/purchase_order.py:0
#, python-format
msgid "Product Variants"
msgstr ""
#. module: purchase_quick
#: model:ir.model,name:purchase_quick.model_purchase_order
msgid "Purchase Order"
msgstr ""
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__seller_price
msgid "Seller Price"
msgstr ""
#. module: purchase_quick
#: model:ir.model.fields,help:purchase_quick.field_product_product__po_line_ids
msgid "Technical: used to compute quantities to purchase."
msgstr ""
#. module: purchase_quick
#: model_terms:ir.ui.view,arch_db:purchase_quick.product_tree_view4purchase
msgid "UoM"
msgstr ""
#. module: purchase_quick
#: model:ir.model.fields,field_description:purchase_quick.field_product_product__variant_specific_seller_ids
msgid "Variant Specific Seller"
msgstr ""

View file

@ -0,0 +1,2 @@
from . import purchase_order
from . import product_product

View file

@ -0,0 +1,93 @@
# © 2014 Today Akretion
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# @author Pierrick Brun <pierrick.brun@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class ProductProduct(models.Model):
_inherit = "product.product"
# for searching purpose
variant_specific_seller_ids = fields.One2many("product.supplierinfo", "product_id")
po_line_ids = fields.One2many(
comodel_name="purchase.order.line",
inverse_name="product_id",
help="Technical: used to compute quantities to purchase.",
)
seller_price = fields.Float(compute="_compute_seller_price")
def _compute_seller_price(self):
po = self.pma_parent
for record in self:
seller = record._select_seller(
partner_id=po.partner_id,
quantity=record.qty_to_process or 1,
uom_id=record.quick_uom_id,
)
price_unit = record.uom_id._compute_price(
seller.price,
record.quick_uom_id,
)
if self.pma_parent.currency_id != seller.currency_id:
price_unit = seller.currency_id._convert(
price_unit, po.currency_id, po.company_id, po.date_order.date()
)
record.seller_price = price_unit
def _default_quick_uom_id(self):
if self.env.context.get("parent_model", False) == "purchase.order":
return self.uom_po_id
return super()._default_quick_uom_id()
def _compute_process_qty_purchase(self):
po_lines = self.env["purchase.order.line"].search(
[("order_id", "=", self.env.context.get("parent_id"))]
)
for product in self:
product.qty_to_process = sum(
po_lines.filtered(lambda l: l.product_id == product).mapped(
"product_qty"
)
)
@api.depends("po_line_ids")
def _compute_process_qty(self):
res = super()._compute_process_qty()
if self.env.context.get("parent_model", False) == "purchase.order":
self._compute_process_qty_purchase()
return res
@api.model
def search(self, args, offset=0, limit=None, order=None, count=False):
purchase = self.env["purchase.order"].browse(self.env.context.get("parent_id"))
if self.env.context.get("in_current_parent") and purchase:
po_lines = self.env["purchase.order.line"].search(
[("order_id", "=", purchase.id)]
)
args.append(("id", "in", po_lines.mapped("product_id").ids))
if self.env.context.get("for_current_supplier") and purchase:
seller = purchase.partner_id
seller = seller.commercial_partner_id or seller
args += [
"|",
("variant_specific_seller_ids.partner_id", "=", seller.id),
"&",
("seller_ids.partner_id", "=", seller.id),
("product_variant_ids", "!=", False),
]
return super().search(
args, offset=offset, limit=limit, order=order, count=count
)
@api.model
def check_access_rights(self, operation, raise_exception=True):
"""hijack product edition rights if we're in the mass edition menu"""
if self.env.context.get("quick_access_rights_purchase"):
return self.env["purchase.order.line"].check_access_rights(
operation, raise_exception
)
return super().check_access_rights(operation, raise_exception)

View file

@ -0,0 +1,71 @@
# © 2014 Today Akretion
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# @author Pierrick Brun <pierrick.brun@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from odoo import _, models
from odoo.exceptions import ValidationError
class PurchaseOrder(models.Model):
_name = "purchase.order"
_inherit = ["purchase.order", "product.mass.addition"]
def add_product(self):
self.ensure_one()
res = self._common_action_keys()
res["context"].update(
{
"search_default_filter_to_purchase": 1,
"search_default_filter_for_current_supplier": 1,
"quick_access_rights_purchase": 1,
}
)
commercial = self.partner_id.commercial_partner_id.name
res["name"] = "🔙 {} ({})".format(_("Product Variants"), commercial)
res["view_id"] = (self.env.ref("purchase_quick.product_tree_view4purchase").id,)
res["search_view_id"] = (
self.env.ref("purchase_quick.product_search_form_view").id,
)
return res
def _get_quick_line(self, product):
result = self.env["purchase.order.line"].search(
[("product_id", "=", product.id), ("order_id", "=", self.id)]
)
nr_lines = len(result.ids)
if nr_lines > 1:
raise ValidationError(
_(
"Must have only 1 line per product for mass addition, but "
"there are %(nr_lines)s lines for the product %(product_name)s",
nr_lines=nr_lines,
product_name=product.display_name,
)
)
return result
def _get_quick_line_qty_vals(self, product):
return {
"product_id": None,
"product_uom": product.quick_uom_id.id,
"product_qty": product.qty_to_process,
}
def _complete_quick_line_vals(self, vals, lines_key=""):
# This params are need for playing correctly the onchange
vals_to_add = {
"order_id": self.id,
"partner_id": self.partner_id.id,
}
vals_to_add.update(vals)
vals = vals_to_add
return super(PurchaseOrder, self)._complete_quick_line_vals(
vals, lines_key="order_line"
)
def _add_quick_line(self, product, lines_key=""):
return super(PurchaseOrder, self)._add_quick_line(
product, lines_key="order_line"
)

View file

@ -0,0 +1,8 @@
* Sébastien BEAU <sebastien.beau@akretion.com>
* Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
* Pierrick Brun <pierrick.brun@akretion.com>
* Kevin Khao <kevin.khao@akretion.com>
* `Sygel <https://www.sygel.es>`_:
* Ángel García de la Chica Herrera <angel.garcia@sygel.es>

View file

@ -0,0 +1 @@
This module allows to add easily products into the purchase order (mass line add/update).

View file

@ -0,0 +1,5 @@
A note on dependencies: this module depends on stock. Mainly, for displaying qty_available of a product.
To avoid this dependency, this module could be split.
Compatibility note: purchase_order_type could be compatible as far as functionality goes, but not
for tests (adding a new required field breaks our usage of Form).

View file

@ -0,0 +1,28 @@
Inside a purchase order, you can click on "Add products", to open a product tree view, then update "qty to purchase" field.
.. image:: ../static/description/01_purchase_order_create.png
:width: 800 px
:alt: Purchase order create
|
.. image:: ../static/description/02_purchase_order_add_product.png
:width: 800 px
:alt: Purchase order Add product
|
The update of "qty to purchase" will add new purchase line or update the existing line. If qty to purchase is 0 it purchase line will deleted if it exists.
|
.. image:: ../static/description/03_purchase_order_updated.png
:width: 800 px
:alt: Purchase order updated
|
.. image:: ../static/description/04_purchase_order_update_product_qty.png
:width: 800 px
:alt: Purchase order update product qty.

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -0,0 +1,462 @@
<!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: https://docutils.sourceforge.io/" />
<title>Quick Purchase order</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/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: gray; } /* 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, pre.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="quick-purchase-order">
<h1 class="title">Quick Purchase order</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:3a289ba9d1081fea8731b01b450c302a2c899b00f870830094fee828f192836a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" 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 image-reference" 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 image-reference" href="https://github.com/OCA/purchase-workflow/tree/16.0/purchase_quick"><img alt="OCA/purchase-workflow" src="https://img.shields.io/badge/github-OCA%2Fpurchase--workflow-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/purchase-workflow-16-0/purchase-workflow-16-0-purchase_quick"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/purchase-workflow&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows to add easily products into the purchase order (mass line add/update).</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="toc-entry-1">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-2">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
<p>Inside a purchase order, you can click on “Add products”, to open a product tree view, then update “qty to purchase” field.</p>
<img alt="Purchase order create" src="https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_quick/static/description/01_purchase_order_create.png" style="width: 800px;" />
<div class="line-block">
<div class="line"><br /></div>
</div>
<img alt="Purchase order Add product" src="https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_quick/static/description/02_purchase_order_add_product.png" style="width: 800px;" />
<div class="line-block">
<div class="line"><br /></div>
</div>
<p>The update of “qty to purchase” will add new purchase line or update the existing line. If qty to purchase is 0 it purchase line will deleted if it exists.</p>
<div class="line-block">
<div class="line"><br /></div>
</div>
<img alt="Purchase order updated" src="https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_quick/static/description/03_purchase_order_updated.png" style="width: 800px;" />
<div class="line-block">
<div class="line"><br /></div>
</div>
<img alt="Purchase order update product qty." src="https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_quick/static/description/04_purchase_order_update_product_qty.png" style="width: 800px;" />
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#toc-entry-2">Known issues / Roadmap</a></h1>
<p>A note on dependencies: this module depends on stock. Mainly, for displaying qty_available of a product.
To avoid this dependency, this module could be split.</p>
<p>Compatibility note: purchase_order_type could be compatible as far as functionality goes, but not
for tests (adding a new required field breaks our usage of Form).</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/purchase-workflow/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 to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/purchase-workflow/issues/new?body=module:%20purchase_quick%0Aversion:%2016.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="#toc-entry-4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>Akretion</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul class="simple">
<li>Sébastien BEAU &lt;<a class="reference external" href="mailto:sebastien.beau&#64;akretion.com">sebastien.beau&#64;akretion.com</a>&gt;</li>
<li>Mourad EL HADJ MIMOUNE &lt;<a class="reference external" href="mailto:mourad.elhadj.mimoune&#64;akretion.com">mourad.elhadj.mimoune&#64;akretion.com</a>&gt;</li>
<li>Pierrick Brun &lt;<a class="reference external" href="mailto:pierrick.brun&#64;akretion.com">pierrick.brun&#64;akretion.com</a>&gt;</li>
<li>Kevin Khao &lt;<a class="reference external" href="mailto:kevin.khao&#64;akretion.com">kevin.khao&#64;akretion.com</a>&gt;</li>
<li><a class="reference external" href="https://www.sygel.es">Sygel</a>:<ul>
<li>Ángel García de la Chica Herrera &lt;<a class="reference external" href="mailto:angel.garcia&#64;sygel.es">angel.garcia&#64;sygel.es</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-7">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>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/legalsylvain"><img alt="legalsylvain" src="https://github.com/legalsylvain.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/purchase-workflow/tree/16.0/purchase_quick">OCA/purchase-workflow</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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View file

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

View file

@ -0,0 +1,216 @@
# @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.exceptions import ValidationError
from odoo.tests import tagged
from odoo.tests.common import Form, TransactionCase
@tagged("post_install", "-at_install")
class TestQuickPurchase(TransactionCase):
@classmethod
def _add_seller(cls, product, prices):
# drop existing seller
product.seller_ids.filtered(lambda s: s.partner_id == cls.partner).unlink()
for min_qty, price in prices:
cls.env["product.supplierinfo"].create(
{
"product_tmpl_id": product.product_tmpl_id.id,
"partner_id": cls.partner.id,
"price": price,
"min_qty": min_qty,
}
)
@classmethod
def _setUpBasicSaleOrder(cls):
vals = {"partner_id": cls.partner.id}
if hasattr(cls.env["purchase.order"], "order_type"):
vals["order_type"] = cls.env.ref("purchase_order_type.po_type_blanket").id
cls.po = cls.env["purchase.order"].create(vals)
with Form(cls.po, "purchase.purchase_order_form") as po_form:
po_form.partner_id = cls.partner
ctx = {"parent_id": cls.po.id, "parent_model": "purchase.order"}
cls.product_1 = cls.product_1.with_context(**ctx)
cls.product_2 = cls.product_2.with_context(**ctx)
cls.product_1.qty_to_process = 5.0
cls.product_2.qty_to_process = 6.0
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.partner = cls.env.ref("base.res_partner_1")
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
cls.uom_dozen = cls.env.ref("uom.product_uom_dozen")
cls.product_1 = cls.env.ref("product.product_product_8")
cls.product_2 = cls.env.ref("product.product_product_11")
cls._add_seller(cls.product_1, [(0, 10), (10, 8)])
cls._add_seller(cls.product_2, [(0, 5), (10, 4)])
cls._setUpBasicSaleOrder()
def test_product_seller_price(self):
self.assertEqual(self.product_1.seller_price, 10)
self.product_1.qty_to_process = 10.0
self.assertEqual(self.product_1.seller_price, 8)
self.product_1.quick_uom_id = self.uom_dozen
self.assertEqual(self.product_1.seller_price, 96)
def test_product_seller_price_with_currency(self):
self.po.currency_id = self.env.ref("base.EUR")
usd = self.env.ref("base.USD")
usd.rate_ids[1:].unlink()
usd.rate_ids.name = self.po.date_order.date()
usd.rate_ids.rate = 2
self.assertEqual(self.product_1.seller_price, 5)
self.product_1.qty_to_process = 10.0
self.assertEqual(self.product_1.seller_price, 4)
self.product_1.quick_uom_id = self.uom_dozen
self.assertEqual(self.product_1.seller_price, 48)
def test_quick_line_add_1(self):
"""
set non-null quantity to any product with no PO line:
-> a new PO line is created with that quantity
"""
line_1, line_2 = self.po.order_line
self.assertAlmostEqual(line_1.product_uom_qty, 5.0)
self.assertAlmostEqual(line_1.price_unit, 10)
self.assertAlmostEqual(line_2.product_uom_qty, 6.0)
self.assertAlmostEqual(line_2.price_unit, 5)
def test_quick_line_add_2(self):
"""
same as previous, but include a different UoM as well
We duplicate _setUpBasicSaleOrder except we ~simultaneously~
write on qty_to_process as well as quick_uom_id
(we want to make sure to test _inverse function when it is triggered twice)
"""
vals = {"partner_id": self.partner.id}
if hasattr(self.env["purchase.order"], "order_type"):
vals["order_type"] = self.env.ref("purchase_order_type.po_type_blanket").id
po = self.env["purchase.order"].create(vals)
with Form(po, "purchase.purchase_order_form") as po_form:
po_form.partner_id = self.partner
ctx = {"parent_id": self.po.id, "parent_model": "purchase.order"}
self.product_1 = self.product_1.with_context(**ctx)
self.product_2 = self.product_2.with_context(**ctx)
self.product_1.write({"qty_to_process": 5.0, "quick_uom_id": self.uom_unit.id})
self.product_2.write({"qty_to_process": 6.0, "quick_uom_id": self.uom_dozen.id})
line_1, line_2 = self.po.order_line
self.assertAlmostEqual(line_1.product_uom_qty, 5.0)
self.assertAlmostEqual(line_1.product_qty, 5.0)
self.assertEqual(line_1.product_uom, self.uom_unit)
self.assertAlmostEqual(line_1.price_unit, 10)
self.assertAlmostEqual(line_2.product_uom_qty, 72.0) # 12 * 6
self.assertAlmostEqual(line_2.product_qty, 6.0)
self.assertEqual(line_2.product_uom, self.uom_dozen)
self.assertAlmostEqual(line_2.price_unit, 48) # 12 * 4
def test_quick_line_update_1(self):
"""
set non-null quantity to any product with an already existing PO line:
-> same PO line is updated with that quantity
"""
self.product_1.qty_to_process = 7.0
self.product_2.qty_to_process = 13.0
line_1, line_2 = self.po.order_line
self.assertAlmostEqual(line_1.product_qty, 7.0)
self.assertAlmostEqual(line_1.price_unit, 10.0)
self.assertAlmostEqual(line_2.product_qty, 13.0)
self.assertAlmostEqual(line_2.price_unit, 4.0)
def test_quick_line_update_2(self):
"""
same as previous update only UoM in isolation, not qty
"""
self.product_1.quick_uom_id = self.uom_dozen
self.product_2.quick_uom_id = self.uom_unit
line_1, line_2 = self.po.order_line
self.assertEqual(line_1.product_uom, self.uom_dozen)
self.assertAlmostEqual(line_1.product_qty, 5.0)
self.assertAlmostEqual(line_1.product_uom_qty, 60.0)
self.assertAlmostEqual(line_1.price_unit, 96)
self.assertEqual(line_2.product_uom, self.uom_unit)
self.assertAlmostEqual(line_2.product_qty, 6.0)
self.assertAlmostEqual(line_2.product_uom_qty, 6.0)
self.assertAlmostEqual(line_2.price_unit, 5.0)
def test_quick_line_update_3(self):
"""
same as previous 2 tests combined: we do simultaneous qty + uom updates
"""
self.product_1.qty_to_process = 7.0
self.product_2.qty_to_process = 13.0
self.product_1.quick_uom_id = self.uom_dozen
self.product_2.quick_uom_id = self.uom_unit
line_1, line_2 = self.po.order_line
self.assertEqual(line_1.product_uom, self.uom_dozen)
self.assertEqual(line_2.product_uom, self.uom_unit)
self.assertEqual(line_1.product_uom, self.uom_dozen)
self.assertAlmostEqual(line_1.product_qty, 7.0)
self.assertAlmostEqual(line_1.product_uom_qty, 84.0)
self.assertAlmostEqual(line_1.price_unit, 96)
self.assertEqual(line_2.product_uom, self.uom_unit)
self.assertAlmostEqual(line_2.product_qty, 13.0)
self.assertAlmostEqual(line_2.product_uom_qty, 13.0)
self.assertAlmostEqual(line_2.price_unit, 4.0)
def test_quick_line_delete(self):
"""
set null quantity to any product with existing PO line:
-> PO line is deleted
"""
self.product_1.qty_to_process = 0.0
self.product_2.qty_to_process = 0.0
self.assertEqual(len(self.po.order_line), 0)
def test_open_quick_view(self):
"""
Test that the "Add" button opens the right action
"""
product_act_from_po = self.po.add_product()
self.assertEqual(product_act_from_po["type"], "ir.actions.act_window")
self.assertEqual(product_act_from_po["res_model"], "product.product")
self.assertEqual(product_act_from_po["view_mode"], "tree")
self.assertEqual(product_act_from_po["target"], "current")
self.assertEqual(
product_act_from_po["view_id"][0],
self.env.ref("purchase_quick.product_tree_view4purchase").id,
)
self.assertEqual(product_act_from_po["context"]["parent_id"], self.po.id)
def test_several_po_for_one_product(self):
"""
Test that when we try to mass add a product that already has
several lines with the same product we get a raise
"""
self.po.order_line[0].copy()
with self.assertRaises(ValidationError):
self.product_1.qty_to_process = 3.0
def test_no_pricelist_for_the_min_qty(self):
"""
Checks that if you enter a qty_to_process lower than the seller's min_qty,
the price_unit in de pusrchase.order.line is the standard_price.
"""
po = self.env["purchase.order"].create({"partner_id": self.partner.id})
ctx = {
"parent_id": po.id,
"parent_model": "purchase.order",
"quick_access_rights_purchase": 1,
}
product_3 = self.env.ref("product.product_product_5")
self._add_seller(product_3, [(5, 5), (10, 1)])
product_3 = product_3.with_context(**ctx)
product_3.write({"qty_to_process": 1.0, "quick_uom_id": self.uom_unit.id})
line_1 = po.order_line
self.assertEqual(line_1.product_qty, 1.0)
self.assertEqual(line_1.product_uom, self.uom_unit)
self.assertEqual(line_1.price_unit, product_3.standard_price)

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Inherit to customize according to your own needs -->
<record id="product_tree_view4purchase" model="ir.ui.view">
<field name="model">product.product</field>
<field name="priority" eval="20" />
<field name="arch" type="xml">
<tree create="0" editable="top">
<field name="display_name" readonly="1" />
<field name="qty_to_process" />
<field name="quick_uom_category_id" invisible="1" />
<field
name="quick_uom_id"
options="{'no_open': True, 'no_create': True}"
string="UoM"
groups="uom.group_uom"
/>
<field name="seller_price" readonly="1" />
<field name="qty_available" />
<field name="incoming_qty" />
<button name="button_quick_open_product" string="Open" type="object" />
</tree>
</field>
</record>
<record id="product_search_form_view" model="ir.ui.view">
<field name="model">product.product</field>
<field name="mode">primary</field>
<field
name="inherit_id"
ref="base_product_mass_addition.product_search_form_view"
/>
<field name="arch" type="xml">
<xpath expr="//filter[last()]" position="after">
<filter
name="filter_for_current_supplier"
string="For current supplier"
domain="[]"
context="{'for_current_supplier': True}"
help="Filter products supplied by the supplier of the current parent object"
/>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_purchase_form" model="ir.ui.view">
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form" />
<field name="arch" type="xml">
<field name="order_line" position="before">
<button
name="add_product"
string="Add from supplier's product list"
type="object"
attrs="{'invisible': [('state', 'not in', ('draft', 'sent'))]}"
class="oe_highlight"
/>
</field>
</field>
</record>
</odoo>