mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[10.0][MIG] mrp_production_request
This commit is contained in:
@@ -53,7 +53,7 @@ to set it to *Done* state.
|
||||
|
||||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/129/9.0
|
||||
:target: https://runbot.odoo-community.org/runbot/129/10.0
|
||||
|
||||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import models
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"summary": "Allows you to use Manufacturing Request as a previous "
|
||||
"step to Manufacturing Orders for better manufacture "
|
||||
"planification.",
|
||||
"version": "9.0.1.0.0",
|
||||
"version": "10.0.1.0.0",
|
||||
"category": "Manufacturing",
|
||||
"website": "https://github.com/OCA/manufacture",
|
||||
"author": "Eficent,"
|
||||
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import mrp_production_request
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp import fields, models
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class MrpProduction(models.Model):
|
||||
@@ -11,3 +11,9 @@ class MrpProduction(models.Model):
|
||||
mrp_production_request_id = fields.Many2one(
|
||||
comodel_name="mrp.production.request",
|
||||
string="Manufacturing Request", copy=False, readonly=True)
|
||||
|
||||
def _generate_finished_moves(self):
|
||||
move = super(MrpProduction, self)._generate_finished_moves()
|
||||
mr_proc = self.mrp_production_request_id.procurement_id
|
||||
if mr_proc and mr_proc.move_dest_id:
|
||||
move.write({"move_dest_id": mr_proc.move_dest_id.id})
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# Copyright 2017-18 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp import api, fields, models, _
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from openerp.exceptions import UserError
|
||||
from odoo import api, fields, models, _
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class MrpProductionRequest(models.Model):
|
||||
_name = "mrp.production.request"
|
||||
_description = "Manufacturing Request"
|
||||
_inherit = "mail.thread"
|
||||
_order = "id DESC"
|
||||
|
||||
@api.model
|
||||
def _company_get(self):
|
||||
@@ -52,9 +53,10 @@ class MrpProductionRequest(models.Model):
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_id(self):
|
||||
self.product_uom = self.product_id.uom_id
|
||||
self.product_uom_id = self.product_id.uom_id
|
||||
self.bom_id = self.env['mrp.bom']._bom_find(
|
||||
product_id=self.product_id.id, properties=[])
|
||||
product=self.product_id, company_id=self.company_id.id,
|
||||
picking_type=self.picking_type_id)
|
||||
|
||||
@api.model
|
||||
def _get_mo_valid_states(self):
|
||||
@@ -122,26 +124,26 @@ class MrpProductionRequest(models.Model):
|
||||
related='product_id.product_tmpl_id')
|
||||
product_qty = fields.Float(
|
||||
string="Required Quantity", required=True, track_visibility='onchange',
|
||||
digits_compute=dp.get_precision('Product Unit of Measure'),
|
||||
digits=dp.get_precision('Product Unit of Measure'),
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
product_uom = fields.Many2one(
|
||||
product_uom_id = fields.Many2one(
|
||||
comodel_name='product.uom', string='Unit of Measure',
|
||||
readonly=True, states={'draft': [('readonly', False)]},
|
||||
domain="[('category_id', '=', category_uom_id)]")
|
||||
category_uom_id = fields.Many2one(related="product_uom.category_id")
|
||||
category_uom_id = fields.Many2one(related="product_uom_id.category_id")
|
||||
manufactured_qty = fields.Float(
|
||||
string="Quantity in Manufacturing Orders",
|
||||
compute=_compute_manufactured_qty, store=True, readonly=True,
|
||||
digits_compute=dp.get_precision('Product Unit of Measure'),
|
||||
digits=dp.get_precision('Product Unit of Measure'),
|
||||
help="Sum of the quantities in Manufacturing Orders (in any state).")
|
||||
done_qty = fields.Float(
|
||||
string="Quantity Done", store=True, readonly=True,
|
||||
compute=_compute_manufactured_qty,
|
||||
digits_compute=dp.get_precision('Product Unit of Measure'),
|
||||
digits=dp.get_precision('Product Unit of Measure'),
|
||||
help="Sum of the quantities in all done Manufacturing Orders.")
|
||||
pending_qty = fields.Float(
|
||||
string="Pending Quantity", compute=_compute_manufactured_qty,
|
||||
store=True, digits_compute=dp.get_precision('Product Unit of Measure'),
|
||||
store=True, digits=dp.get_precision('Product Unit of Measure'),
|
||||
readonly=True,
|
||||
help="Quantity pending to add to Manufacturing Orders "
|
||||
"to fulfill the Manufacturing Request requirement.")
|
||||
@@ -159,12 +161,17 @@ class MrpProductionRequest(models.Model):
|
||||
location_src_id = fields.Many2one(
|
||||
comodel_name='stock.location', string='Raw Materials Location',
|
||||
default=lambda self: self.env['stock.location'].browse(
|
||||
self.env['mrp.production']._src_id_default()),
|
||||
self.env['mrp.production']._get_default_location_src_id()),
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]})
|
||||
location_dest_id = fields.Many2one(
|
||||
comodel_name='stock.location', string='Finished Products Location',
|
||||
default=lambda self: self.env['stock.location'].browse(
|
||||
self.env['mrp.production']._dest_id_default()),
|
||||
self.env['mrp.production']._get_default_location_dest_id()),
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]})
|
||||
picking_type_id = fields.Many2one(
|
||||
comodel_name='stock.picking.type', string='Picking Type',
|
||||
default=lambda self: self.env['stock.picking.type'].browse(
|
||||
self.env['mrp.production']._get_default_picking_type()),
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]})
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp import api, fields, models, _
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class ProcurementOrder(models.Model):
|
||||
@@ -12,31 +12,32 @@ class ProcurementOrder(models.Model):
|
||||
comodel_name="mrp.production.request", string="Manufacturing Request",
|
||||
copy=False)
|
||||
|
||||
@api.model
|
||||
def _prepare_mrp_production_request(self, procurement):
|
||||
data = self._prepare_mo_vals(procurement)
|
||||
data['procurement_id'] = procurement.id
|
||||
@api.multi
|
||||
def _prepare_mrp_production_request(self):
|
||||
self.ensure_one()
|
||||
data = self._prepare_mo_vals(self._get_matching_bom())
|
||||
data['procurement_id'] = self.id
|
||||
data['state'] = 'to_approve'
|
||||
return data
|
||||
|
||||
@api.model
|
||||
def _run(self, procurement):
|
||||
if (procurement.rule_id and
|
||||
procurement.rule_id.action == 'manufacture' and
|
||||
procurement.product_id.mrp_production_request):
|
||||
if not procurement.check_bom_exists():
|
||||
procurement.message_post(
|
||||
@api.multi
|
||||
def _run(self):
|
||||
self.ensure_one()
|
||||
if (self.rule_id and
|
||||
self.rule_id.action == 'manufacture' and
|
||||
self.product_id.mrp_production_request):
|
||||
if not self._get_matching_bom():
|
||||
self.message_post(
|
||||
body=_("No BoM exists for this product!"))
|
||||
return False
|
||||
if not self.mrp_production_request_id:
|
||||
request_data = self._prepare_mrp_production_request(
|
||||
procurement)
|
||||
request_data = self._prepare_mrp_production_request()
|
||||
req = self.env['mrp.production.request'].create(request_data)
|
||||
procurement.message_post(body=_(
|
||||
self.message_post(body=_(
|
||||
"Manufacturing Request created"))
|
||||
procurement.mrp_production_request_id = req.id
|
||||
self.mrp_production_request_id = req.id
|
||||
return True
|
||||
return super(ProcurementOrder, self)._run(procurement)
|
||||
return super(ProcurementOrder, self)._run()
|
||||
|
||||
@api.multi
|
||||
def propagate_cancels(self):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp import fields, models
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp import api, models
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import test_mrp_production_request
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp.tests.common import TransactionCase
|
||||
from openerp import fields
|
||||
from openerp.exceptions import UserError
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo import fields
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class TestMrpProductionRequest(TransactionCase):
|
||||
@@ -61,7 +61,6 @@ class TestMrpProductionRequest(TransactionCase):
|
||||
('mrp_production_request_id', '=', request.id)])
|
||||
self.assertTrue(mo, "No MO created.")
|
||||
self.assertEqual(request.pending_qty, 0.0)
|
||||
mo.action_confirm()
|
||||
request.button_done()
|
||||
|
||||
def test_cancellation_from_request(self):
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<field name="done_qty"/>
|
||||
<field name="manufactured_qty"/>
|
||||
<field name="pending_qty"/>
|
||||
<field name="product_uom" groups="product.group_uom"/>
|
||||
<field name="product_uom_id" groups="product.group_uom"/>
|
||||
<field name="category_uom_id" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
@@ -80,6 +80,7 @@
|
||||
<group>
|
||||
<field name="location_src_id"/>
|
||||
<field name="location_dest_id"/>
|
||||
<field name="picking_type_id"/>
|
||||
<field name="date_planned"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<field name="inherit_id"
|
||||
ref="mrp.mrp_production_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="move_prod_id" position="after">
|
||||
<field name="availability" position="after">
|
||||
<field name="mrp_production_request_id"/>
|
||||
</field>
|
||||
</field>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import mrp_production_request_create_mo
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# Copyright 2017-18 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp import api, fields, models
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from odoo import api, fields, models
|
||||
import odoo.addons.decimal_precision as dp
|
||||
|
||||
|
||||
class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
@@ -14,8 +14,7 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
def compute_product_line_ids(self):
|
||||
self.product_line_ids.unlink()
|
||||
res = self._prepare_lines()
|
||||
product_lines = res[0]
|
||||
# TODO: expand with workcenter_lines: they are in res[1].
|
||||
product_lines = res[1]
|
||||
for line in product_lines:
|
||||
self.env['mrp.production.request.create.mo.line'].create(
|
||||
self._prepare_product_line(line))
|
||||
@@ -23,27 +22,25 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
return {"type": "ir.actions.do_nothing"}
|
||||
|
||||
def _prepare_lines(self):
|
||||
"""Get the components (product_lines) and Work Centers Utilisation
|
||||
(workcenter_lines) needed for manufacturing the given a BoM.
|
||||
:return: product_lines, workcenter_lines
|
||||
"""Get the components (product_lines) needed for manufacturing the
|
||||
given a BoM.
|
||||
:return: boms_done, lines_done
|
||||
"""
|
||||
bom_obj = self.env['mrp.bom']
|
||||
uom_obj = self.env['product.uom']
|
||||
bom_point = self.bom_id
|
||||
factor = uom_obj._compute_qty(
|
||||
self.mrp_production_request_id.product_uom.id, self.pending_qty,
|
||||
bom_point.product_uom.id)
|
||||
return bom_obj._bom_explode(
|
||||
bom_point, self.mrp_production_request_id.product_id,
|
||||
factor / bom_point.product_qty,
|
||||
routing_id=self.mrp_production_request_id.routing_id.id)
|
||||
factor = self.mrp_production_request_id.product_uom_id.\
|
||||
_compute_quantity(self.pending_qty, bom_point.product_uom_id)
|
||||
return bom_point.explode(
|
||||
self.mrp_production_request_id.product_id,
|
||||
factor / bom_point.product_qty)
|
||||
|
||||
@api.one
|
||||
@api.multi
|
||||
def _get_mo_qty(self):
|
||||
"""Propose a qty to create a MO available to produce."""
|
||||
bottle_neck = min(self.product_line_ids.mapped('bottle_neck_factor'))
|
||||
bottle_neck = max(min(1, bottle_neck), 0)
|
||||
self.mo_qty = self.pending_qty * bottle_neck
|
||||
for rec in self:
|
||||
bottle_neck = min(rec.product_line_ids.mapped(
|
||||
'bottle_neck_factor'))
|
||||
bottle_neck = max(min(1, bottle_neck), 0)
|
||||
rec.mo_qty = rec.pending_qty * bottle_neck
|
||||
|
||||
mrp_production_request_id = fields.Many2one(
|
||||
comodel_name="mrp.production.request", readonly=True)
|
||||
@@ -51,12 +48,12 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
related='mrp_production_request_id.bom_id', readonly=True)
|
||||
mo_qty = fields.Float(
|
||||
string="Quantity",
|
||||
digits_compute=dp.get_precision("Product Unit of Measure"))
|
||||
digits=dp.get_precision("Product Unit of Measure"))
|
||||
pending_qty = fields.Float(
|
||||
related="mrp_production_request_id.pending_qty",
|
||||
digits_compute=dp.get_precision("Product Unit of Measure"))
|
||||
product_uom = fields.Many2one(
|
||||
related="mrp_production_request_id.product_uom")
|
||||
digits=dp.get_precision("Product Unit of Measure"))
|
||||
product_uom_id = fields.Many2one(
|
||||
related="mrp_production_request_id.product_uom_id")
|
||||
product_line_ids = fields.One2many(
|
||||
comodel_name="mrp.production.request.create.mo.line",
|
||||
string="Products needed",
|
||||
@@ -64,9 +61,9 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
|
||||
def _prepare_product_line(self, pl):
|
||||
return {
|
||||
'product_id': pl['product_id'],
|
||||
'product_qty': pl['product_qty'],
|
||||
'product_uom': pl['product_uom'],
|
||||
'product_id': pl[0].product_id.id,
|
||||
'product_qty': pl[1]['qty'],
|
||||
'product_uom_id': pl[0].product_uom_id.id,
|
||||
'mrp_production_request_create_mo_id': self.id,
|
||||
'location_id': self.mrp_production_request_id.location_src_id.id,
|
||||
}
|
||||
@@ -79,13 +76,13 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
'product_id': request_id.product_id.id,
|
||||
'bom_id': request_id.bom_id.id,
|
||||
'product_qty': self.mo_qty,
|
||||
'product_uom': self.product_uom.id,
|
||||
'product_uom_id': self.product_uom_id.id,
|
||||
'mrp_production_request_id': self.mrp_production_request_id.id,
|
||||
'origin': request_id.origin,
|
||||
'location_src_id': request_id.location_src_id.id,
|
||||
'location_dest_id': request_id.location_dest_id.id,
|
||||
'picking_type_id': request_id.picking_type_id.id,
|
||||
'routing_id': request_id.routing_id.id,
|
||||
'move_prod_id': request_id.procurement_id.move_dest_id.id or False,
|
||||
'date_planned': request_id.date_planned,
|
||||
'company_id': request_id.company_id.id,
|
||||
}
|
||||
@@ -108,33 +105,35 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
class MrpProductionRequestCreateMoLine(models.TransientModel):
|
||||
_name = "mrp.production.request.create.mo.line"
|
||||
|
||||
@api.one
|
||||
@api.multi
|
||||
def _compute_available_qty(self):
|
||||
product_available = self.product_id.with_context(
|
||||
location=self.location_id.id)._product_available()[
|
||||
self.product_id.id]['qty_available_not_res']
|
||||
res = self.product_uom._compute_qty(
|
||||
self.product_id.product_tmpl_id.uom_id.id, product_available,
|
||||
self.product_uom.id)
|
||||
self.available_qty = res
|
||||
for rec in self:
|
||||
product_available = rec.product_id.with_context(
|
||||
location=rec.location_id.id).\
|
||||
_compute_product_available_not_res_dict()[
|
||||
rec.product_id.id]['qty_available_not_res']
|
||||
res = rec.product_id.product_tmpl_id.uom_id._compute_quantity(
|
||||
product_available, rec.product_uom_id)
|
||||
rec.available_qty = res
|
||||
|
||||
@api.one
|
||||
@api.multi
|
||||
def _compute_bottle_neck_factor(self):
|
||||
if self.product_qty:
|
||||
self.bottle_neck_factor = self.available_qty / self.product_qty
|
||||
for rec in self:
|
||||
if rec.product_qty:
|
||||
rec.bottle_neck_factor = rec.available_qty / rec.product_qty
|
||||
|
||||
product_id = fields.Many2one(
|
||||
comodel_name='product.product', string='Product', required=True)
|
||||
product_qty = fields.Float(
|
||||
string='Quantity Required', required=True,
|
||||
digits_compute=dp.get_precision('Product Unit of Measure'))
|
||||
product_uom = fields.Many2one(
|
||||
digits=dp.get_precision('Product Unit of Measure'))
|
||||
product_uom_id = fields.Many2one(
|
||||
comodel_name='product.uom', string='UoM', required=True)
|
||||
mrp_production_request_create_mo_id = fields.Many2one(
|
||||
comodel_name='mrp.production.request.create.mo')
|
||||
available_qty = fields.Float(
|
||||
string='Quantity Available', compute=_compute_available_qty,
|
||||
digits_compute=dp.get_precision('Product Unit of Measure'))
|
||||
digits=dp.get_precision('Product Unit of Measure'))
|
||||
bottle_neck_factor = fields.Float(compute=_compute_bottle_neck_factor)
|
||||
location_id = fields.Many2one(comodel_name='stock.location',
|
||||
required=True)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<field name="product_line_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="product_id"/>
|
||||
<field name="product_uom"/>
|
||||
<field name="product_uom_id"/>
|
||||
<field name="product_qty"/>
|
||||
<field name="available_qty"/>
|
||||
<field name="bottle_neck_factor"/>
|
||||
@@ -31,7 +31,7 @@
|
||||
</group>
|
||||
<group name="destination" string="Manufacturing Order:" col="4">
|
||||
<field name="mo_qty"/>
|
||||
<field name="product_uom" options="{'no_open': True}"
|
||||
<field name="product_uom_id" options="{'no_open': True}"
|
||||
groups="product.group_uom"/>
|
||||
</group>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user