Initial commit: OCA Mrp packages (117 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:05 +02:00
commit 277e84fd7a
4403 changed files with 395154 additions and 0 deletions

View file

@ -0,0 +1,45 @@
# Unbuild orders with return subcontracting
Odoo addon: mrp_unbuild_subcontracting
## Installation
```bash
pip install odoo-bringout-oca-manufacture-mrp_unbuild_subcontracting
```
## Dependencies
This addon depends on:
- mrp_account
- mrp_subcontracting_purchase
## Manifest Information
- **Name**: Unbuild orders with return subcontracting
- **Version**: 16.0.1.0.0
- **Category**: Manufacture
- **License**: LGPL-3
- **Installable**: True
## Source
Based on [OCA/manufacture](https://github.com/OCA/manufacture) branch 16.0, addon `mrp_unbuild_subcontracting`.
## License
This package maintains the original LGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Mrp_unbuild_subcontracting Module - mrp_unbuild_subcontracting
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for mrp_unbuild_subcontracting. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,6 @@
# Dependencies
This addon depends on:
- [mrp_account](../../odoo-bringout-oca-ocb-mrp_account)
- [mrp_subcontracting_purchase](../../odoo-bringout-oca-ocb-mrp_subcontracting_purchase)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon mrp_unbuild_subcontracting or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-manufacture-mrp_unbuild_subcontracting"
# or
uv pip install odoo-bringout-oca-manufacture-mrp_unbuild_subcontracting"
```

View file

@ -0,0 +1,14 @@
# Models
Detected core models and extensions in mrp_unbuild_subcontracting.
```mermaid
classDiagram
class mrp_unbuild
class stock_move
class stock_picking
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: mrp_unbuild_subcontracting. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon mrp_unbuild_subcontracting
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,8 @@
# Security
This module does not define custom security rules or access controls beyond Odoo defaults.
Default Odoo security applies:
- Base user access through standard groups
- Model access inherited from dependencies
- No custom row-level security rules

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon mrp_unbuild_subcontracting
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,80 @@
=========================================
Unbuild orders with return subcontracting
=========================================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:11f2e6d4a0a7c585038a75213c3ea46945a33fa40ecde95aef22f0cc999c8853
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github
:target: https://github.com/OCA/manufacture/tree/16.0/mrp_unbuild_subcontracting
:alt: OCA/manufacture
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/manufacture-16-0/manufacture-16-0-mrp_unbuild_subcontracting
: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/manufacture&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module automatically creates an unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated.
To view the unbuilds created, you have to select the operation Subcontracted Unbuild Orders in debug mode
**Table of contents**
.. contents::
:local:
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 to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_unbuild_subcontracting%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
~~~~~~~
* ForgeFlow
Contributors
~~~~~~~~~~~~
* `ForgeFlow <https://www.forgeflow.com>`_:
* Thiago Mulero <thiago.mulero@forgeflow.com>
* Bernat Puig <bernat.puig@forgeflow.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/manufacture <https://github.com/OCA/manufacture/tree/16.0/mrp_unbuild_subcontracting>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1,15 @@
# Copyright 2022 ForgeFlow S.L. (https://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
{
"name": "Unbuild orders with return subcontracting",
"version": "16.0.1.0.0",
"license": "LGPL-3",
"category": "Manufacture",
"summary": "Unbuild orders are created automatically "
"when is returned a product subcontracted",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/manufacture",
"depends": ["mrp_account", "mrp_subcontracting_purchase"],
"data": ["views/mrp_unbuild_views.xml"],
"installable": True,
}

View file

@ -0,0 +1,78 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_unbuild_subcontracting
#
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: mrp_unbuild_subcontracting
#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid ""
"An unbuild order is used to break down a finished product into its "
"components."
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__is_subcontracted
msgid "Is Subcontracted?"
msgstr "Da li je podugovarano?"
#. module: mrp_unbuild_subcontracting
#. odoo-python
#: code:addons/mrp_unbuild_subcontracting/models/stock_picking.py:0
#, python-format
msgid ""
"It's not possible to create the subcontracting unbuild order\n"
"The subcontract move %(smn)s is linked with more than one manufacturing order: %(jmm)s"
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid "No unbuild order found"
msgstr "Nije pronađen nalog za rastavljanje"
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_move
msgid "Stock Move"
msgstr "Skladišno kretanje"
#. module: mrp_unbuild_subcontracting
#: model:ir.ui.menu,name:mrp_unbuild_subcontracting.menu_mrp_unbuild_subcontracted
msgid "Subcontracted Unbuild Orders"
msgstr "Podugovarani radni nalozi demontaže"
#. module: mrp_unbuild_subcontracting
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__subcontracted_unbuild_ids
msgid "Subcontracted unbuilds"
msgstr "Podugovarane demontaže"
#. module: mrp_unbuild_subcontracting
#. odoo-python
#: code:addons/mrp_unbuild_subcontracting/models/stock_move.py:0
#, python-format
msgid "To subcontract, use a planned transfer."
msgstr "Za podugovaranje, koristite planiran transfer"
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_picking
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__picking_id
msgid "Transfer"
msgstr "Prijenos"
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_mrp_unbuild
msgid "Unbuild Order"
msgstr "Nalog rastavljanja"
#. module: mrp_unbuild_subcontracting
#: model:ir.actions.act_window,name:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid "Unbuild Orders - Subcontracted"
msgstr "Radni nalozi demontaže - Podugovarani"

View file

@ -0,0 +1,102 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_unbuild_subcontracting
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-12 20: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: mrp_unbuild_subcontracting
#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid ""
"An unbuild order is used to break down a finished product into its "
"components."
msgstr ""
"Un ordine di smontaggio viene utilizzato per separare un prodotto finito nei "
"sui componenti."
#. module: mrp_unbuild_subcontracting
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__is_subcontracted
msgid "Is Subcontracted?"
msgstr "In conto lavoro?"
#. module: mrp_unbuild_subcontracting
#. odoo-python
#: code:addons/mrp_unbuild_subcontracting/models/stock_picking.py:0
#, python-format
msgid ""
"It's not possible to create the subcontracting unbuild order\n"
"The subcontract move %(smn)s is linked with more than one manufacturing "
"order: %(jmm)s"
msgstr ""
"Non è possibile creare l'ordine di smontaggio del conto lavoro\n"
"Lo spostamento del conto lavoro %(smn)s è collegato a più di un ordine di "
"produzione: %(jmm)s"
#. module: mrp_unbuild_subcontracting
#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid "No unbuild order found"
msgstr "Nessun ordine di smontaggio trovato"
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_move
msgid "Stock Move"
msgstr "Movimento di magazzino"
#. module: mrp_unbuild_subcontracting
#: model:ir.ui.menu,name:mrp_unbuild_subcontracting.menu_mrp_unbuild_subcontracted
msgid "Subcontracted Unbuild Orders"
msgstr "Ordini di smontaggio in conto lavoro"
#. module: mrp_unbuild_subcontracting
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__subcontracted_unbuild_ids
msgid "Subcontracted unbuilds"
msgstr "Smontaggi conto lavoro"
#. module: mrp_unbuild_subcontracting
#. odoo-python
#: code:addons/mrp_unbuild_subcontracting/models/stock_move.py:0
#, python-format
msgid "To subcontract, use a planned transfer."
msgstr "Per conto lavoro, utilizzare un trasferimento pianificato."
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_picking
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__picking_id
msgid "Transfer"
msgstr "Trasferimento"
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_mrp_unbuild
msgid "Unbuild Order"
msgstr "Ordine di smontaggio"
#. module: mrp_unbuild_subcontracting
#: model:ir.actions.act_window,name:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid "Unbuild Orders - Subcontracted"
msgstr "Ordini smontaggio - In conto lavoro"
#~ msgid "Display Name"
#~ msgstr "Nome visualizzato"
#~ msgid "ID"
#~ msgstr "ID"
#~ msgid "Is Subcontracted"
#~ msgstr "In conto lavoro"
#~ msgid "Last Modified on"
#~ msgstr "Ultima modifica il"
#~ msgid "Suncontracted unbuilds"
#~ msgstr "Smontaggi in conto lavoro"

View file

@ -0,0 +1,78 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_unbuild_subcontracting
#
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: mrp_unbuild_subcontracting
#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid ""
"An unbuild order is used to break down a finished product into its "
"components."
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__is_subcontracted
msgid "Is Subcontracted?"
msgstr ""
#. module: mrp_unbuild_subcontracting
#. odoo-python
#: code:addons/mrp_unbuild_subcontracting/models/stock_picking.py:0
#, python-format
msgid ""
"It's not possible to create the subcontracting unbuild order\n"
"The subcontract move %(smn)s is linked with more than one manufacturing order: %(jmm)s"
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model_terms:ir.actions.act_window,help:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid "No unbuild order found"
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_move
msgid "Stock Move"
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model:ir.ui.menu,name:mrp_unbuild_subcontracting.menu_mrp_unbuild_subcontracted
msgid "Subcontracted Unbuild Orders"
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_stock_picking__subcontracted_unbuild_ids
msgid "Subcontracted unbuilds"
msgstr ""
#. module: mrp_unbuild_subcontracting
#. odoo-python
#: code:addons/mrp_unbuild_subcontracting/models/stock_move.py:0
#, python-format
msgid "To subcontract, use a planned transfer."
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_stock_picking
#: model:ir.model.fields,field_description:mrp_unbuild_subcontracting.field_mrp_unbuild__picking_id
msgid "Transfer"
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model:ir.model,name:mrp_unbuild_subcontracting.model_mrp_unbuild
msgid "Unbuild Order"
msgstr ""
#. module: mrp_unbuild_subcontracting
#: model:ir.actions.act_window,name:mrp_unbuild_subcontracting.mrp_unbuild_subcontracted
msgid "Unbuild Orders - Subcontracted"
msgstr ""

View file

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

View file

@ -0,0 +1,8 @@
from odoo import fields, models
class MrpUnbuild(models.Model):
_inherit = "mrp.unbuild"
picking_id = fields.Many2one("stock.picking", "Transfer", readonly=True)
is_subcontracted = fields.Boolean("Is Subcontracted?", readonly=True)

View file

@ -0,0 +1,52 @@
from collections import defaultdict
from odoo import _, models
from odoo.exceptions import UserError
from odoo.tools.float_utils import float_is_zero
class StockMove(models.Model):
_inherit = "stock.move"
def _action_confirm(self, merge=True, merge_into=False):
if self.origin_returned_move_id:
subcontract_details_per_picking = defaultdict(list)
move_to_not_merge = self.env["stock.move"]
for move in self:
if (
move.location_dest_id.usage == "supplier"
and move.location_id
== self.picking_id.picking_type_id.default_location_src_id
):
continue
if move.move_orig_ids.production_id:
continue
bom = move._get_subcontract_bom()
if not bom:
continue
if (
float_is_zero(
move.product_qty, precision_rounding=move.product_uom.rounding
)
and move.picking_id.immediate_transfer is True
):
raise UserError(_("To subcontract, use a planned transfer."))
subcontract_details_per_picking[move.picking_id].append((move, bom))
move_to_not_merge |= move
for picking, subcontract_details in subcontract_details_per_picking.items():
picking._subcontracted_produce_unbuild(subcontract_details)
# We avoid merging move due to complication with stock.rule.
res = super(StockMove, move_to_not_merge)._action_confirm(merge=False)
res |= super(StockMove, self - move_to_not_merge)._action_confirm(
merge=merge, merge_into=merge_into
)
if subcontract_details_per_picking:
self.env["stock.picking"].concat(
*list(subcontract_details_per_picking.keys())
).action_assign()
return res
result = super(StockMove, self)._action_confirm(
merge=merge, merge_into=merge_into
)
return result

View file

@ -0,0 +1,111 @@
from datetime import timedelta
from odoo import _, fields, models
from odoo.exceptions import UserError
from odoo.osv.expression import OR
class StockPicking(models.Model):
_inherit = "stock.picking"
subcontracted_unbuild_ids = fields.One2many(
"mrp.unbuild", "picking_id", readonly=True, string="Subcontracted unbuilds"
)
def _prepare_subcontract_unbuild_vals(self, subcontract_move, bom):
subcontract_move.ensure_one()
product = subcontract_move.product_id
mos = subcontract_move.mapped(
"origin_returned_move_id.move_orig_ids.production_id"
)
if len(mos) > 1:
raise UserError(
_(
"It's not possible to create the subcontracting unbuild order\n"
"The subcontract move %(smn)s is linked with more than "
"one manufacturing order: %(jmm)s"
)
% {"smn": subcontract_move.name, "jmm": ",".join(mos.mapped("name"))}
)
vals = {
"company_id": subcontract_move.company_id.id,
"product_id": product.id,
"product_uom_id": subcontract_move.product_uom.id,
"bom_id": bom.id,
"location_id": subcontract_move.picking_id.partner_id.with_company(
subcontract_move.company_id
).property_stock_subcontractor.id,
"location_dest_id": subcontract_move.picking_id.partner_id.with_company(
subcontract_move.company_id
).property_stock_subcontractor.id,
"product_qty": subcontract_move.product_uom_qty,
"picking_id": self.id,
"is_subcontracted": True,
"mo_id": mos.id,
"lot_id": subcontract_move.move_orig_ids.lot_ids.id,
}
return vals
def _subcontracted_produce_unbuild(self, subcontract_details):
self.ensure_one()
for move, bom in subcontract_details:
unbuild = (
self.env["mrp.unbuild"]
.with_company(move.company_id)
.create(self._prepare_subcontract_unbuild_vals(move, bom))
)
self.subcontracted_unbuild_ids |= unbuild
def _action_done(self):
res = super(StockPicking, self)._action_done()
for picking in self:
unbuilds_to_done = picking.subcontracted_unbuild_ids.filtered(
lambda x: x.state == "draft"
)
if not unbuilds_to_done:
continue
unbuild_ids_backorder = []
if not self.env.context.get("cancel_backorder"):
unbuild_ids_backorder = unbuilds_to_done.filtered(
lambda u: u.state == "draft"
).ids
for unbuild in unbuilds_to_done:
unbuild.with_context(
subcontract_move_id=True, mo_ids_to_backorder=unbuild_ids_backorder
).action_validate()
moves = picking.move_ids.filtered(lambda move: move.is_subcontract)
finished_move = unbuilds_to_done.produce_line_ids.filtered(
lambda m: m.product_id.id in moves.mapped("product_id").ids
)
for move in moves:
finished_move.write({"move_dest_ids": [(4, move.id, False)]})
# For concistency, set the date on production move before the date
# on picking. (Traceability report + Product Moves menu item)
minimum_date = min(picking.move_line_ids.mapped("date"))
unbuild_moves = (
unbuilds_to_done.produce_line_ids | unbuilds_to_done.consume_line_ids
)
unbuild_moves.write({"date": minimum_date - timedelta(seconds=1)})
unbuild_moves.move_line_ids.write(
{"date": minimum_date - timedelta(seconds=1)}
)
return res
def action_view_stock_valuation_layers(self):
action = super(StockPicking, self).action_view_stock_valuation_layers()
subcontracted_unbuilds = self.subcontracted_unbuild_ids
if not subcontracted_unbuilds:
return action
domain = action["domain"]
domain_subcontracting = [
(
"id",
"in",
(
subcontracted_unbuilds.produce_line_ids
| subcontracted_unbuilds.consume_line_ids
).stock_valuation_layer_ids.ids,
)
]
domain = OR([domain, domain_subcontracting])
return dict(action, domain=domain)

View file

@ -0,0 +1,4 @@
* `ForgeFlow <https://www.forgeflow.com>`_:
* Thiago Mulero <thiago.mulero@forgeflow.com>
* Bernat Puig <bernat.puig@forgeflow.com>

View file

@ -0,0 +1,2 @@
This module automatically creates an unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated.
To view the unbuilds created, you have to select the operation Subcontracted Unbuild Orders in debug mode

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -0,0 +1,426 @@
<?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: https://docutils.sourceforge.io/" />
<title>Unbuild orders with return subcontracting</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
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: 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="unbuild-orders-with-return-subcontracting">
<h1 class="title">Unbuild orders with return subcontracting</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:11f2e6d4a0a7c585038a75213c3ea46945a33fa40ecde95aef22f0cc999c8853
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/manufacture/tree/16.0/mrp_unbuild_subcontracting"><img alt="OCA/manufacture" src="https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/manufacture-16-0/manufacture-16-0-mrp_unbuild_subcontracting"><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/manufacture&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 automatically creates an unbuild in draft state when a subcontracting picking return is created. In addition, when the picking is validated, the unbuild is also validated.
To view the unbuilds created, you have to select the operation Subcontracted Unbuild Orders in debug mode</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-1">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-2">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-3">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-4">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-5">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-1">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 to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_unbuild_subcontracting%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-2">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-3">Authors</a></h2>
<ul class="simple">
<li>ForgeFlow</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-4">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.forgeflow.com">ForgeFlow</a>:<ul>
<li>Thiago Mulero &lt;<a class="reference external" href="mailto:thiago.mulero&#64;forgeflow.com">thiago.mulero&#64;forgeflow.com</a>&gt;</li>
<li>Bernat Puig &lt;<a class="reference external" href="mailto:bernat.puig&#64;forgeflow.com">bernat.puig&#64;forgeflow.com</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-5">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/16.0/mrp_unbuild_subcontracting">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_mrp_unbuild_subcontracting

View file

@ -0,0 +1,412 @@
from odoo.exceptions import UserError
from odoo.tests import Form, TransactionCase
class TestSubcontractingPurchaseFlows(TransactionCase):
def setUp(self):
super().setUp()
self.subcontractor = self.env["res.partner"].create(
{"name": "SuperSubcontractor"}
)
self.finished, self.compo = self.env["product.product"].create(
[
{
"name": "SuperProduct",
"type": "product",
},
{
"name": "Component",
"type": "consu",
},
]
)
self.bom = self.env["mrp.bom"].create(
{
"product_tmpl_id": self.finished.product_tmpl_id.id,
"type": "subcontract",
"subcontractor_ids": [(6, 0, self.subcontractor.ids)],
"bom_line_ids": [
(
0,
0,
{
"product_id": self.compo.id,
"product_qty": 1,
},
)
],
}
)
def test_purchase_and_return(self):
"""
The user buys 10 x a subcontracted product P. He receives the 10
products and then does a return with 3 x P. The test ensures that
the unbuild is created with the correct quantities and states
"""
po = self.env["purchase.order"].create(
{
"partner_id": self.subcontractor.id,
"order_line": [
(
0,
0,
{
"name": self.finished.name,
"product_id": self.finished.id,
"product_uom_qty": 10,
"product_uom": self.finished.uom_id.id,
"price_unit": 1,
},
)
],
}
)
po.button_confirm()
mo = self.env["mrp.production"].search([("bom_id", "=", self.bom.id)])
self.assertTrue(mo)
receipt = po.picking_ids
receipt.move_ids.quantity_done = 10
receipt.button_validate()
return_form = Form(
self.env["stock.return.picking"].with_context(
active_id=receipt.id, active_model="stock.picking"
)
)
with return_form.product_return_moves.edit(0) as line:
line.quantity = 3
return_wizard = return_form.save()
return_id, _ = return_wizard._create_returns()
return_picking = self.env["stock.picking"].browse(return_id)
return_picking.move_ids.quantity_done = 3
subcontractor_location = self.subcontractor.property_stock_subcontractor
unbuild = self.env["mrp.unbuild"].search([("bom_id", "=", self.bom.id)])
self.assertTrue(unbuild)
self.assertEqual(
unbuild.state, "draft", "The state of the unbuild should be draft"
)
self.assertEqual(
unbuild.product_qty, 3, "The quantity of the unbuild should be 3"
)
self.assertEqual(
unbuild.location_id,
subcontractor_location,
"The source location of the unbuild should be the property stock "
"of the subcontractor",
)
self.assertEqual(
unbuild.location_dest_id,
subcontractor_location,
"The destination location of the unbuild should be the property "
"stock of the subcontractor",
)
return_picking.button_validate()
self.assertEqual(self.finished.qty_available, 7.0)
self.assertEqual(po.order_line.qty_received, 7.0)
self.assertEqual(
unbuild.state, "done", "The state of the unbuild should be done"
)
move = return_picking.move_ids
self.assertEqual(
move.location_id,
receipt.location_dest_id,
"The source location of the stock move should be the same as "
"destination location of the original purchase",
)
self.assertEqual(
move.location_dest_id,
subcontractor_location,
"The destination location of the stock move should be the property "
"stock of the subcontractor",
)
# Call the action to view the layers associated to the pickings
result1 = return_picking.action_view_stock_valuation_layers()
result2 = receipt.action_view_stock_valuation_layers()
layers1 = result1["domain"][2][2]
layers2 = result2["domain"][2][2]
self.assertTrue(
layers1,
)
self.assertTrue(
layers2,
)
def test_purchase_partial_receipt_and_refund(self):
po = self.env["purchase.order"].create(
{
"partner_id": self.subcontractor.id,
"order_line": [
(
0,
0,
{
"name": self.finished.name,
"product_id": self.finished.id,
"product_uom_qty": 10,
"product_qty": 10,
"product_uom": self.finished.uom_id.id,
"price_unit": 1,
},
)
],
}
)
po.button_confirm()
mo = self.env["mrp.production"].search([("bom_id", "=", self.bom.id)])
self.assertTrue(mo)
receipt = po.picking_ids.filtered(lambda x: x.state != "done")
receipt.move_ids.quantity_done = 3
result_dict = receipt.button_validate()
self.env["stock.backorder.confirmation"].with_context(
**result_dict["context"]
).process()
self.assertEqual(po.order_line.qty_received, 3)
receipt = po.picking_ids.filtered(lambda x: x.state != "done")
receipt.move_ids.quantity_done = 3
picking_to_return = receipt
result_dict = receipt.button_validate()
self.env["stock.backorder.confirmation"].with_context(
**result_dict["context"]
).process()
self.assertEqual(po.order_line.qty_received, 6)
receipt = po.picking_ids.filtered(lambda x: x.state != "done")
receipt.move_ids.quantity_done = 3
result_dict = receipt.button_validate()
self.env["stock.backorder.confirmation"].with_context(
**result_dict["context"]
).process()
self.assertEqual(po.order_line.qty_received, 9)
self.assertEqual(len(po.picking_ids), 4)
return_wizard = (
self.env["stock.return.picking"]
.with_context(
active_id=picking_to_return.id, active_ids=picking_to_return.ids
)
.create(
{
"location_id": picking_to_return.location_id.id,
"picking_id": picking_to_return.id,
}
)
)
return_wizard._onchange_picking_id()
with self.assertRaises(UserError):
return_id, _ = return_wizard._create_returns()
# This part cannot be tested since we cannot unbuild
# subcontracting orders with more than one origin.
#
# return_picking = self.env["stock.picking"].browse(return_id)
# return_picking.move_ids.quantity_done = 3
# return_picking.button_validate()
#
# self.assertEqual(po.order_line.qty_received, 6)
#
# mo = picking_to_return.mapped("move_ids.move_orig_ids.production_id")
# unbuild = self.env["mrp.unbuild"].search([("mo_id", "in", mo.ids)])
# self.assertTrue(unbuild.exists())
class TestSubcontractingTracking(TransactionCase):
def setUp(self):
super(TestSubcontractingTracking, self).setUp()
# 1: Create a subcontracting partner
main_company_1 = self.env["res.partner"].create({"name": "main_partner"})
self.subcontractor_partner1 = self.env["res.partner"].create(
{
"name": "Subcontractor 1",
"parent_id": main_company_1.id,
"company_id": self.env.ref("base.main_company").id,
}
)
# 2. Create a BOM of subcontracting type
# 2.1. Comp1 has tracking by lot
self.comp1_sn = self.env["product.product"].create(
{
"name": "Component1",
"type": "product",
"categ_id": self.env.ref("product.product_category_all").id,
"tracking": "serial",
}
)
self.comp2 = self.env["product.product"].create(
{
"name": "Component2",
"type": "product",
"categ_id": self.env.ref("product.product_category_all").id,
}
)
# 2.2. Finished prodcut has tracking by serial number
self.finished_product = self.env["product.product"].create(
{
"name": "finished",
"type": "product",
"categ_id": self.env.ref("product.product_category_all").id,
"tracking": "lot",
}
)
bom_form = Form(self.env["mrp.bom"])
bom_form.type = "subcontract"
bom_form.subcontractor_ids.add(self.subcontractor_partner1)
bom_form.product_tmpl_id = self.finished_product.product_tmpl_id
with bom_form.bom_line_ids.new() as bom_line:
bom_line.product_id = self.comp1_sn
bom_line.product_qty = 1
with bom_form.bom_line_ids.new() as bom_line:
bom_line.product_id = self.comp2
bom_line.product_qty = 1
self.bom_tracked = bom_form.save()
def test_purchase_and_return_with_serial_numbers(self):
"""
The user buys one subcontracted product P with serial number.
Then does the return . The test ensures that the unbuild is
created with the correct quantities, serial number of the product and states
"""
# Create a receipt picking from the subcontractor
picking_form = Form(self.env["stock.picking"])
picking_form.picking_type_id = self.env.ref("stock.picking_type_in")
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product
move.product_uom_qty = 1
picking_receipt = picking_form.save()
picking_receipt.action_confirm()
# We should be able to call the 'record_components' button
self.assertTrue(picking_receipt.display_action_record_components)
# Check the created manufacturing order
mo = self.env["mrp.production"].search([("bom_id", "=", self.bom_tracked.id)])
self.assertEqual(len(mo), 1)
self.assertEqual(len(mo.picking_ids), 0)
wh = picking_receipt.picking_type_id.warehouse_id
self.assertEqual(mo.picking_type_id, wh.subcontracting_type_id)
self.assertFalse(mo.picking_type_id.active)
# Create a RR
pg1 = self.env["procurement.group"].create({})
self.env["stock.warehouse.orderpoint"].create(
{
"name": "xxx",
"product_id": self.comp1_sn.id,
"product_min_qty": 0,
"product_max_qty": 0,
"location_id": self.env.user.company_id.subcontracting_location_id.id,
"group_id": pg1.id,
}
)
# Run the scheduler and check the created picking
self.env["procurement.group"].run_scheduler()
picking = self.env["stock.picking"].search([("group_id", "=", pg1.id)])
self.assertEqual(len(picking), 1)
self.assertEqual(picking.picking_type_id, wh.subcontracting_resupply_type_id)
lot_id = self.env["stock.lot"].create(
{
"name": "lot1",
"product_id": self.finished_product.id,
"company_id": self.env.company.id,
}
)
serial_id = self.env["stock.lot"].create(
{
"name": "lot1",
"product_id": self.comp1_sn.id,
"company_id": self.env.company.id,
}
)
action = picking_receipt.action_record_components()
mo = self.env["mrp.production"].browse(action["res_id"])
mo_form = Form(mo.with_context(**action["context"]), view=action["view_id"])
mo_form.qty_producing = 1
mo_form.lot_producing_id = lot_id
with mo_form.move_line_raw_ids.edit(0) as ml:
ml.lot_id = serial_id
mo = mo_form.save()
mo.subcontracting_record_component()
# We should not be able to call the 'record_components' button
self.assertEqual(picking_receipt.display_action_record_components, "hide")
picking_receipt.button_validate()
self.assertEqual(mo.state, "done")
return_form = Form(
self.env["stock.return.picking"].with_context(
active_id=picking_receipt.id, active_model="stock.picking"
)
)
with return_form.product_return_moves.edit(0) as line:
line.quantity = 1
return_wizard = return_form.save()
return_id, _ = return_wizard._create_returns()
return_picking = self.env["stock.picking"].browse(return_id)
return_picking.move_ids.quantity_done = 1
subcontractor_location = (
self.subcontractor_partner1.property_stock_subcontractor
)
unbuild = self.env["mrp.unbuild"].search([("bom_id", "=", self.bom_tracked.id)])
self.assertTrue(unbuild)
self.assertEqual(
unbuild.state, "draft", "The state of the unbuild should be draft"
)
self.assertEqual(
unbuild.product_qty, 1, "The quantity of the unbuild should be 1"
)
self.assertEqual(
unbuild.location_id,
subcontractor_location,
"The source location of the unbuild should be the property stock "
"of the subcontractor",
)
self.assertEqual(
unbuild.location_dest_id,
subcontractor_location,
"The destination location of the unbuild should be the property "
"stock of the subcontractor",
)
return_picking.move_line_ids_without_package.lot_id = lot_id
return_picking.button_validate()
self.assertEqual(
unbuild.state, "done", "The state of the unbuild should be done"
)
move = return_picking.move_ids
self.assertEqual(
move.location_id,
picking_receipt.location_dest_id,
"The source location of the stock move should be the same as "
"destination location of the original purchase",
)
self.assertEqual(
move.location_dest_id,
subcontractor_location,
"The destination location of the stock move should be the property "
"stock of the subcontractor",
)

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.actions.act_window" id="mrp.mrp_unbuild">
<field name="domain">[('is_subcontracted', '=', False)]</field>
</record>
<record model="ir.actions.act_window" id="mrp_unbuild_subcontracted">
<field name="name">Unbuild Orders - Subcontracted</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.unbuild</field>
<field name="view_mode">tree,kanban,form</field>
<field name="domain">[('is_subcontracted', '=', True)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No unbuild order found
</p><p>
An unbuild order is used to break down a finished product into its components.
</p>
</field>
</record>
<menuitem
id="menu_mrp_unbuild_subcontracted"
name="Subcontracted Unbuild Orders"
parent="mrp.menu_mrp_manufacturing"
action="mrp_unbuild_subcontracted"
sequence="21"
groups="base.group_no_one"
/>
</odoo>

View file

@ -0,0 +1,43 @@
[project]
name = "odoo-bringout-oca-manufacture-mrp_unbuild_subcontracting"
version = "16.0.0"
description = "Unbuild orders with return subcontracting - Unbuild orders are created automatically when is returned a product subcontracted"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-manufacture-mrp_account>=16.0.0",
"odoo-bringout-oca-manufacture-mrp_subcontracting_purchase>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["mrp_unbuild_subcontracting"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]