Merge PR #681 into 13.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2021-10-20 10:04:22 +00:00
20 changed files with 959 additions and 0 deletions

View File

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

View File

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

View File

@@ -0,0 +1,98 @@
==========================
Stock whole kit constraint
==========================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github
:target: https://github.com/OCA/manufacture/tree/13.0/stock_whole_kit_constraint
:alt: OCA/manufacture
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/manufacture-13-0/manufacture-13-0-stock_whole_kit_constraint
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/129/13.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows to configure a product that has a BoM of type kit to disallow partial
deliveries so that the components can't be partially delivered.
**Table of contents**
.. contents::
:local:
Configuration
=============
To allow/disallow the partial delivery of kits:
#. Go to the kit product template or variant and then to the *Inventory* tab,
*Logistics* group.
#. The "Allow Partial Kit" check controls this. If marked, it will allow it.
#. By default, the check is not marked.
Usage
=====
To use this module, you need to:
#. Make a delivery picking with a kit product.
#. Try to deliver it partially.
#. An error will raise.
If you want to deliver other items in the picking you can do so and leave the
whole kit components units pending in a backorder.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/manufacture/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/manufacture/issues/new?body=module:%20stock_whole_kit_constraint%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Tecnativa
Contributors
~~~~~~~~~~~~
* Tecnativa <https://www.tecnativa.com>__
* David Vidal
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/manufacture <https://github.com/OCA/manufacture/tree/13.0/stock_whole_kit_constraint>`_ 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,14 @@
# Copyright 2021 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Stock whole kit constraint",
"summary": "Avoid to deliver a kit partially",
"version": "13.0.1.0.0",
"category": "Stock",
"website": "https://github.com/OCA/manufacture",
"author": "Tecnativa, Odoo Community Association (OCA)",
"license": "AGPL-3",
"installable": True,
"depends": ["mrp"],
"data": ["views/product_template_views.xml"],
}

View File

@@ -0,0 +1,52 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_whole_kit_constraint
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-19 10:39+0000\n"
"PO-Revision-Date: 2021-03-19 11:41+0100\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"Language: es\n"
"X-Generator: Poedit 2.3\n"
#. module: stock_whole_kit_constraint
#: model:ir.model.fields,field_description:stock_whole_kit_constraint.field_product_product__allow_partial_kit_delivery
#: model:ir.model.fields,field_description:stock_whole_kit_constraint.field_product_template__allow_partial_kit_delivery
#: model:ir.model.fields,field_description:stock_whole_kit_constraint.field_stock_move__allow_partial_kit_delivery
msgid "Allow Partial Kit Delivery"
msgstr "Permitir entrega parcial de kit"
#. module: stock_whole_kit_constraint
#: model:ir.model.fields,help:stock_whole_kit_constraint.field_product_product__allow_partial_kit_delivery
#: model:ir.model.fields,help:stock_whole_kit_constraint.field_product_template__allow_partial_kit_delivery
msgid "If not set when a kit product components are delivered, it won't be allowed to do it partially."
msgstr "No no está activo, cuando haya una entrega con un kit, no se permitirá la entrega parcial de sus componentes."
#. module: stock_whole_kit_constraint
#: model:ir.model,name:stock_whole_kit_constraint.model_product_template
msgid "Product Template"
msgstr "Plantilla de producto"
#. module: stock_whole_kit_constraint
#: model:ir.model,name:stock_whole_kit_constraint.model_stock_move
msgid "Stock Move"
msgstr "Movimiento de existencias"
#. module: stock_whole_kit_constraint
#: model:ir.model,name:stock_whole_kit_constraint.model_stock_picking
msgid "Transfer"
msgstr "Albarán"
#. module: stock_whole_kit_constraint
#: code:addons/stock_whole_kit_constraint/models/stock_picking.py:23
#, python-format
msgid "You can't make a partial delivery of components of the %s kit"
msgstr "No puede realizar una entrega parcial de los componentes del kit %s"

View File

@@ -0,0 +1,49 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_whole_kit_constraint
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.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: stock_whole_kit_constraint
#: model:ir.model.fields,field_description:stock_whole_kit_constraint.field_product_product__allow_partial_kit_delivery
#: model:ir.model.fields,field_description:stock_whole_kit_constraint.field_product_template__allow_partial_kit_delivery
#: model:ir.model.fields,field_description:stock_whole_kit_constraint.field_stock_move__allow_partial_kit_delivery
msgid "Allow Partial Kit Delivery"
msgstr ""
#. module: stock_whole_kit_constraint
#: model:ir.model.fields,help:stock_whole_kit_constraint.field_product_product__allow_partial_kit_delivery
#: model:ir.model.fields,help:stock_whole_kit_constraint.field_product_template__allow_partial_kit_delivery
msgid "If not set, and this product is delivered with a BoM of type kit, partial deliveries of the components won't be allowed."
msgstr ""
#. module: stock_whole_kit_constraint
#: model:ir.model,name:stock_whole_kit_constraint.model_product_template
msgid "Product Template"
msgstr ""
#. module: stock_whole_kit_constraint
#: model:ir.model,name:stock_whole_kit_constraint.model_stock_move
msgid "Stock Move"
msgstr ""
#. module: stock_whole_kit_constraint
#: model:ir.model,name:stock_whole_kit_constraint.model_stock_picking
msgid "Transfer"
msgstr ""
#. module: stock_whole_kit_constraint
#: code:addons/stock_whole_kit_constraint/models/stock_picking.py:23
#, python-format
msgid "You can't make a partial delivery of components of the %s kit"
msgstr ""

View File

@@ -0,0 +1,3 @@
from . import product_template
from . import stock_move
from . import stock_picking

View File

@@ -0,0 +1,13 @@
# Copyright 2021 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProductTemplate(models.Model):
_inherit = "product.template"
allow_partial_kit_delivery = fields.Boolean(
default=True,
help="If not set, and this product is delivered with a BoM of type "
"kit, partial deliveries of the components won't be allowed.",
)

View File

@@ -0,0 +1,51 @@
# Copyright 2021 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class StockMove(models.Model):
_inherit = "stock.move"
allow_partial_kit_delivery = fields.Boolean(
compute="_compute_allow_partial_kit_delivery", compute_sudo=True,
)
@api.depends("product_id.product_tmpl_id.allow_partial_kit_delivery", "state")
def _compute_allow_partial_kit_delivery(self):
"""Take it from the product only if it's a kit"""
self.write({"allow_partial_kit_delivery": True})
for move in self.filtered(
lambda x: x.product_id and x.state not in ["done", "cancel"]
):
# If it isn't a kit it will always be True
if not move.bom_line_id or move.bom_line_id.bom_id.type != "phantom":
move.allow_partial_kit_delivery = True
continue
move.allow_partial_kit_delivery = (
move.bom_line_id.bom_id.product_tmpl_id.allow_partial_kit_delivery
)
def _check_backorder_moves(self):
"""Check if there are partial deliveries on any set of moves. The
computing is done in the same way the main picking method does it """
quantity_todo = {}
quantity_done = {}
for move in self:
quantity_todo.setdefault(move.product_id.id, 0)
quantity_done.setdefault(move.product_id.id, 0)
quantity_todo[move.product_id.id] += move.product_uom_qty
quantity_done[move.product_id.id] += move.quantity_done
for ops in self.mapped("move_line_ids").filtered(
lambda x: x.package_id and not x.product_id and not x.move_id
):
for quant in ops.package_id.quant_ids:
quantity_done.setdefault(quant.product_id.id, 0)
quantity_done[quant.product_id.id] += quant.qty
for pack in self.mapped("move_line_ids").filtered(
lambda x: x.product_id and not x.move_id
):
quantity_done.setdefault(pack.product_id.id, 0)
quantity_done[pack.product_id.id] += pack.product_uom_id._compute_quantity(
pack.qty_done, pack.product_id.uom_id
)
return any(quantity_done[x] < quantity_todo.get(x, 0) for x in quantity_done)

View File

@@ -0,0 +1,29 @@
# Copyright 2021 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, models
from odoo.exceptions import ValidationError
class StockPicking(models.Model):
_inherit = "stock.picking"
def _check_backorder(self):
"""On the moment of the picking validation, we'll check wether there
are kits that can't be partially delivered or not"""
moves = self.mapped("move_lines").filtered(
lambda x: not x.allow_partial_kit_delivery and x.bom_line_id
)
boms = moves.mapped("bom_line_id.bom_id")
for bom in boms:
bom_moves = moves.filtered(lambda x: x.bom_line_id.bom_id == bom)
# We can put it in backorder if the whole kit goes
if not sum(bom_moves.mapped("quantity_done")):
continue
if bom_moves._check_backorder_moves():
raise ValidationError(
_(
"You can't make a partial delivery of components of the "
"%s kit" % bom.product_tmpl_id.display_name
)
)
return super()._check_backorder()

View File

@@ -0,0 +1,6 @@
To allow/disallow the partial delivery of kits:
#. Go to the kit product template or variant and then to the *Inventory* tab,
*Logistics* group.
#. The "Allow Partial Kit" check controls this. If marked, it will allow it.
#. By default, the check is not marked.

View File

@@ -0,0 +1,3 @@
* Tecnativa <https://www.tecnativa.com>__
* David Vidal

View File

@@ -0,0 +1,2 @@
This module allows to configure a product that has a BoM of type kit to disallow partial
deliveries so that the components can't be partially delivered.

View File

@@ -0,0 +1,8 @@
To use this module, you need to:
#. Make a delivery picking with a kit product.
#. Try to deliver it partially.
#. An error will raise.
If you want to deliver other items in the picking you can do so and leave the
whole kit components units pending in a backorder.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,446 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
<title>Stock whole kit constraint</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="stock-whole-kit-constraint">
<h1 class="title">Stock whole kit constraint</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/manufacture/tree/13.0/stock_whole_kit_constraint"><img alt="OCA/manufacture" src="https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/manufacture-13-0/manufacture-13-0-stock_whole_kit_constraint"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/129/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows to configure a product that has a BoM of type kit to disallow partial
deliveries so that the components cant be partially delivered.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="id1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id1">Configuration</a></h1>
<p>To allow/disallow the partial delivery of kits:</p>
<ol class="arabic simple">
<li>Go to the kit product template or variant and then to the <em>Inventory</em> tab,
<em>Logistics</em> group.</li>
<li>The “Allow Partial Kit” check controls this. If marked, it will allow it.</li>
<li>By default, the check is not marked.</li>
</ol>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id2">Usage</a></h1>
<p>To use this module, you need to:</p>
<ol class="arabic simple">
<li>Make a delivery picking with a kit product.</li>
<li>Try to deliver it partially.</li>
<li>An error will raise.</li>
</ol>
<p>If you want to deliver other items in the picking you can do so and leave the
whole kit components units pending in a backorder.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/manufacture/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/manufacture/issues/new?body=module:%20stock_whole_kit_constraint%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>Tecnativa</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li>Tecnativa &lt;<a class="reference external" href="https://www.tecnativa.com">https://www.tecnativa.com</a>&gt;__<ul>
<li>David Vidal</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/manufacture/tree/13.0/stock_whole_kit_constraint">OCA/manufacture</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

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

View File

@@ -0,0 +1,145 @@
# Copyright 2021 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.exceptions import ValidationError
from odoo.tests import Form, common
class TestStockWholeKitConstraint(common.SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.customer = cls.env["res.partner"].create({"name": "Mr. Odoo"})
# Kit 1 that can be partially delivered
cls.product_kit_1 = cls.env["product.product"].create(
{"name": "Product Kit 1", "type": "consu"}
)
cls.component_1_kit_1 = cls.env["product.product"].create(
{"name": "Component 1 Kit 1", "type": "product"}
)
cls.component_2_kit_1 = cls.env["product.product"].create(
{"name": "Component 2 Kit 1", "type": "product"}
)
bom_form = Form(cls.env["mrp.bom"])
bom_form.product_tmpl_id = cls.product_kit_1.product_tmpl_id
bom_form.product_id = cls.product_kit_1
bom_form.type = "phantom"
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.component_1_kit_1
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.component_2_kit_1
cls.bom_kit_1 = bom_form.save()
# Kit 2 - disallow partial deliveries
cls.product_kit_2 = cls.env["product.product"].create(
{
"name": "Product Kit 2",
"type": "consu",
"allow_partial_kit_delivery": False,
}
)
cls.component_1_kit_2 = cls.env["product.product"].create(
{"name": "Component 1 Kit 2", "type": "product"}
)
cls.component_2_kit_2 = cls.env["product.product"].create(
{"name": "Component 2 Kit 2", "type": "product"}
)
bom_form = Form(cls.env["mrp.bom"])
bom_form.product_tmpl_id = cls.product_kit_2.product_tmpl_id
bom_form.product_id = cls.product_kit_2
bom_form.type = "phantom"
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.component_1_kit_2
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.component_2_kit_2
cls.bom_kit_2 = bom_form.save()
# Manufactured product as control
cls.product_mrp = cls.env["product.product"].create(
{
"name": "Product Kit 2",
"type": "consu",
# Force the setting in a manufactured product.
# It should not affect it
"allow_partial_kit_delivery": False,
}
)
bom_form = Form(cls.env["mrp.bom"])
bom_form.product_tmpl_id = cls.product_mrp.product_tmpl_id
bom_form.product_id = cls.product_mrp
bom_form.type = "normal"
with bom_form.bom_line_ids.new() as line:
line.product_id = cls.component_1_kit_2
cls.bom_mrp = bom_form.save()
# Not a kit product as control
cls.regular_product = cls.env["product.product"].create(
{
"name": "Regular test product",
"type": "product",
# Force the setting in a regular product. It should not affect it
"allow_partial_kit_delivery": False,
}
)
# Delivery picking
picking_form = Form(cls.env["stock.picking"])
picking_form.picking_type_id = cls.env.ref("stock.picking_type_out")
picking_form.partner_id = cls.customer
with picking_form.move_ids_without_package.new() as move:
move.product_id = cls.product_kit_1
move.product_uom_qty = 3.0
with picking_form.move_ids_without_package.new() as move:
move.product_id = cls.product_kit_2
move.product_uom_qty = 3.0
with picking_form.move_ids_without_package.new() as move:
move.product_id = cls.product_mrp
move.product_uom_qty = 3.0
with picking_form.move_ids_without_package.new() as move:
move.product_id = cls.regular_product
move.product_uom_qty = 3.0
cls.customer_picking = picking_form.save()
cls.customer_picking.action_confirm()
def test_01_all_partially_done_but_the_disallow_partial_kit(self):
"""No quantity is done for the kit disallowed and only partially for the
others so the backorder wizard raises."""
moves_allowed = self.customer_picking.move_lines.filtered(
lambda x: x.bom_line_id.bom_id != self.bom_kit_2
)
moves_allowed.write({"quantity_done": 1})
response = self.customer_picking.button_validate()
self.assertEqual("stock.backorder.confirmation", response.get("res_model"))
def test_02_all_done_but_partial_disallow_partial_kit(self):
"""We try to deliver partially the disallowed kit"""
moves_disallowed = self.customer_picking.move_lines.filtered(
lambda x: x.bom_line_id.bom_id == self.bom_kit_2
)
moves_disallowed.write({"quantity_done": 1})
with self.assertRaises(ValidationError):
self.customer_picking.button_validate()
# We can split the picking if the whole kit components are delivered
moves_disallowed.write({"quantity_done": 3})
# We've got a backorder on the rest of the lines
response = self.customer_picking.button_validate()
self.assertEqual("stock.backorder.confirmation", response.get("res_model"))
def test_03_all_done(self):
"""Deliver the whole picking normally"""
self.customer_picking.move_lines.write({"quantity_done": 3})
self.customer_picking.button_validate()
self.assertEqual("done", self.customer_picking.state)
def test_04_manual_move_lines(self):
"""If a user adds manual operations, we should consider it as well"""
# We need to enable detaild operations to test this case
self.customer_picking.picking_type_id.show_operations = True
picking_form = Form(self.customer_picking)
for product in (self.bom_kit_1 + self.bom_kit_2).mapped(
"bom_line_ids.product_id"
):
with picking_form.move_line_ids_without_package.new() as line:
line.product_id = product
line.qty_done = 3
picking_form.save()
self.customer_picking.move_lines.filtered(
lambda x: x.product_id in (self.product_mrp, self.regular_product)
).write({"quantity_done": 3})
self.customer_picking.button_validate()
self.assertEqual("done", self.customer_picking.state)

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="product_template_form_view_bom_button">
<field name="model">product.template</field>
<field name="inherit_id" ref="mrp.product_template_form_view_bom_button" />
<field name="groups_id" eval="[(4, ref('stock.group_stock_manager'))]" />
<field name="arch" type="xml">
<xpath expr="//group[@name='group_lots_and_weight']" position="inside">
<field
name="allow_partial_kit_delivery"
attrs="{'invisible': [('bom_count', '=', 0), ('type', 'not in', ['consu', 'product'])]}"
/>
</xpath>
</field>
</record>
<record id="product_product_form_view_bom_button" model="ir.ui.view">
<field name="model">product.product</field>
<field name="inherit_id" ref="mrp.product_product_form_view_bom_button" />
<field name="groups_id" eval="[(4, ref('stock.group_stock_manager'))]" />
<field name="arch" type="xml">
<xpath expr="//group[@name='group_lots_and_weight']" position="inside">
<field
name="allow_partial_kit_delivery"
attrs="{'invisible': [('bom_count', '=', 0), ('type', 'not in', ['consu', 'product'])]}"
/>
</xpath>
</field>
</record>
</odoo>