Merge PR #1382 into 14.0

Signed-off-by sebalix
This commit is contained in:
OCA-git-bot
2024-11-15 13:18:57 +00:00
23 changed files with 1422 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,20 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
{
"name": "MRP Serial Number Propagation",
"version": "14.0.1.0.0",
"development_status": "Beta",
"license": "AGPL-3",
"author": "Camptocamp, Odoo Community Association (OCA)",
"maintainers": ["sebalix"],
"summary": "Propagate a serial number from a component to a finished product",
"website": "https://github.com/OCA/manufacture",
"category": "Manufacturing",
"depends": ["mrp"],
"data": [
"views/mrp_bom.xml",
"views/mrp_production.xml",
],
"installable": True,
"application": False,
}

View File

@@ -0,0 +1,192 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_lot_number_propagation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-10-29 08:29+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.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.17\n"
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/product_template.py:0
#, python-format
msgid ""
"A BoM propagating serial numbers requires this product to be tracked as "
"such."
msgstr ""
"Una BoM que propague números de serie requiere que este producto sea "
"rastreado como tal."
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation
msgid ""
"Allow to propagate the lot/serial number from a component to the finished "
"product."
msgstr ""
"Permite propagar el número de lote/serie de un componente al producto "
"acabado."
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom
msgid "Bill of Material"
msgstr "Lista de Material"
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom_line
msgid "Bill of Material Line"
msgstr "Línea de la factura de materiales"
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Bill of material is marked for lot number propagation, but there are "
"multiple components propagating lot number. Please check BOM configuration."
msgstr ""
"La lista de materiales está marcada para la propagación del número de lote, "
"pero hay varios componentes que propagan el número de lote. Compruebe la "
"configuración de la lista de materiales."
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Bill of material is marked for lot number propagation, but there are no "
"components propagating lot number. Please check BOM configuration."
msgstr ""
"La lista de materiales está marcada para la propagación del número de lote, "
"pero no hay componentes que propaguen el número de lote. Compruebe la "
"configuración de la lista de materiales."
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation
msgid "Display Lot Number Propagation"
msgstr "Mostrar Número de Lote Propagación"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__display_propagate_lot_number
msgid "Display Propagate Lot Number"
msgstr "Mostrar Propagación Número de Lote"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated
msgid "Is Lot Number Propagated"
msgstr "Es el Número de Lote Propagado"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation
msgid "Lot Number Propagation"
msgstr "Propagación del Número de Lote"
#. module: mrp_lot_number_propagation
#: model_terms:ir.ui.view,arch_db:mrp_lot_number_propagation.mrp_production_form_view
msgid "Lot/Serial Number"
msgstr "Lote/Número de serie"
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Lot/Serial number %s already exists and has been used. Unable to propagate "
"it."
msgstr ""
"El lote/número de serie %s ya existe y ha sido utilizado. No se ha podido "
"propagar."
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Lot/Serial number is propagated from a component, you are not allowed to "
"change it."
msgstr ""
"El número de lote/serie se propaga desde un componente, usted no está "
"autorizado a modificarlo."
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated
msgid ""
"Lot/serial number is propagated from a component to the finished product."
msgstr ""
"El número de lote/serie se propaga de un componente al producto acabado."
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0
#, python-format
msgid ""
"Only components tracked by serial number can propagate its lot/serial number"
" to the finished product."
msgstr ""
"Sólo los componentes rastreados por número de serie pueden propagar su "
"número de lote/serie al producto acabado."
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_product_product
msgid "Product"
msgstr "Producto"
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_product_template
msgid "Product Template"
msgstr "Plantilla del Producto"
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_production
msgid "Production Order"
msgstr "Orden de Producción"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__propagate_lot_number
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_stock_move__propagate_lot_number
msgid "Propagate Lot Number"
msgstr "Propagar el Número de Lote"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing
msgid "Propagated Lot Producing"
msgstr "Producción de Lotes Propagados"
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_stock_move
msgid "Stock Move"
msgstr "Movimiento de Existencias"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing
msgid ""
"The BoM used on this manufacturing order is set to propagate lot number from"
" one of its components. The value will be computed once the corresponding "
"component is selected."
msgstr ""
"La lista de materiales utilizada en esta orden de producción está "
"configurada para propagar el número de lote desde uno de sus componentes. El "
"valor se calculará una vez seleccionado el componente correspondiente."
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/product_template.py:0
#, python-format
msgid ""
"This component is configured to propagate its serial number in the following"
" Bill of Materials:{boms}'"
msgstr ""
"Este componente está configurado para propagar su número de serie en la "
"siguiente lista de materiales:{boms}'"
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_bom.py:0
#, python-format
msgid ""
"With 'Lot Number Propagation' enabled, a line has to be configured with the "
"'Propagate Lot Number' option."
msgstr ""
"Con la \"Propagación del número de lote\" activada, debe configurarse una "
"línea con la opción \"Propagar número de lote\"."

View File

@@ -0,0 +1,198 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_lot_number_propagation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-11-21 16:35+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 4.17\n"
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/product_template.py:0
#, python-format
msgid ""
"A BoM propagating serial numbers requires this product to be tracked as such."
msgstr ""
"Una distinta base che propaga i numeri di serie richiede che questo prodotto "
"sia tracciato come tale."
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation
msgid ""
"Allow to propagate the lot/serial number from a component to the finished "
"product."
msgstr ""
"Consente di propagare il numero di lotto/matricola da un componente al "
"prodotto finito."
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom
msgid "Bill of Material"
msgstr "Distinta base"
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom_line
msgid "Bill of Material Line"
msgstr "Riga distinta base"
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Bill of material is marked for lot number propagation, but there are "
"multiple components propagating lot number. Please check BOM configuration."
msgstr ""
"La distinta base è impostata per la propagazione del numero di lotto, ma ci "
"sono più componenti che propagano il numero di lotto. Verificare la "
"configurazione della DB."
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Bill of material is marked for lot number propagation, but there are no "
"components propagating lot number. Please check BOM configuration."
msgstr ""
"La distinta base è impostata per la propagazione del numero di lotto, ma non "
"ci sono componenti che propagano il numero di lotto. Verificare la "
"configurazione della DB."
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation
msgid "Display Lot Number Propagation"
msgstr "Visualizza propagazione numero di lotto"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__display_propagate_lot_number
msgid "Display Propagate Lot Number"
msgstr "Visualizza il numero di lotto propagato"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated
msgid "Is Lot Number Propagated"
msgstr "Il numero di lotto è propagato"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation
msgid "Lot Number Propagation"
msgstr "Propagazione numero di lotto"
#. module: mrp_lot_number_propagation
#: model_terms:ir.ui.view,arch_db:mrp_lot_number_propagation.mrp_production_form_view
msgid "Lot/Serial Number"
msgstr "Numero di lotto/serie"
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Lot/Serial number %s already exists and has been used. Unable to propagate "
"it."
msgstr ""
"Il lotto/seriale %s esiste ed è stato usato. Non è possibile propagarlo."
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Lot/Serial number is propagated from a component, you are not allowed to "
"change it."
msgstr ""
"Il numero di lotto/matricola è propagato da un componente, non è consentito "
"modificarlo."
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated
msgid ""
"Lot/serial number is propagated from a component to the finished product."
msgstr ""
"Il numero di lotto/matricola è propagato da un componente al prodotto finito."
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0
#, python-format
msgid ""
"Only components tracked by serial number can propagate its lot/serial number "
"to the finished product."
msgstr ""
"Solo i componenti tracciati da una matricola possono propagare il loro "
"numero di lotto/matricola al prodotto finito."
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_product_product
msgid "Product"
msgstr "Prodotto"
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_product_template
msgid "Product Template"
msgstr "Modello prodotto"
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_production
msgid "Production Order"
msgstr "Ordine di produzione"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__propagate_lot_number
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_stock_move__propagate_lot_number
msgid "Propagate Lot Number"
msgstr "Propaga numero di lotto"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing
msgid "Propagated Lot Producing"
msgstr "Produzione lotto propagato"
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_stock_move
msgid "Stock Move"
msgstr "Movimento di magazzino"
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing
msgid ""
"The BoM used on this manufacturing order is set to propagate lot number from "
"one of its components. The value will be computed once the corresponding "
"component is selected."
msgstr ""
"La DiBa utilizzata in questo ordine di produzione è impostata per propagare "
"il nomero di lotto da uno dei suoi componenti. Il valore verrà calcolato una "
"volta che il componente corrispondente sarà selezionato."
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/product_template.py:0
#, python-format
msgid ""
"This component is configured to propagate its serial number in the following "
"Bill of Materials:{boms}'"
msgstr ""
"Questo componente è configurato per propagare la sua matricola nella "
"seguente DiBa: {boms}'"
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_bom.py:0
#, python-format
msgid ""
"With 'Lot Number Propagation' enabled, a line has to be configured with the "
"'Propagate Lot Number' option."
msgstr ""
"Con 'Propagazione numero di lotto' abilitata, una riga deve essere "
"configurata con l'opzione 'Propaga numero di lotto'."
#, python-format
#~ msgid ""
#~ "Only one BoM line can propagate its lot/serial number to the finished "
#~ "product."
#~ msgstr ""
#~ "Solo una riga di DiBa può propagare il suo numero di lotto/matricola al "
#~ "prodotto finito."

View File

@@ -0,0 +1,165 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_lot_number_propagation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.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: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/product_template.py:0
#, python-format
msgid ""
"A BoM propagating serial numbers requires this product to be tracked as "
"such."
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation
msgid ""
"Allow to propagate the lot/serial number from a component to the finished "
"product."
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom
msgid "Bill of Material"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom_line
msgid "Bill of Material Line"
msgstr ""
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Bill of material is marked for lot number propagation, but there are "
"multiple components propagating lot number. Please check BOM configuration."
msgstr ""
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Bill of material is marked for lot number propagation, but there are no "
"components propagating lot number. Please check BOM configuration."
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation
msgid "Display Lot Number Propagation"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__display_propagate_lot_number
msgid "Display Propagate Lot Number"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated
msgid "Is Lot Number Propagated"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation
msgid "Lot Number Propagation"
msgstr ""
#. module: mrp_lot_number_propagation
#: model_terms:ir.ui.view,arch_db:mrp_lot_number_propagation.mrp_production_form_view
msgid "Lot/Serial Number"
msgstr ""
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Lot/Serial number %s already exists and has been used. Unable to propagate "
"it."
msgstr ""
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0
#, python-format
msgid ""
"Lot/Serial number is propagated from a component, you are not allowed to "
"change it."
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated
msgid ""
"Lot/serial number is propagated from a component to the finished product."
msgstr ""
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0
#, python-format
msgid ""
"Only components tracked by serial number can propagate its lot/serial number"
" to the finished product."
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_product_product
msgid "Product"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_product_template
msgid "Product Template"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_production
msgid "Production Order"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__propagate_lot_number
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_stock_move__propagate_lot_number
msgid "Propagate Lot Number"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing
msgid "Propagated Lot Producing"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model,name:mrp_lot_number_propagation.model_stock_move
msgid "Stock Move"
msgstr ""
#. module: mrp_lot_number_propagation
#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing
msgid ""
"The BoM used on this manufacturing order is set to propagate lot number from"
" one of its components. The value will be computed once the corresponding "
"component is selected."
msgstr ""
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/product_template.py:0
#, python-format
msgid ""
"This component is configured to propagate its serial number in the following"
" Bill of Materials:{boms}'"
msgstr ""
#. module: mrp_lot_number_propagation
#: code:addons/mrp_lot_number_propagation/models/mrp_bom.py:0
#, python-format
msgid ""
"With 'Lot Number Propagation' enabled, a line has to be configured with the "
"'Propagate Lot Number' option."
msgstr ""

View File

@@ -0,0 +1,6 @@
from . import mrp_bom
from . import mrp_bom_line
from . import mrp_production
from . import product_product
from . import product_template
from . import stock_move

View File

@@ -0,0 +1,88 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import _, api, fields, models, tools
from odoo.exceptions import ValidationError
class MrpBom(models.Model):
_inherit = "mrp.bom"
lot_number_propagation = fields.Boolean(
default=False,
help=(
"Allow to propagate the lot/serial number "
"from a component to the finished product."
),
)
display_lot_number_propagation = fields.Boolean(
compute="_compute_display_lot_number_propagation"
)
@api.depends(
"type",
"product_tmpl_id.tracking",
"product_qty",
"product_uom_id",
"bom_line_ids.product_id.tracking",
"bom_line_ids.product_qty",
"bom_line_ids.product_uom_id",
)
def _compute_display_lot_number_propagation(self):
"""Check if a lot number can be propagated.
A lot number can be propagated from a component to the finished product if:
- the type of the BoM is normal (Manufacture this product)
- the finished product is tracked by serial number
- the quantity of the finished product is 1 and its UoM is unit
- there is at least one bom line, with a component tracked by serial,
having a quantity of 1 and its UoM is unit
"""
uom_unit = self.env.ref("uom.product_uom_unit")
for bom in self:
bom.display_lot_number_propagation = (
bom.type in self._get_lot_number_propagation_bom_types()
and bom.product_tmpl_id.tracking == "serial"
and tools.float_compare(
bom.product_qty, 1, precision_rounding=bom.product_uom_id.rounding
)
== 0
and bom.product_uom_id == uom_unit
and bom._has_tracked_product_to_propagate()
)
def _get_lot_number_propagation_bom_types(self):
return ["normal"]
def _has_tracked_product_to_propagate(self):
self.ensure_one()
uom_unit = self.env.ref("uom.product_uom_unit")
for line in self.bom_line_ids:
if (
line.product_id.tracking == "serial"
and line.product_uom_id == uom_unit
and tools.float_compare(
line.product_qty, 1, precision_rounding=line.product_uom_id.rounding
)
== 0
):
return True
return False
@api.onchange("display_lot_number_propagation")
def onchange_display_lot_number_propagation(self):
if not self.display_lot_number_propagation:
self.lot_number_propagation = False
@api.constrains("lot_number_propagation")
def _check_propagate_lot_number(self):
for bom in self:
if not bom.lot_number_propagation:
continue
if not bom.bom_line_ids.filtered("propagate_lot_number"):
raise ValidationError(
_(
"With 'Lot Number Propagation' enabled, a line has "
"to be configured with the 'Propagate Lot Number' option."
)
)

View File

@@ -0,0 +1,48 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class MrpBomLine(models.Model):
_inherit = "mrp.bom.line"
propagate_lot_number = fields.Boolean(
default=False,
)
display_propagate_lot_number = fields.Boolean(
compute="_compute_display_propagate_lot_number"
)
@api.depends(
"bom_id.display_lot_number_propagation",
"bom_id.lot_number_propagation",
)
def _compute_display_propagate_lot_number(self):
for line in self:
line.display_propagate_lot_number = (
line.bom_id.display_lot_number_propagation
and line.bom_id.lot_number_propagation
)
@api.constrains("propagate_lot_number")
def _check_propagate_lot_number(self):
"""
This function should check:
- if the bom has lot_number_propagation marked, there is one and
only one line of this bom with propagate_lot_number marked.
- the bom line being marked with lot_number_propagation is of the same
tracking type as the finished product
"""
for line in self:
if not line.bom_id.lot_number_propagation:
continue
if line.propagate_lot_number and line.product_id.tracking != "serial":
raise ValidationError(
_(
"Only components tracked by serial number can propagate "
"its lot/serial number to the finished product."
)
)

View File

@@ -0,0 +1,193 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from lxml import etree
from odoo import _, api, fields, models, tools
from odoo.exceptions import UserError
from odoo.osv import expression
from odoo.tools.safe_eval import safe_eval
from odoo.addons.base.models.ir_ui_view import (
transfer_modifiers_to_node,
transfer_node_to_modifiers,
)
class MrpProduction(models.Model):
_inherit = "mrp.production"
is_lot_number_propagated = fields.Boolean(
default=False,
readonly=True,
help=(
"Lot/serial number is propagated "
"from a component to the finished product."
),
)
propagated_lot_producing = fields.Char(
compute="_compute_propagated_lot_producing",
help=(
"The BoM used on this manufacturing order is set to propagate "
"lot number from one of its components. The value will be "
"computed once the corresponding component is selected."
),
)
@api.depends(
"move_raw_ids.propagate_lot_number",
"move_raw_ids.move_line_ids.qty_done",
"move_raw_ids.move_line_ids.lot_id",
)
def _compute_propagated_lot_producing(self):
for order in self:
order.propagated_lot_producing = False
move_with_lot = order._get_propagating_component_move()
line_with_sn = move_with_lot.move_line_ids.filtered(
lambda l: (
l.lot_id
and l.product_id.tracking == "serial"
and tools.float_compare(
l.qty_done, 1, precision_rounding=l.product_uom_id.rounding
)
== 0
)
)
if len(line_with_sn) == 1:
order.propagated_lot_producing = line_with_sn.lot_id.name
@api.onchange("bom_id")
def _onchange_bom_id_lot_number_propagation(self):
self.is_lot_number_propagated = self.bom_id.lot_number_propagation
def action_confirm(self):
res = super().action_confirm()
self._set_lot_number_propagation_data_from_bom()
return res
def _get_propagating_component_move(self):
self.ensure_one()
return self.move_raw_ids.filtered(lambda o: o.propagate_lot_number)
def _set_lot_number_propagation_data_from_bom(self):
"""Copy information from BoM to the manufacturing order."""
for order in self:
propagate_lot = order.bom_id.lot_number_propagation
if not propagate_lot:
continue
order.is_lot_number_propagated = propagate_lot
propagate_move = order.move_raw_ids.filtered(
lambda m: m.bom_line_id.propagate_lot_number
)
if not propagate_move:
raise UserError(
_(
"Bill of material is marked for lot number propagation, but "
"there are no components propagating lot number. "
"Please check BOM configuration."
)
)
elif len(propagate_move) > 1:
raise UserError(
_(
"Bill of material is marked for lot number propagation, but "
"there are multiple components propagating lot number. "
"Please check BOM configuration."
)
)
else:
propagate_move.propagate_lot_number = True
def _post_inventory(self, cancel_backorder=False):
self._create_and_assign_propagated_lot_number()
return super()._post_inventory(cancel_backorder=cancel_backorder)
def _create_and_assign_propagated_lot_number(self):
for order in self:
if not order.is_lot_number_propagated or order.lot_producing_id:
continue
finish_moves = order.move_finished_ids.filtered(
lambda m: m.product_id == order.product_id
and m.state not in ("done", "cancel")
)
if finish_moves and not finish_moves.quantity_done:
lot_model = self.env["stock.production.lot"]
lot = lot_model.search(
[
("product_id", "=", order.product_id.id),
("company_id", "=", order.company_id.id),
("name", "=", order.propagated_lot_producing),
],
limit=1,
)
if lot.quant_ids:
raise UserError(
_(
"Lot/Serial number %s already exists and has been used. "
"Unable to propagate it."
)
)
if not lot:
lot = self.env["stock.production.lot"].create(
{
"product_id": order.product_id.id,
"company_id": order.company_id.id,
"name": order.propagated_lot_producing,
}
)
order.with_context(lot_propagation=True).lot_producing_id = lot
def write(self, vals):
for order in self:
if (
order.is_lot_number_propagated
and "lot_producing_id" in vals
and not self.env.context.get("lot_propagation")
):
raise UserError(
_(
"Lot/Serial number is propagated from a component, "
"you are not allowed to change it."
)
)
return super().write(vals)
def fields_view_get(
self, view_id=None, view_type="form", toolbar=False, submenu=False
):
# Override to hide the "lot_producing_id" field + "action_generate_serial"
# button if the MO is configured to propagate a serial number
result = super().fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
)
if result.get("name") in self._views_to_adapt():
result["arch"] = self._fields_view_get_adapt_lot_tags_attrs(result)
return result
def _views_to_adapt(self):
"""Return the form view names bound to 'mrp.production' to adapt."""
return ["mrp.production.form"]
def _fields_view_get_adapt_lot_tags_attrs(self, view):
"""Hide elements related to lot if it is automatically propagated."""
doc = etree.XML(view["arch"])
tags = (
"//label[@for='lot_producing_id']",
"//field[@name='lot_producing_id']/..", # parent <div>
)
for xpath_expr in tags:
attrs_key = "invisible"
nodes = doc.xpath(xpath_expr)
for field in nodes:
attrs = safe_eval(field.attrib.get("attrs", "{}"))
if not attrs[attrs_key]:
continue
invisible_domain = expression.OR(
[attrs[attrs_key], [("is_lot_number_propagated", "=", True)]]
)
attrs[attrs_key] = invisible_domain
field.set("attrs", str(attrs))
modifiers = {}
transfer_node_to_modifiers(field, modifiers, self.env.context)
transfer_modifiers_to_node(modifiers, field)
return etree.tostring(doc, encoding="unicode")

View File

@@ -0,0 +1,13 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import api, models
class ProductProduct(models.Model):
_inherit = "product.product"
@api.constrains("tracking")
def _check_bom_propagate_lot_number(self):
for product in self:
product.product_tmpl_id._check_bom_propagate_lot_number()

View File

@@ -0,0 +1,42 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import _, api, models
from odoo.exceptions import ValidationError
class ProductTemplate(models.Model):
_inherit = "product.template"
@api.constrains("tracking")
def _check_bom_propagate_lot_number(self):
"""Block tracking type updates if the product is used by a BoM."""
for product in self:
if product.tracking == "serial":
continue
# Check BoMs
for bom in product.bom_ids:
if bom.lot_number_propagation:
raise ValidationError(
_(
"A BoM propagating serial numbers requires "
"this product to be tracked as such."
)
)
# Check lines of BoMs
bom_lines = self.env["mrp.bom.line"].search(
[
("product_id", "in", product.product_variant_ids.ids),
("propagate_lot_number", "=", True),
("bom_id.lot_number_propagation", "=", True),
]
)
if bom_lines:
boms = "\n- ".join(bom_lines.mapped("bom_id.display_name"))
boms = "\n- " + boms
raise ValidationError(
_(
"This component is configured to propagate its "
"serial number in the following Bill of Materials:{boms}'"
).format(boms=boms)
)

View File

@@ -0,0 +1,13 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import fields, models
class StockMove(models.Model):
_inherit = "stock.move"
propagate_lot_number = fields.Boolean(
default=False,
readonly=True,
)

View File

@@ -0,0 +1,2 @@
* Akim Juillerat <akim.juillerat@camptocamp.com>
* Sébastien Alix <sebastien.alix@camptocamp.com>

View File

@@ -0,0 +1 @@
Allow to propagate a lot number from a component to a finished product.

View File

@@ -0,0 +1 @@
* Add compatibility with lot number (in addition to serial number)

View File

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

View File

@@ -0,0 +1,159 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import random
import string
from odoo import fields
from odoo.tests import Form, common
class Common(common.SavepointCase):
LOT_NAME = "PROPAGATED-LOT"
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.bom = cls.env.ref("mrp.mrp_bom_desk")
cls.bom_product_template = cls.env.ref(
"mrp.product_product_computer_desk_product_template"
)
cls.bom_product_product = cls.env.ref("mrp.product_product_computer_desk")
cls.product_tracked_by_lot = cls.env.ref(
"mrp.product_product_computer_desk_leg"
)
cls.product_tracked_by_sn = cls.env.ref(
"mrp.product_product_computer_desk_head"
)
cls.product_template_tracked_by_sn = cls.env.ref(
"mrp.product_product_computer_desk_head_product_template"
)
cls.line_tracked_by_lot = cls.bom.bom_line_ids.filtered(
lambda o: o.product_id == cls.product_tracked_by_lot
)
cls.line_tracked_by_sn = cls.bom.bom_line_ids.filtered(
lambda o: o.product_id == cls.product_tracked_by_sn
)
cls.line_no_tracking = fields.first(
cls.bom.bom_line_ids.filtered(lambda o: o.product_id.tracking == "none")
)
@classmethod
def _update_qty_in_location(
cls, location, product, quantity, package=None, lot=None, in_date=None
):
quants = cls.env["stock.quant"]._gather(
product, location, lot_id=lot, package_id=package, strict=True
)
# this method adds the quantity to the current quantity, so remove it
quantity -= sum(quants.mapped("quantity"))
cls.env["stock.quant"]._update_available_quantity(
product,
location,
quantity,
package_id=package,
lot_id=lot,
in_date=in_date,
)
@classmethod
def _update_stock_component_qty(cls, order=None, bom=None, location=None):
if not order and not bom:
return
if order:
bom = order.bom_id
if not location:
location = cls.env.ref("stock.stock_location_stock")
for line in bom.bom_line_ids:
if line.product_id.type != "product":
continue
lot = None
if line.product_id.tracking != "none":
lot_name = "".join(
random.choice(string.ascii_lowercase) for i in range(10)
)
if line.propagate_lot_number:
lot_name = cls.LOT_NAME
vals = {
"product_id": line.product_id.id,
"company_id": line.company_id.id,
"name": lot_name,
}
lot = cls.env["stock.production.lot"].create(vals)
cls._update_qty_in_location(
location,
line.product_id,
line.product_qty,
lot=lot,
)
@classmethod
def _get_lot_quants(cls, lot, location=None):
quants = lot.quant_ids.filtered(lambda q: q.quantity > 0)
if location:
quants = quants.filtered(
lambda q: q.location_id.parent_path in location.parent_path
)
return quants
@classmethod
def _add_color_and_legs_variants(cls, product_template):
color_attribute = cls.env.ref("product.product_attribute_2")
color_att_value_white = cls.env.ref("product.product_attribute_value_3")
color_att_value_black = cls.env.ref("product.product_attribute_value_4")
legs_attribute = cls.env.ref("product.product_attribute_1")
legs_att_value_steel = cls.env.ref("product.product_attribute_value_1")
legs_att_value_alu = cls.env.ref("product.product_attribute_value_2")
cls._add_variants(
product_template,
{
color_attribute: [color_att_value_white, color_att_value_black],
legs_attribute: [legs_att_value_steel, legs_att_value_alu],
},
)
@classmethod
def _add_variants(cls, product_template, attribute_values_dict):
for attribute, att_values_list in attribute_values_dict.items():
cls.env["product.template.attribute.line"].create(
{
"product_tmpl_id": product_template.id,
"attribute_id": attribute.id,
"value_ids": [(6, 0, [att_val.id for att_val in att_values_list])],
}
)
@classmethod
def _create_bom_with_variants(cls):
attribute_values_dict = {
att_val.product_attribute_value_id.name: att_val.id
for att_val in cls.env["product.template.attribute.value"].search(
[("product_tmpl_id", "=", cls.bom_product_template.id)]
)
}
new_bom_form = Form(cls.env["mrp.bom"])
new_bom_form.product_tmpl_id = cls.bom_product_template
new_bom = new_bom_form.save()
bom_line_create_values = []
for product in cls.product_template_tracked_by_sn.product_variant_ids:
create_values = {"bom_id": new_bom.id}
create_values["product_id"] = product.id
att_values_commands = []
for att_value in product.product_template_attribute_value_ids:
att_values_commands.append(
(4, attribute_values_dict[att_value.name], 0)
)
create_values[
"bom_product_template_attribute_value_ids"
] = att_values_commands
bom_line_create_values.append(create_values)
cls.env["mrp.bom.line"].create(bom_line_create_values)
new_bom_form = Form(new_bom)
new_bom_form.lot_number_propagation = True
for line_position, _bom_line in enumerate(new_bom.bom_line_ids):
new_bom_line_form = new_bom_form.bom_line_ids.edit(line_position)
new_bom_line_form.propagate_lot_number = True
new_bom_line_form.save()
return new_bom_form.save()

View File

@@ -0,0 +1,72 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo.exceptions import ValidationError
from odoo.tests.common import Form
from .common import Common
class TestMrpBom(Common):
def test_bom_display_lot_number_propagation(self):
self.assertTrue(self.bom.display_lot_number_propagation)
self.bom.product_tmpl_id.tracking = "none"
self.assertFalse(self.bom.display_lot_number_propagation)
def test_bom_line_check_propagate_lot_number_not_tracked(self):
form = Form(self.bom)
form.lot_number_propagation = True
# Flag a line that can't be propagated
line_form = form.bom_line_ids.edit(2) # line without tracking
line_form.propagate_lot_number = True
line_form.save()
with self.assertRaisesRegex(ValidationError, "Only components tracked"):
form.save()
def test_bom_line_check_propagate_lot_number_tracked_by_lot(self):
form = Form(self.bom)
form.lot_number_propagation = True
# Flag a line tracked by lot (not SN) which is not supported
line_form = form.bom_line_ids.edit(1)
line_form.propagate_lot_number = True
line_form.save()
with self.assertRaisesRegex(ValidationError, "Only components tracked"):
form.save()
def test_bom_line_check_propagate_lot_number_same_tracking(self):
form = Form(self.bom)
form.lot_number_propagation = True
# Flag a line whose tracking type is the same than the finished product
line_form = form.bom_line_ids.edit(0)
line_form.propagate_lot_number = True
line_form.save()
form.save()
def test_bom_check_propagate_lot_number(self):
# Configure the BoM to propagate the lot/SN without enabling any line
with self.assertRaisesRegex(ValidationError, "a line has to be configured"):
self.bom.lot_number_propagation = True
def test_reset_tracking_on_bom_product(self):
# Configure the BoM to propagate the lot/SN
with Form(self.bom) as form:
form.lot_number_propagation = True
line_form = form.bom_line_ids.edit(0) # Line tracked by SN
line_form.propagate_lot_number = True
line_form.save()
form.save()
# Reset the tracking on the finished product
with self.assertRaisesRegex(ValidationError, "A BoM propagating"):
self.bom.product_tmpl_id.tracking = "none"
def test_reset_tracking_on_bom_component(self):
# Configure the BoM to propagate the lot/SN
with Form(self.bom) as form:
form.lot_number_propagation = True
line_form = form.bom_line_ids.edit(0) # Line tracked by SN
line_form.propagate_lot_number = True
line_form.save()
form.save()
# Reset the tracking on the component which propagates the SN
with self.assertRaisesRegex(ValidationError, "This component is"):
self.line_tracked_by_sn.product_id.tracking = "none"

View File

@@ -0,0 +1,147 @@
# Copyright 2022 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo.exceptions import UserError
from odoo.tests.common import Form
from .common import Common
class TestMrpProduction(Common):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Configure the BoM to propagate lot number
cls._configure_bom()
cls.order = cls._create_order(cls.bom_product_product, cls.bom)
@classmethod
def _configure_bom(cls):
with Form(cls.bom) as form:
form.lot_number_propagation = True
line_form = form.bom_line_ids.edit(0) # Line tracked by SN
line_form.propagate_lot_number = True
line_form.save()
form.save()
@classmethod
def _create_order(cls, product, bom):
with Form(cls.env["mrp.production"]) as form:
form.product_id = product
form.bom_id = bom
return form.save()
def _set_qty_done(self, order):
for line in order.move_raw_ids.move_line_ids:
line.qty_done = line.product_uom_qty
order.qty_producing = order.product_qty
def test_order_propagated_lot_producing(self):
self.assertTrue(self.order.is_lot_number_propagated) # set by onchange
self._update_stock_component_qty(self.order)
self.order.action_confirm()
self.order.action_assign()
self.assertTrue(self.order.is_lot_number_propagated) # set by action_confirm
self.assertTrue(any(self.order.move_raw_ids.mapped("propagate_lot_number")))
self._set_qty_done(self.order)
self.assertEqual(self.order.propagated_lot_producing, self.LOT_NAME)
def test_order_write_lot_producing_id_not_allowed(self):
with self.assertRaisesRegex(UserError, "not allowed"):
self.order.write({"lot_producing_id": False})
def test_order_post_inventory(self):
self._update_stock_component_qty(self.order)
self.order.action_confirm()
self.order.action_assign()
self._set_qty_done(self.order)
self.assertTrue(self.order.is_lot_number_propagated) # set by action_confirm
self.order.button_mark_done()
self.assertEqual(self.order.lot_producing_id.name, self.LOT_NAME)
def test_order_post_inventory_lot_already_exists_but_not_used(self):
self._update_stock_component_qty(self.order)
self.order.action_confirm()
self.order.action_assign()
self._set_qty_done(self.order)
self.assertEqual(self.order.propagated_lot_producing, self.LOT_NAME)
# Create a lot with the same number for the finished product
# without any stock/quants (so not used at all) before validating the MO
existing_lot = self.env["stock.production.lot"].create(
{
"product_id": self.order.product_id.id,
"company_id": self.order.company_id.id,
"name": self.order.propagated_lot_producing,
}
)
self.order.button_mark_done()
self.assertEqual(self.order.lot_producing_id, existing_lot)
def test_order_post_inventory_lot_already_exists_and_used(self):
self._update_stock_component_qty(self.order)
self.order.action_confirm()
self.order.action_assign()
self._set_qty_done(self.order)
self.assertEqual(self.order.propagated_lot_producing, self.LOT_NAME)
# Create a lot with the same number for the finished product
# with some stock/quants (so it is considered as used) before
# validating the MO
existing_lot = self.env["stock.production.lot"].create(
{
"product_id": self.order.product_id.id,
"company_id": self.order.company_id.id,
"name": self.order.propagated_lot_producing,
}
)
self._update_qty_in_location(
self.env.ref("stock.stock_location_stock"),
self.order.product_id,
1,
lot=existing_lot,
)
with self.assertRaisesRegex(UserError, "already exists and has been used"):
self.order.button_mark_done()
def test_confirm_with_variant_ok(self):
self._add_color_and_legs_variants(self.bom_product_template)
self._add_color_and_legs_variants(self.product_template_tracked_by_sn)
new_bom = self._create_bom_with_variants()
self.assertTrue(new_bom.lot_number_propagation)
# As all variants must have a single component
# where lot must be propagated, there should not be any error
for product in self.bom_product_template.product_variant_ids:
new_order = self._create_order(product, new_bom)
new_order.action_confirm()
def test_confirm_with_variant_multiple(self):
self._add_color_and_legs_variants(self.bom_product_template)
self._add_color_and_legs_variants(self.product_template_tracked_by_sn)
new_bom = self._create_bom_with_variants()
# Remove application on variant for first bom line
# with this only the first variant of the product template
# will have a single component where lot must be propagated
new_bom.bom_line_ids[0].bom_product_template_attribute_value_ids = [(5, 0, 0)]
for cnt, product in enumerate(self.bom_product_template.product_variant_ids):
new_order = self._create_order(product, new_bom)
if cnt == 0:
new_order.action_confirm()
else:
with self.assertRaisesRegex(UserError, "multiple components"):
new_order.action_confirm()
def test_confirm_with_variant_no(self):
self._add_color_and_legs_variants(self.bom_product_template)
self._add_color_and_legs_variants(self.product_template_tracked_by_sn)
new_bom = self._create_bom_with_variants()
for cnt, product in enumerate(self.bom_product_template.product_variant_ids):
new_order = self._create_order(product, new_bom)
if cnt == 0:
# Fake the case with no component by removing the flag
new_order.move_raw_ids.bom_line_id.write(
{"propagate_lot_number": False}
)
with self.assertRaisesRegex(UserError, "no component"):
new_order.action_confirm()
new_order.action_assign()
else:
new_order.action_confirm()

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2022 Camptocamp SA
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="mrp_bom_form_view" model="ir.ui.view">
<field name="name">mrp.bom.form.inherit</field>
<field name="model">mrp.bom</field>
<field name="inherit_id" ref="mrp.mrp_bom_form_view" />
<field name="arch" type="xml">
<field name="company_id" position="after">
<field name="display_lot_number_propagation" invisible="1" />
<field
name="lot_number_propagation"
attrs="{'invisible': [('display_lot_number_propagation', '=', False)]}"
/>
</field>
<xpath expr="//field[@name='bom_line_ids']/tree" position="inside">
<field name="display_propagate_lot_number" invisible="1" />
<field
name="propagate_lot_number"
attrs="{'column_invisible': ['|', ('parent.display_lot_number_propagation', '=', False), ('parent.lot_number_propagation', '=', False)]}"
/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2022 Camptocamp SA
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="mrp_production_form_view" model="ir.ui.view">
<field name="name">mrp.production.form.inherit</field>
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_form_view" />
<field name="arch" type="xml">
<!-- Place new fields in the first group while being compatible with OE -->
<xpath expr="//field[@name='id']/.." position="inside">
<field name="is_lot_number_propagated" force_save="1" />
</xpath>
<label for="lot_producing_id" position="before">
<field
name="propagated_lot_producing"
string="Lot/Serial Number"
attrs="{'invisible': [('is_lot_number_propagated', '=', False)]}"
/>
</label>
</field>
</record>
</odoo>

View File

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

View File

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