mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[ADD] Added module with pre-commit changes only.
This commit is contained in:
@@ -7,11 +7,10 @@
|
||||
"planification.",
|
||||
"version": "12.0.1.0.0",
|
||||
"development_status": "Mature",
|
||||
"maintainers": ['lreficent'],
|
||||
"maintainers": ["lreficent"],
|
||||
"category": "Manufacturing",
|
||||
"website": "https://github.com/OCA/manufacture",
|
||||
"author": "Eficent,"
|
||||
"Odoo Community Association (OCA)",
|
||||
"author": "Eficent," "Odoo Community Association (OCA)",
|
||||
"license": "AGPL-3",
|
||||
"application": False,
|
||||
"installable": True,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
|
||||
<odoo noupdate="1">
|
||||
|
||||
<record id="seq_mrp_production_request" model="ir.sequence">
|
||||
<field name="name">Manufacturing Request</field>
|
||||
<field name="code">mrp.production.request</field>
|
||||
@@ -11,5 +9,4 @@
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False" />
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -9,7 +9,10 @@ class MrpProduction(models.Model):
|
||||
|
||||
mrp_production_request_id = fields.Many2one(
|
||||
comodel_name="mrp.production.request",
|
||||
string="Manufacturing Request", copy=False, readonly=True)
|
||||
string="Manufacturing Request",
|
||||
copy=False,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
def _generate_finished_moves(self):
|
||||
"""`move_dest_ids` is a One2many fields in mrp.production, thus we
|
||||
@@ -21,7 +24,7 @@ class MrpProduction(models.Model):
|
||||
move = super()._generate_finished_moves()
|
||||
request = self.mrp_production_request_id
|
||||
if request and request.move_dest_ids:
|
||||
move.write({
|
||||
'move_dest_ids': [(4, x.id) for x in request.move_dest_ids],
|
||||
})
|
||||
move.write(
|
||||
{"move_dest_ids": [(4, x.id) for x in request.move_dest_ids],}
|
||||
)
|
||||
return move
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# Copyright 2017-19 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
import odoo.addons.decimal_precision as dp
|
||||
|
||||
|
||||
class MrpProductionRequest(models.Model):
|
||||
_name = "mrp.production.request"
|
||||
@@ -14,153 +15,239 @@ class MrpProductionRequest(models.Model):
|
||||
|
||||
@api.model
|
||||
def _company_get(self):
|
||||
company_id = self.env['res.company']._company_default_get()
|
||||
return self.env['res.company'].browse(company_id.id)
|
||||
company_id = self.env["res.company"]._company_default_get()
|
||||
return self.env["res.company"].browse(company_id.id)
|
||||
|
||||
@api.model
|
||||
def _get_default_requested_by(self):
|
||||
return self.env.user
|
||||
|
||||
name = fields.Char(
|
||||
default="/", required=True,
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
default="/",
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
origin = fields.Char(
|
||||
string='Source Document',
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
string="Source Document", readonly=True, states={"draft": [("readonly", False)]}
|
||||
)
|
||||
requested_by = fields.Many2one(
|
||||
comodel_name='res.users', string='Requested by',
|
||||
comodel_name="res.users",
|
||||
string="Requested by",
|
||||
default=lambda self: self._get_default_requested_by(),
|
||||
required=True, track_visibility='onchange',
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
required=True,
|
||||
track_visibility="onchange",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
assigned_to = fields.Many2one(
|
||||
comodel_name='res.users', string='Approver',
|
||||
track_visibility='onchange',
|
||||
readonly=True, states={'draft': [('readonly', False)]},
|
||||
domain=lambda self: [('groups_id', 'in', self.env.ref(
|
||||
'mrp_production_request.'
|
||||
'group_mrp_production_request_manager').id)])
|
||||
description = fields.Text('Description')
|
||||
comodel_name="res.users",
|
||||
string="Approver",
|
||||
track_visibility="onchange",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
domain=lambda self: [
|
||||
(
|
||||
"groups_id",
|
||||
"in",
|
||||
self.env.ref(
|
||||
"mrp_production_request." "group_mrp_production_request_manager"
|
||||
).id,
|
||||
)
|
||||
],
|
||||
)
|
||||
description = fields.Text("Description")
|
||||
date_planned_start = fields.Datetime(
|
||||
'Deadline Start', copy=False, default=fields.Datetime.now,
|
||||
index=True, required=True,
|
||||
states={'confirmed': [('readonly', False)]}, oldname="date_planned")
|
||||
date_planned_finished = fields.Datetime(
|
||||
'Deadline End', copy=False, default=fields.Datetime.now,
|
||||
"Deadline Start",
|
||||
copy=False,
|
||||
default=fields.Datetime.now,
|
||||
index=True,
|
||||
states={'confirmed': [('readonly', False)]})
|
||||
required=True,
|
||||
states={"confirmed": [("readonly", False)]},
|
||||
oldname="date_planned",
|
||||
)
|
||||
date_planned_finished = fields.Datetime(
|
||||
"Deadline End",
|
||||
copy=False,
|
||||
default=fields.Datetime.now,
|
||||
index=True,
|
||||
states={"confirmed": [("readonly", False)]},
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name='res.company', string='Company',
|
||||
required=True, default=lambda self: self._company_get())
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
required=True,
|
||||
default=lambda self: self._company_get(),
|
||||
)
|
||||
mrp_production_ids = fields.One2many(
|
||||
comodel_name="mrp.production", string="Manufacturing Orders",
|
||||
inverse_name="mrp_production_request_id", readonly=True)
|
||||
comodel_name="mrp.production",
|
||||
string="Manufacturing Orders",
|
||||
inverse_name="mrp_production_request_id",
|
||||
readonly=True,
|
||||
)
|
||||
mrp_production_count = fields.Integer(
|
||||
compute="_compute_mrp_production_count",
|
||||
string="MO's Count",
|
||||
compute="_compute_mrp_production_count", string="MO's Count",
|
||||
)
|
||||
state = fields.Selection(
|
||||
selection=[("draft", "Draft"),
|
||||
selection=[
|
||||
("draft", "Draft"),
|
||||
("to_approve", "To Be Approved"),
|
||||
("approved", "Approved"),
|
||||
("done", "Done"),
|
||||
("cancel", "Cancelled")],
|
||||
index=True, track_visibility='onchange',
|
||||
required=True, copy=False, default='draft')
|
||||
procurement_group_id = fields.Many2one(
|
||||
string='Procurement Group',
|
||||
comodel_name='procurement.group',
|
||||
copy=False)
|
||||
propagate = fields.Boolean(
|
||||
'Propagate cancel and split',
|
||||
help='If checked, when the previous move of the move '
|
||||
'(which was generated by a next procurement) is cancelled '
|
||||
'or split, the move generated by this move will too')
|
||||
product_id = fields.Many2one(
|
||||
comodel_name="product.product", string="Product", required=True,
|
||||
domain=[('type', 'in', ['product', 'consu'])],
|
||||
("cancel", "Cancelled"),
|
||||
],
|
||||
index=True,
|
||||
track_visibility="onchange",
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
required=True,
|
||||
copy=False,
|
||||
default="draft",
|
||||
)
|
||||
procurement_group_id = fields.Many2one(
|
||||
string="Procurement Group", comodel_name="procurement.group", copy=False
|
||||
)
|
||||
propagate = fields.Boolean(
|
||||
"Propagate cancel and split",
|
||||
help="If checked, when the previous move of the move "
|
||||
"(which was generated by a next procurement) is cancelled "
|
||||
"or split, the move generated by this move will too",
|
||||
)
|
||||
product_id = fields.Many2one(
|
||||
comodel_name="product.product",
|
||||
string="Product",
|
||||
required=True,
|
||||
domain=[("type", "in", ["product", "consu"])],
|
||||
track_visibility="onchange",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
product_tmpl_id = fields.Many2one(
|
||||
comodel_name='product.template', string='Product Template',
|
||||
related='product_id.product_tmpl_id')
|
||||
comodel_name="product.template",
|
||||
string="Product Template",
|
||||
related="product_id.product_tmpl_id",
|
||||
)
|
||||
product_qty = fields.Float(
|
||||
string="Required Quantity", required=True, track_visibility='onchange',
|
||||
digits=dp.get_precision('Product Unit of Measure'), default=1.0,
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
string="Required Quantity",
|
||||
required=True,
|
||||
track_visibility="onchange",
|
||||
digits=dp.get_precision("Product Unit of Measure"),
|
||||
default=1.0,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
product_uom_id = fields.Many2one(
|
||||
comodel_name='uom.uom', string='Unit of Measure',
|
||||
readonly=True, states={'draft': [('readonly', False)]},
|
||||
domain="[('category_id', '=', category_uom_id)]")
|
||||
comodel_name="uom.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_id.category_id")
|
||||
manufactured_qty = fields.Float(
|
||||
string="Quantity in Manufacturing Orders",
|
||||
compute="_compute_manufactured_qty", store=True, readonly=True,
|
||||
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=dp.get_precision('Product Unit of Measure'),
|
||||
help="Sum of the quantities in all done Manufacturing Orders.")
|
||||
store=True,
|
||||
readonly=True,
|
||||
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=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=dp.get_precision('Product Unit of Measure'),
|
||||
string="Pending Quantity",
|
||||
compute="_compute_manufactured_qty",
|
||||
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.")
|
||||
"to fulfill the Manufacturing Request requirement.",
|
||||
)
|
||||
bom_id = fields.Many2one(
|
||||
comodel_name="mrp.bom", string="Bill of Materials", required=True,
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
comodel_name="mrp.bom",
|
||||
string="Bill of Materials",
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
routing_id = fields.Many2one(
|
||||
comodel_name='mrp.routing', string='Routing',
|
||||
on_delete='setnull', readonly=True,
|
||||
states={'draft': [('readonly', False)]},
|
||||
comodel_name="mrp.routing",
|
||||
string="Routing",
|
||||
on_delete="setnull",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
help="The list of operations (list of work centers) to produce "
|
||||
"the finished product. The routing is mainly used to compute "
|
||||
"work center costs during operations and to plan future loads "
|
||||
"on work centers based on production plannification.")
|
||||
"on work centers based on production plannification.",
|
||||
)
|
||||
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']._get_default_location_src_id()),
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]})
|
||||
comodel_name="stock.location",
|
||||
string="Raw Materials Location",
|
||||
default=lambda self: self.env["stock.location"].browse(
|
||||
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']._get_default_location_dest_id()),
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]})
|
||||
comodel_name="stock.location",
|
||||
string="Finished Products Location",
|
||||
default=lambda self: self.env["stock.location"].browse(
|
||||
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)]})
|
||||
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)]},
|
||||
)
|
||||
move_dest_ids = fields.One2many(
|
||||
comodel_name='stock.move',
|
||||
inverse_name='created_mrp_production_request_id',
|
||||
string="Stock Movements of Produced Goods")
|
||||
comodel_name="stock.move",
|
||||
inverse_name="created_mrp_production_request_id",
|
||||
string="Stock Movements of Produced Goods",
|
||||
)
|
||||
orderpoint_id = fields.Many2one(
|
||||
comodel_name='stock.warehouse.orderpoint',
|
||||
string='Orderpoint')
|
||||
comodel_name="stock.warehouse.orderpoint", string="Orderpoint"
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique(name, company_id)',
|
||||
'Reference must be unique per Company!'),
|
||||
(
|
||||
"name_uniq",
|
||||
"unique(name, company_id)",
|
||||
"Reference must be unique per Company!",
|
||||
),
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _get_mo_valid_states(self):
|
||||
return ['planned', 'confirmed', 'progress', 'done']
|
||||
return ["planned", "confirmed", "progress", "done"]
|
||||
|
||||
@api.multi
|
||||
@api.depends('mrp_production_ids', 'mrp_production_ids.state', 'state')
|
||||
@api.depends("mrp_production_ids", "mrp_production_ids.state", "state")
|
||||
def _compute_manufactured_qty(self):
|
||||
valid_states = self._get_mo_valid_states()
|
||||
for req in self:
|
||||
done_mo = req.mrp_production_ids.filtered(
|
||||
lambda mo: mo.state in 'done').mapped('product_qty')
|
||||
lambda mo: mo.state in "done"
|
||||
).mapped("product_qty")
|
||||
req.done_qty = sum(done_mo)
|
||||
valid_mo = req.mrp_production_ids.filtered(
|
||||
lambda mo: mo.state in valid_states).mapped('product_qty')
|
||||
lambda mo: mo.state in valid_states
|
||||
).mapped("product_qty")
|
||||
req.manufactured_qty = sum(valid_mo)
|
||||
req.pending_qty = max(req.product_qty - req.manufactured_qty, 0.0)
|
||||
|
||||
@@ -169,25 +256,29 @@ class MrpProductionRequest(models.Model):
|
||||
for rec in self:
|
||||
rec.mrp_production_count = len(rec.mrp_production_ids)
|
||||
|
||||
@api.onchange('product_id')
|
||||
@api.onchange("product_id")
|
||||
def _onchange_product_id(self):
|
||||
self.product_uom_id = self.product_id.uom_id
|
||||
self.bom_id = self.env['mrp.bom']._bom_find(
|
||||
product=self.product_id, company_id=self.company_id.id,
|
||||
picking_type=self.picking_type_id)
|
||||
self.bom_id = self.env["mrp.bom"]._bom_find(
|
||||
product=self.product_id,
|
||||
company_id=self.company_id.id,
|
||||
picking_type=self.picking_type_id,
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def _subscribe_assigned_user(self, vals):
|
||||
self.ensure_one()
|
||||
if vals.get('assigned_to'):
|
||||
if vals.get("assigned_to"):
|
||||
self.message_subscribe(
|
||||
partner_ids=self.assigned_to.mapped('partner_id').ids)
|
||||
partner_ids=self.assigned_to.mapped("partner_id").ids
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _create_sequence(self, vals):
|
||||
if not vals.get('name') or vals.get('name') == '/':
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code(
|
||||
'mrp.production.request') or '/'
|
||||
if not vals.get("name") or vals.get("name") == "/":
|
||||
vals["name"] = (
|
||||
self.env["ir.sequence"].next_by_code("mrp.production.request") or "/"
|
||||
)
|
||||
return vals
|
||||
|
||||
@api.model
|
||||
@@ -208,59 +299,70 @@ class MrpProductionRequest(models.Model):
|
||||
|
||||
@api.multi
|
||||
def button_to_approve(self):
|
||||
self.write({'state': 'to_approve'})
|
||||
self.write({"state": "to_approve"})
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def button_approved(self):
|
||||
self.write({'state': 'approved'})
|
||||
self.write({"state": "approved"})
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def button_done(self):
|
||||
self.write({'state': 'done'})
|
||||
self.write({"state": "done"})
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def _check_reset_allowed(self):
|
||||
if any([s in self._get_mo_valid_states() for s in self.mapped(
|
||||
'mrp_production_ids.state')]):
|
||||
if any(
|
||||
[
|
||||
s in self._get_mo_valid_states()
|
||||
for s in self.mapped("mrp_production_ids.state")
|
||||
]
|
||||
):
|
||||
raise UserError(
|
||||
_("You cannot reset a manufacturing request if the related "
|
||||
"manufacturing orders are not cancelled."))
|
||||
_(
|
||||
"You cannot reset a manufacturing request if the related "
|
||||
"manufacturing orders are not cancelled."
|
||||
)
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def button_draft(self):
|
||||
self._check_reset_allowed()
|
||||
self.write({'state': 'draft'})
|
||||
self.write({"state": "draft"})
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def _check_cancel_allowed(self):
|
||||
if any([s == 'done' for s in self.mapped('state')]):
|
||||
if any([s == "done" for s in self.mapped("state")]):
|
||||
raise UserError(
|
||||
_('You cannot reject a manufacturing request related to '
|
||||
'done procurement orders.'))
|
||||
_(
|
||||
"You cannot reject a manufacturing request related to "
|
||||
"done procurement orders."
|
||||
)
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def button_cancel(self):
|
||||
self._check_cancel_allowed()
|
||||
self.write({'state': 'cancel'})
|
||||
self.mapped('move_dest_ids').filtered(
|
||||
lambda r: r.state != 'cancel')._action_cancel()
|
||||
self.write({"state": "cancel"})
|
||||
self.mapped("move_dest_ids").filtered(
|
||||
lambda r: r.state != "cancel"
|
||||
)._action_cancel()
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def action_view_mrp_productions(self):
|
||||
action = self.env.ref('mrp.mrp_production_action')
|
||||
action = self.env.ref("mrp.mrp_production_action")
|
||||
result = action.read()[0]
|
||||
result['context'] = {}
|
||||
mos = self.mapped('mrp_production_ids')
|
||||
result["context"] = {}
|
||||
mos = self.mapped("mrp_production_ids")
|
||||
# choose the view_mode accordingly
|
||||
if len(mos) != 1:
|
||||
result['domain'] = [('id', 'in', mos.ids)]
|
||||
result["domain"] = [("id", "in", mos.ids)]
|
||||
elif len(mos) == 1:
|
||||
form = self.env.ref('mrp.mrp_production_form_view', False)
|
||||
result['views'] = [(form and form.id or False, 'form')]
|
||||
result['res_id'] = mos[0].id
|
||||
form = self.env.ref("mrp.mrp_production_form_view", False)
|
||||
result["views"] = [(form and form.id or False, "form")]
|
||||
result["res_id"] = mos[0].id
|
||||
return result
|
||||
|
||||
@@ -8,7 +8,7 @@ class ProductTemplate(models.Model):
|
||||
_inherit = "product.template"
|
||||
|
||||
mrp_production_request = fields.Boolean(
|
||||
string='Manufacturing Request',
|
||||
string="Manufacturing Request",
|
||||
help="Check this box to generate manufacturing request instead of "
|
||||
"generating manufacturing orders from procurement.",
|
||||
)
|
||||
|
||||
@@ -8,15 +8,13 @@ class StockMove(models.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
created_mrp_production_request_id = fields.Many2one(
|
||||
comodel_name='mrp.production.request',
|
||||
string='Created Production Request',
|
||||
comodel_name="mrp.production.request", string="Created Production Request",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if 'production_id' in vals:
|
||||
production = self.env['mrp.production'].browse(
|
||||
vals['production_id'])
|
||||
if "production_id" in vals:
|
||||
production = self.env["mrp.production"].browse(vals["production_id"])
|
||||
if production.mrp_production_request_id:
|
||||
vals['propagate'] = False
|
||||
vals["propagate"] = False
|
||||
return super().create(vals)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2018-19 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models, _
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
@@ -10,74 +10,96 @@ class StockRule(models.Model):
|
||||
|
||||
@api.multi
|
||||
def _prepare_mrp_production_request(
|
||||
self, product_id, product_qty, product_uom, location_id, name,
|
||||
origin, values, bom):
|
||||
self,
|
||||
product_id,
|
||||
product_qty,
|
||||
product_uom,
|
||||
location_id,
|
||||
name,
|
||||
origin,
|
||||
values,
|
||||
bom,
|
||||
):
|
||||
self.ensure_one()
|
||||
data = self._prepare_mo_vals(
|
||||
product_id, product_qty, product_uom, location_id, name,
|
||||
origin, values, bom)
|
||||
data['state'] = 'to_approve'
|
||||
orderpoint = values.get('orderpoint_id')
|
||||
product_id, product_qty, product_uom, location_id, name, origin, values, bom
|
||||
)
|
||||
data["state"] = "to_approve"
|
||||
orderpoint = values.get("orderpoint_id")
|
||||
if orderpoint:
|
||||
data['orderpoint_id'] = orderpoint.id
|
||||
procurement_group = values.get('group_id')
|
||||
data["orderpoint_id"] = orderpoint.id
|
||||
procurement_group = values.get("group_id")
|
||||
if procurement_group:
|
||||
data['procurement_group_id'] = procurement_group.id
|
||||
data["procurement_group_id"] = procurement_group.id
|
||||
return data
|
||||
|
||||
@api.multi
|
||||
def _need_production_request(self, product_id):
|
||||
return self.action == 'manufacture' \
|
||||
and product_id.mrp_production_request
|
||||
return self.action == "manufacture" and product_id.mrp_production_request
|
||||
|
||||
@api.multi
|
||||
def _run_production_request(self, product_id, product_qty, product_uom,
|
||||
location_id, name, origin, values):
|
||||
def _run_production_request(
|
||||
self, product_id, product_qty, product_uom, location_id, name, origin, values
|
||||
):
|
||||
"""Trying to handle this as much similar as possible to Odoo
|
||||
production orders. See `_run_manufacture` in Odoo standard."""
|
||||
request_obj = self.env['mrp.production.request']
|
||||
request_obj = self.env["mrp.production.request"]
|
||||
request_obj_sudo = request_obj.sudo().with_context(
|
||||
force_company=values['company_id'].id)
|
||||
force_company=values["company_id"].id
|
||||
)
|
||||
bom = self._get_matching_bom(product_id, values)
|
||||
if not bom:
|
||||
raise UserError(_(
|
||||
'There is no Bill of Material found for the product %s. '
|
||||
'Please define a Bill of Material for this product.') % (
|
||||
product_id.display_name,))
|
||||
raise UserError(
|
||||
_(
|
||||
"There is no Bill of Material found for the product %s. "
|
||||
"Please define a Bill of Material for this product."
|
||||
)
|
||||
% (product_id.display_name,)
|
||||
)
|
||||
|
||||
# create the MR as SUPERUSER because the current user may not
|
||||
# have the rights to do it (mto product launched by a sale for example)
|
||||
request = request_obj_sudo.create(
|
||||
self._prepare_mrp_production_request(
|
||||
product_id, product_qty, product_uom, location_id, name,
|
||||
origin, values, bom))
|
||||
origin_production = values.get('move_dest_ids') and \
|
||||
values['move_dest_ids'][0].raw_material_production_id or False
|
||||
orderpoint = values.get('orderpoint_id')
|
||||
product_id,
|
||||
product_qty,
|
||||
product_uom,
|
||||
location_id,
|
||||
name,
|
||||
origin,
|
||||
values,
|
||||
bom,
|
||||
)
|
||||
)
|
||||
origin_production = (
|
||||
values.get("move_dest_ids")
|
||||
and values["move_dest_ids"][0].raw_material_production_id
|
||||
or False
|
||||
)
|
||||
orderpoint = values.get("orderpoint_id")
|
||||
if orderpoint:
|
||||
request.message_post_with_view(
|
||||
'mail.message_origin_link',
|
||||
values={'self': request,
|
||||
'origin': orderpoint},
|
||||
subtype_id=self.env.ref('mail.mt_note').id,
|
||||
"mail.message_origin_link",
|
||||
values={"self": request, "origin": orderpoint},
|
||||
subtype_id=self.env.ref("mail.mt_note").id,
|
||||
)
|
||||
if origin_production:
|
||||
request.message_post_with_view(
|
||||
'mail.message_origin_link',
|
||||
values={'self': request,
|
||||
'origin': origin_production},
|
||||
subtype_id=self.env.ref('mail.mt_note').id,
|
||||
"mail.message_origin_link",
|
||||
values={"self": request, "origin": origin_production},
|
||||
subtype_id=self.env.ref("mail.mt_note").id,
|
||||
)
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def _run_manufacture(self, product_id, product_qty, product_uom,
|
||||
location_id, name, origin, values):
|
||||
def _run_manufacture(
|
||||
self, product_id, product_qty, product_uom, location_id, name, origin, values
|
||||
):
|
||||
if self._need_production_request(product_id):
|
||||
return self._run_production_request(
|
||||
product_id, product_qty, product_uom,
|
||||
location_id, name, origin, values)
|
||||
product_id, product_qty, product_uom, location_id, name, origin, values
|
||||
)
|
||||
|
||||
return super()._run_manufacture(
|
||||
product_id, product_qty, product_uom, location_id, name,
|
||||
origin, values)
|
||||
product_id, product_qty, product_uom, location_id, name, origin, values
|
||||
)
|
||||
|
||||
@@ -9,11 +9,14 @@ class Orderpoint(models.Model):
|
||||
|
||||
def _quantity_in_progress(self):
|
||||
res = super()._quantity_in_progress()
|
||||
mrp_requests = self.env['mrp.production.request'].search([
|
||||
('state', 'not in', ('done', 'cancel')),
|
||||
('orderpoint_id', 'in', self.ids),
|
||||
])
|
||||
mrp_requests = self.env["mrp.production.request"].search(
|
||||
[
|
||||
("state", "not in", ("done", "cancel")),
|
||||
("orderpoint_id", "in", self.ids),
|
||||
]
|
||||
)
|
||||
for rec in mrp_requests:
|
||||
res[rec.orderpoint_id.id] += rec.product_uom_id._compute_quantity(
|
||||
rec.pending_qty, rec.orderpoint_id.product_uom, round=False)
|
||||
rec.pending_qty, rec.orderpoint_id.product_uom, round=False
|
||||
)
|
||||
return res
|
||||
|
||||
@@ -2,32 +2,31 @@
|
||||
<!-- Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
|
||||
<odoo>
|
||||
|
||||
<data noupdate="0">
|
||||
|
||||
<record model="ir.module.category" id="module_category_mrp_production_request">
|
||||
<field name="name">Manufacturing Request</field>
|
||||
<field name="parent_id" ref="base.module_category_manufacturing" />
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
|
||||
<record id="group_mrp_production_request_user" model="res.groups">
|
||||
<field name="name">User</field>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]" />
|
||||
<field name="category_id" ref="module_category_mrp_production_request" />
|
||||
</record>
|
||||
|
||||
<record id="group_mrp_production_request_manager" model="res.groups">
|
||||
<field name="name">Manager</field>
|
||||
<field name="implied_ids"
|
||||
eval="[(4, ref('mrp_production_request.group_mrp_production_request_user'))]"/>
|
||||
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
<field
|
||||
name="implied_ids"
|
||||
eval="[(4, ref('mrp_production_request.group_mrp_production_request_user'))]"
|
||||
/>
|
||||
<field
|
||||
name="users"
|
||||
eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"
|
||||
/>
|
||||
<field name="category_id" ref="module_category_mrp_production_request" />
|
||||
</record>
|
||||
|
||||
</data>
|
||||
<data noupdate="0">
|
||||
|
||||
<record model="ir.rule" id="mrp_production_request_comp_rule">
|
||||
<field name="name">Manufacturing Request multi-company</field>
|
||||
<field name="model_id" ref="model_mrp_production_request" />
|
||||
@@ -35,11 +34,13 @@
|
||||
<field name="domain_force">['|',('company_id','=',False),
|
||||
('company_id','child_of',[user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_production_request_followers_rule" model="ir.rule">
|
||||
<field name="name">Follow Manufacturing Request</field>
|
||||
<field name="model_id" ref="model_mrp_production_request" />
|
||||
<field name="groups" eval="[(6,0, [ref('group_mrp_production_request_user')])]"/>
|
||||
<field
|
||||
name="groups"
|
||||
eval="[(6,0, [ref('group_mrp_production_request_user')])]"
|
||||
/>
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="False" />
|
||||
<field name="perm_create" eval="False" />
|
||||
@@ -47,27 +48,30 @@
|
||||
<field name="domain_force">['|',('requested_by','=',user.id),
|
||||
('message_partner_ids', 'in', [user.partner_id.id])]</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_production_request_rule" model="ir.rule">
|
||||
<field name="name">Manufacturing Request User</field>
|
||||
<field name="model_id" ref="model_mrp_production_request" />
|
||||
<field name="groups" eval="[(6,0, [ref('group_mrp_production_request_user')])]"/>
|
||||
<field
|
||||
name="groups"
|
||||
eval="[(6,0, [ref('group_mrp_production_request_user')])]"
|
||||
/>
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="True" />
|
||||
<field name="perm_create" eval="True" />
|
||||
<field name="perm_unlink" eval="True" />
|
||||
<field name="domain_force">[('requested_by','=',user.id)]</field>
|
||||
</record>
|
||||
|
||||
<record id="mpr_production_request_line_manager_rule" model="ir.rule">
|
||||
<field name="name">Manufacturing Request Line Manager</field>
|
||||
<field name="model_id" ref="model_mrp_production_request" />
|
||||
<field name="groups" eval="[(6,0, [ref('group_mrp_production_request_manager')])]"/>
|
||||
<field
|
||||
name="groups"
|
||||
eval="[(6,0, [ref('group_mrp_production_request_manager')])]"
|
||||
/>
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="True" />
|
||||
<field name="perm_create" eval="True" />
|
||||
<field name="perm_unlink" eval="True" />
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,101 +1,119 @@
|
||||
# Copyright 2017-18 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo import fields
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestMrpProductionRequest(TransactionCase):
|
||||
def setUp(self, *args, **kwargs):
|
||||
super().setUp(*args, **kwargs)
|
||||
self.production_model = self.env['mrp.production']
|
||||
self.request_model = self.env['mrp.production.request']
|
||||
self.wiz_model = self.env['mrp.production.request.create.mo']
|
||||
self.bom_model = self.env['mrp.bom']
|
||||
self.group_model = self.env['procurement.group']
|
||||
self.product_model = self.env['product.product']
|
||||
self.bom_model = self.env['mrp.bom']
|
||||
self.boml_model = self.env['mrp.bom.line']
|
||||
self.production_model = self.env["mrp.production"]
|
||||
self.request_model = self.env["mrp.production.request"]
|
||||
self.wiz_model = self.env["mrp.production.request.create.mo"]
|
||||
self.bom_model = self.env["mrp.bom"]
|
||||
self.group_model = self.env["procurement.group"]
|
||||
self.product_model = self.env["product.product"]
|
||||
self.bom_model = self.env["mrp.bom"]
|
||||
self.boml_model = self.env["mrp.bom.line"]
|
||||
|
||||
self.warehouse = self.env.ref('stock.warehouse0')
|
||||
self.stock_loc = self.env.ref('stock.stock_location_stock')
|
||||
route_manuf = self.env.ref('mrp.route_warehouse0_manufacture')
|
||||
self.warehouse = self.env.ref("stock.warehouse0")
|
||||
self.stock_loc = self.env.ref("stock.stock_location_stock")
|
||||
route_manuf = self.env.ref("mrp.route_warehouse0_manufacture")
|
||||
|
||||
# Prepare Products:
|
||||
self.product = self.env.ref('product.product_product_3')
|
||||
self.product = self.env.ref("product.product_product_3")
|
||||
self.product.mrp_production_request = True
|
||||
self.product.route_ids = [(4, route_manuf.id, 0)]
|
||||
|
||||
self.product_no_bom = self.product_model.create({
|
||||
'name': 'Test Product without BoM',
|
||||
'mrp_production_request': True,
|
||||
'route_ids': [(6, 0, route_manuf.ids)],
|
||||
})
|
||||
self.product_orderpoint = self.product_model.create({
|
||||
'name': 'Test Product for orderpoint',
|
||||
'mrp_production_request': True,
|
||||
'route_ids': [(6, 0, route_manuf.ids)],
|
||||
})
|
||||
product_component = self.product_model.create({
|
||||
'name': 'Test component',
|
||||
'mrp_production_request': True,
|
||||
'route_ids': [(6, 0, route_manuf.ids)],
|
||||
})
|
||||
self.product_no_bom = self.product_model.create(
|
||||
{
|
||||
"name": "Test Product without BoM",
|
||||
"mrp_production_request": True,
|
||||
"route_ids": [(6, 0, route_manuf.ids)],
|
||||
}
|
||||
)
|
||||
self.product_orderpoint = self.product_model.create(
|
||||
{
|
||||
"name": "Test Product for orderpoint",
|
||||
"mrp_production_request": True,
|
||||
"route_ids": [(6, 0, route_manuf.ids)],
|
||||
}
|
||||
)
|
||||
product_component = self.product_model.create(
|
||||
{
|
||||
"name": "Test component",
|
||||
"mrp_production_request": True,
|
||||
"route_ids": [(6, 0, route_manuf.ids)],
|
||||
}
|
||||
)
|
||||
|
||||
# Create Bill of Materials:
|
||||
self.test_bom_1 = self.bom_model.create({
|
||||
'product_id': self.product_orderpoint.id,
|
||||
'product_tmpl_id': self.product_orderpoint.product_tmpl_id.id,
|
||||
'product_uom_id': self.product_orderpoint.uom_id.id,
|
||||
'product_qty': 1.0,
|
||||
'type': 'normal',
|
||||
})
|
||||
self.boml_model.create({
|
||||
'bom_id': self.test_bom_1.id,
|
||||
'product_id': product_component.id,
|
||||
'product_qty': 1.0,
|
||||
})
|
||||
self.test_bom_1 = self.bom_model.create(
|
||||
{
|
||||
"product_id": self.product_orderpoint.id,
|
||||
"product_tmpl_id": self.product_orderpoint.product_tmpl_id.id,
|
||||
"product_uom_id": self.product_orderpoint.uom_id.id,
|
||||
"product_qty": 1.0,
|
||||
"type": "normal",
|
||||
}
|
||||
)
|
||||
self.boml_model.create(
|
||||
{
|
||||
"bom_id": self.test_bom_1.id,
|
||||
"product_id": product_component.id,
|
||||
"product_qty": 1.0,
|
||||
}
|
||||
)
|
||||
|
||||
# Create Orderpoint:
|
||||
self.orderpoint = self.env['stock.warehouse.orderpoint'].create({
|
||||
'warehouse_id': self.warehouse.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
'product_id': self.product_orderpoint.id,
|
||||
'product_min_qty': 10.0,
|
||||
'product_max_qty': 50.0,
|
||||
'product_uom': self.product_orderpoint.uom_id.id,
|
||||
})
|
||||
self.orderpoint = self.env["stock.warehouse.orderpoint"].create(
|
||||
{
|
||||
"warehouse_id": self.warehouse.id,
|
||||
"location_id": self.warehouse.lot_stock_id.id,
|
||||
"product_id": self.product_orderpoint.id,
|
||||
"product_min_qty": 10.0,
|
||||
"product_max_qty": 50.0,
|
||||
"product_uom": self.product_orderpoint.uom_id.id,
|
||||
}
|
||||
)
|
||||
|
||||
# Create Procurement Group:
|
||||
self.test_group = self.group_model.create({
|
||||
'name': 'TEST',
|
||||
})
|
||||
self.test_group = self.group_model.create({"name": "TEST",})
|
||||
|
||||
# Create User:
|
||||
self.test_user = self.env['res.users'].create({
|
||||
'name': 'John',
|
||||
'login': 'test',
|
||||
})
|
||||
self.test_user = self.env["res.users"].create(
|
||||
{"name": "John", "login": "test",}
|
||||
)
|
||||
|
||||
def procure(self, group, product, qty=4.0,):
|
||||
def procure(
|
||||
self, group, product, qty=4.0,
|
||||
):
|
||||
values = {
|
||||
'date_planned': fields.Datetime.now(),
|
||||
'group_id': group,
|
||||
"date_planned": fields.Datetime.now(),
|
||||
"group_id": group,
|
||||
}
|
||||
self.group_model.run(
|
||||
product, qty, product.uom_id, self.stock_loc,
|
||||
group.name, group.name, values,
|
||||
product,
|
||||
qty,
|
||||
product.uom_id,
|
||||
self.stock_loc,
|
||||
group.name,
|
||||
group.name,
|
||||
values,
|
||||
)
|
||||
return True
|
||||
|
||||
def test_01_manufacture_request(self):
|
||||
"""Tests manufacture request workflow."""
|
||||
self.procure(self.test_group, self.product)
|
||||
request = self.request_model.search([
|
||||
('product_id', '=', self.product.id),
|
||||
('procurement_group_id', '=', self.test_group.id),
|
||||
])
|
||||
request = self.request_model.search(
|
||||
[
|
||||
("product_id", "=", self.product.id),
|
||||
("procurement_group_id", "=", self.test_group.id),
|
||||
]
|
||||
)
|
||||
self.assertEqual(len(request), 1)
|
||||
request.button_to_approve()
|
||||
request.button_draft()
|
||||
@@ -103,13 +121,14 @@ class TestMrpProductionRequest(TransactionCase):
|
||||
request.button_approved()
|
||||
self.assertEqual(request.pending_qty, 4.0)
|
||||
wiz = self.wiz_model.with_context(
|
||||
active_ids=request.ids,
|
||||
active_model="mrp.production.request").create({})
|
||||
active_ids=request.ids, active_model="mrp.production.request"
|
||||
).create({})
|
||||
wiz.compute_product_line_ids()
|
||||
wiz.mo_qty = 4.0
|
||||
wiz.create_mo()
|
||||
mo = self.production_model.search([
|
||||
('mrp_production_request_id', '=', request.id)])
|
||||
mo = self.production_model.search(
|
||||
[("mrp_production_request_id", "=", request.id)]
|
||||
)
|
||||
self.assertTrue(mo, "No MO created.")
|
||||
self.assertEqual(request.pending_qty, 0.0)
|
||||
request.button_done()
|
||||
@@ -117,39 +136,42 @@ class TestMrpProductionRequest(TransactionCase):
|
||||
def test_02_assignation(self):
|
||||
"""Tests assignation of manufacturing requests."""
|
||||
randon_bom_id = self.bom_model.search([], limit=1).id
|
||||
request = self.request_model.create({
|
||||
'assigned_to': self.test_user.id,
|
||||
'product_id': self.product.id,
|
||||
'product_qty': 5.0,
|
||||
'bom_id': randon_bom_id,
|
||||
})
|
||||
request = self.request_model.create(
|
||||
{
|
||||
"assigned_to": self.test_user.id,
|
||||
"product_id": self.product.id,
|
||||
"product_qty": 5.0,
|
||||
"bom_id": randon_bom_id,
|
||||
}
|
||||
)
|
||||
request._onchange_product_id()
|
||||
self.assertEqual(
|
||||
request.bom_id.product_tmpl_id, self.product.product_tmpl_id,
|
||||
"Wrong Bill of Materials.")
|
||||
request.write({
|
||||
'assigned_to': self.uid,
|
||||
})
|
||||
self.assertTrue(request.message_follower_ids,
|
||||
"Followers not added correctly.")
|
||||
request.bom_id.product_tmpl_id,
|
||||
self.product.product_tmpl_id,
|
||||
"Wrong Bill of Materials.",
|
||||
)
|
||||
request.write(
|
||||
{"assigned_to": self.uid,}
|
||||
)
|
||||
self.assertTrue(request.message_follower_ids, "Followers not added correctly.")
|
||||
|
||||
def test_03_substract_qty_from_orderpoint(self):
|
||||
"""Quantity in Manufacturing Requests should be considered by
|
||||
orderpoints."""
|
||||
request = self.request_model.search([
|
||||
('product_id', '=', self.product_orderpoint.id),
|
||||
])
|
||||
request = self.request_model.search(
|
||||
[("product_id", "=", self.product_orderpoint.id),]
|
||||
)
|
||||
self.assertFalse(request)
|
||||
self.env['procurement.group'].run_scheduler()
|
||||
request = self.request_model.search([
|
||||
('product_id', '=', self.product_orderpoint.id),
|
||||
])
|
||||
self.env["procurement.group"].run_scheduler()
|
||||
request = self.request_model.search(
|
||||
[("product_id", "=", self.product_orderpoint.id),]
|
||||
)
|
||||
self.assertEqual(len(request), 1)
|
||||
# Running again the scheduler should not generate a new MR.
|
||||
self.env['procurement.group'].run_scheduler()
|
||||
request = self.request_model.search([
|
||||
('product_id', '=', self.product_orderpoint.id),
|
||||
])
|
||||
self.env["procurement.group"].run_scheduler()
|
||||
request = self.request_model.search(
|
||||
[("product_id", "=", self.product_orderpoint.id),]
|
||||
)
|
||||
self.assertEqual(len(request), 1)
|
||||
|
||||
def test_04_raise_errors(self):
|
||||
|
||||
@@ -2,60 +2,94 @@
|
||||
<!-- Copyright 2017-18 Eficent Business and IT Consulting Services S.L.
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
|
||||
<odoo>
|
||||
|
||||
<record model="ir.ui.view" id="view_mrp_production_request_form">
|
||||
<field name="name">mrp.production.request.form</field>
|
||||
<field name="model">mrp.production.request</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Manufacturing Request">
|
||||
<header>
|
||||
<button name="button_draft"
|
||||
<button
|
||||
name="button_draft"
|
||||
states="to_approve,approved,cancel,done"
|
||||
string="Reset"
|
||||
type="object"
|
||||
groups="mrp_production_request.group_mrp_production_request_manager"/>
|
||||
<button name="button_to_approve" states="draft"
|
||||
string="Request approval" type="object"
|
||||
class="oe_highlight"/>
|
||||
<button name="button_approved" states="to_approve"
|
||||
string="Approve" type="object" class="oe_highlight"
|
||||
groups="mrp_production_request.group_mrp_production_request_manager"/>
|
||||
<button name="%(mrp_production_request_create_mo_action)d"
|
||||
groups="mrp_production_request.group_mrp_production_request_manager"
|
||||
/>
|
||||
<button
|
||||
name="button_to_approve"
|
||||
states="draft"
|
||||
string="Request approval"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
<button
|
||||
name="button_approved"
|
||||
states="to_approve"
|
||||
string="Approve"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
groups="mrp_production_request.group_mrp_production_request_manager"
|
||||
/>
|
||||
<button
|
||||
name="%(mrp_production_request_create_mo_action)d"
|
||||
states="approved"
|
||||
string="Create Manufacturing Order" type="action"/>
|
||||
<button name="button_done" states="approved"
|
||||
string="Done" type="object" class="oe_highlight"
|
||||
groups="mrp_production_request.group_mrp_production_request_manager"/>
|
||||
<button name="button_cancel" states="to_approve,approved"
|
||||
string="Reject" type="object"
|
||||
groups="mrp_production_request.group_mrp_production_request_manager"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,approved,done"/>
|
||||
string="Create Manufacturing Order"
|
||||
type="action"
|
||||
/>
|
||||
<button
|
||||
name="button_done"
|
||||
states="approved"
|
||||
string="Done"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
groups="mrp_production_request.group_mrp_production_request_manager"
|
||||
/>
|
||||
<button
|
||||
name="button_cancel"
|
||||
states="to_approve,approved"
|
||||
string="Reject"
|
||||
type="object"
|
||||
groups="mrp_production_request.group_mrp_production_request_manager"
|
||||
/>
|
||||
<field
|
||||
name="state"
|
||||
widget="statusbar"
|
||||
statusbar_visible="draft,approved,done"
|
||||
/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button type="object" name="action_view_mrp_productions"
|
||||
<button
|
||||
type="object"
|
||||
name="action_view_mrp_productions"
|
||||
class="oe_stat_button"
|
||||
icon="fa-wrench"
|
||||
attrs="{'invisible': [('mrp_production_count', '=', 0)]}">
|
||||
<field name="mrp_production_count" widget="statinfo"
|
||||
string="MOs"/>
|
||||
attrs="{'invisible': [('mrp_production_count', '=', 0)]}"
|
||||
>
|
||||
<field
|
||||
name="mrp_production_count"
|
||||
widget="statinfo"
|
||||
string="MOs"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<label for="name" string="Manufacturing Request" />
|
||||
<h1>
|
||||
<field name="name" />
|
||||
</h1>
|
||||
<group name="request">
|
||||
<group>
|
||||
<field name="product_id"
|
||||
domain="[('bom_ids','!=',False),('bom_ids.type','!=','phantom')]"/>
|
||||
<field
|
||||
name="product_id"
|
||||
domain="[('bom_ids','!=',False),('bom_ids.type','!=','phantom')]"
|
||||
/>
|
||||
<field name="product_tmpl_id" invisible="1" />
|
||||
<field name="bom_id"
|
||||
<field
|
||||
name="bom_id"
|
||||
domain="['&', '|', ('product_id','=',product_id),
|
||||
'&', ('product_tmpl_id.product_variant_ids','=',product_id),
|
||||
('product_id','=',False), ('type', '=', 'normal')]"/>
|
||||
('product_id','=',False), ('type', '=', 'normal')]"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="product_qty" />
|
||||
@@ -86,7 +120,6 @@
|
||||
<field name="location_src_id" />
|
||||
<field name="location_dest_id" />
|
||||
<field name="picking_type_id" />
|
||||
|
||||
</group>
|
||||
<group>
|
||||
<field name="origin" />
|
||||
@@ -103,28 +136,32 @@
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mrp_production_request_tree">
|
||||
<field name="name">mrp.production.request.tree</field>
|
||||
<field name="model">mrp.production.request</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree decoration-info="state in ('draft','to_approve')"
|
||||
<tree
|
||||
decoration-info="state in ('draft','to_approve')"
|
||||
decoration-muted="state in ('cancel')"
|
||||
string="Manufacturing Requests">
|
||||
string="Manufacturing Requests"
|
||||
>
|
||||
<field name="name" />
|
||||
<field name="product_id" />
|
||||
<field name="requested_by" />
|
||||
<field name="assigned_to" />
|
||||
<field name="product_qty" />
|
||||
<field name="pending_qty" />
|
||||
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
|
||||
<field
|
||||
name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
widget="selection"
|
||||
/>
|
||||
<field name="origin" />
|
||||
<field name="date_planned_start" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_mrp_production_request_search" model="ir.ui.view">
|
||||
<field name="name">mrp.production.request.search</field>
|
||||
<field name="model">mrp.production.request</field>
|
||||
@@ -140,62 +177,98 @@
|
||||
<field name="state" />
|
||||
<field name="pending_qty" invisible="1" />
|
||||
<!--Filters:-->
|
||||
<filter name="unassigned" string="Unassigned"
|
||||
<filter
|
||||
name="unassigned"
|
||||
string="Unassigned"
|
||||
domain="[('assigned_to','=', False)]"
|
||||
help="Unassigned Request"/>
|
||||
<filter name="assigned_to_me"
|
||||
help="Unassigned Request"
|
||||
/>
|
||||
<filter
|
||||
name="assigned_to_me"
|
||||
domain="[('assigned_to','=', uid)]"
|
||||
string="Assigned to me"/>
|
||||
<filter name="requested_by_me"
|
||||
string="Assigned to me"
|
||||
/>
|
||||
<filter
|
||||
name="requested_by_me"
|
||||
domain="[('requested_by','=', uid)]"
|
||||
string="My requests" help="Requested by me"/>
|
||||
string="My requests"
|
||||
help="Requested by me"
|
||||
/>
|
||||
<separator />
|
||||
<filter name="pending"
|
||||
<filter
|
||||
name="pending"
|
||||
domain="[('pending_qty','!=', 0.0)]"
|
||||
string="Pending Qty"
|
||||
help="Request with pending quantity"/>
|
||||
help="Request with pending quantity"
|
||||
/>
|
||||
<separator />
|
||||
<filter name="todo" string="To Do"
|
||||
<filter
|
||||
name="todo"
|
||||
string="To Do"
|
||||
domain="[('state','in',('draft', 'to_approve','approved'))]"
|
||||
help="Manufacturing Requests not done or cancelled."/>
|
||||
help="Manufacturing Requests not done or cancelled."
|
||||
/>
|
||||
<separator />
|
||||
<filter name="state_draft" string="Draft"
|
||||
<filter
|
||||
name="state_draft"
|
||||
string="Draft"
|
||||
domain="[('state','=','draft')]"
|
||||
help="Request is to be approved"/>
|
||||
<filter name="state_to_approve" string="To Approve"
|
||||
help="Request is to be approved"
|
||||
/>
|
||||
<filter
|
||||
name="state_to_approve"
|
||||
string="To Approve"
|
||||
domain="[('state','=','to_approve')]"
|
||||
help="Request is to be approved"/>
|
||||
<filter name="state_approved" string="Approved"
|
||||
help="Request is to be approved"
|
||||
/>
|
||||
<filter
|
||||
name="state_approved"
|
||||
string="Approved"
|
||||
domain="[('state','=','approved')]"
|
||||
help="Request is approved"/>
|
||||
<filter name="state_cancel" string="Cancelled"
|
||||
help="Request is approved"
|
||||
/>
|
||||
<filter
|
||||
name="state_cancel"
|
||||
string="Cancelled"
|
||||
domain="[('state','=','cancel')]"
|
||||
help="Request is cancelled"/>
|
||||
<filter name="state_done" string="Done"
|
||||
help="Request is cancelled"
|
||||
/>
|
||||
<filter
|
||||
name="state_done"
|
||||
string="Done"
|
||||
domain="[('state','=','done')]"
|
||||
help="Request is done"/>
|
||||
help="Request is done"
|
||||
/>
|
||||
<separator />
|
||||
<filter name="message_needaction"
|
||||
<filter
|
||||
name="message_needaction"
|
||||
string="Unread Messages"
|
||||
domain="[('message_needaction','=',True)]"/>
|
||||
domain="[('message_needaction','=',True)]"
|
||||
/>
|
||||
<!--Group by:-->
|
||||
<filter name="requested_by_group_by"
|
||||
string="Requested by" icon="terp-personal"
|
||||
<filter
|
||||
name="requested_by_group_by"
|
||||
string="Requested by"
|
||||
icon="terp-personal"
|
||||
domain="[]"
|
||||
context="{'group_by':'requested_by'}"/>
|
||||
<filter name="assign_to_group_by"
|
||||
string="Assigned to" icon="terp-personal"
|
||||
context="{'group_by':'requested_by'}"
|
||||
/>
|
||||
<filter
|
||||
name="assign_to_group_by"
|
||||
string="Assigned to"
|
||||
icon="terp-personal"
|
||||
domain="[]"
|
||||
context="{'group_by':'assigned_to'}"/>
|
||||
<filter name="origin_group_by"
|
||||
context="{'group_by':'assigned_to'}"
|
||||
/>
|
||||
<filter
|
||||
name="origin_group_by"
|
||||
string="Source"
|
||||
domain="[]"
|
||||
context="{'group_by':'origin'}"/>
|
||||
context="{'group_by':'origin'}"
|
||||
/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="mrp_production_request_action">
|
||||
<field name="name">Manufacturing Requests</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
@@ -206,28 +279,25 @@
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to start a new manufacturing request process.
|
||||
</p><p>
|
||||
</p>
|
||||
<p>
|
||||
A Manufacturing Request is an instruction to production to produce
|
||||
a certain quantity of a given product.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_mrp_production_request_act"
|
||||
sequence="10"
|
||||
parent="mrp.menu_mrp_manufacturing"
|
||||
action="mrp_production_request_action"
|
||||
/>
|
||||
|
||||
<!--Sever actions-->
|
||||
<record id="action_server_mrp_production_request_refresh"
|
||||
model="ir.actions.server">
|
||||
<record id="action_server_mrp_production_request_refresh" model="ir.actions.server">
|
||||
<field name="name">Refresh Quantities</field>
|
||||
<field name="model_id" ref="model_mrp_production_request" />
|
||||
<field name="binding_model_id" ref="model_mrp_production_request" />
|
||||
<field name="state">code</field>
|
||||
<field name="code">records._compute_manufactured_qty()</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
<!-- Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
|
||||
<odoo>
|
||||
|
||||
<record id="mrp_production_form_view" model="ir.ui.view">
|
||||
<field name="name">mrp.production.form - mrp_production_request</field>
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="inherit_id"
|
||||
ref="mrp.mrp_production_form_view"/>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="availability" position="after">
|
||||
<field name="mrp_production_request_id" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
<!-- Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
|
||||
<odoo>
|
||||
|
||||
<record model="ir.ui.view" id="view_template_property_form">
|
||||
<field name="name">product.template.form - mrp_production_request</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id"
|
||||
ref="stock.view_template_property_form"/>
|
||||
<field name="inherit_id" ref="stock.view_template_property_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="route_ids" position="after">
|
||||
<field name="mrp_production_request" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# Copyright 2017-19 Eficent Business and IT Consulting Services S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
import odoo.addons.decimal_precision as dp
|
||||
|
||||
|
||||
class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
_name = "mrp.production.request.create.mo"
|
||||
@@ -16,14 +17,16 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
res = self._prepare_lines()
|
||||
product_lines = res[1]
|
||||
for line in product_lines:
|
||||
self.env['mrp.production.request.create.mo.line'].create(
|
||||
self._prepare_product_line(line))
|
||||
self.env["mrp.production.request.create.mo.line"].create(
|
||||
self._prepare_product_line(line)
|
||||
)
|
||||
self._get_mo_qty()
|
||||
# The wizard must be reloaded in order to show the new product lines
|
||||
action = self.env.ref(
|
||||
'mrp_production_request.mrp_production_request_create_mo_action')
|
||||
"mrp_production_request.mrp_production_request_create_mo_action"
|
||||
)
|
||||
res = action.read()[0]
|
||||
res['res_id'] = self.id
|
||||
res["res_id"] = self.id
|
||||
return res
|
||||
|
||||
def _prepare_lines(self):
|
||||
@@ -32,70 +35,71 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
:return: boms_done, lines_done
|
||||
"""
|
||||
bom_point = self.bom_id
|
||||
factor = self.mrp_production_request_id.product_uom_id.\
|
||||
_compute_quantity(self.pending_qty, bom_point.product_uom_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)
|
||||
self.mrp_production_request_id.product_id, factor / bom_point.product_qty
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def _get_mo_qty(self):
|
||||
"""Propose a qty to create a MO available to produce."""
|
||||
for rec in self:
|
||||
bottle_neck = min(rec.product_line_ids.mapped(
|
||||
'bottle_neck_factor'))
|
||||
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)
|
||||
bom_id = fields.Many2one(
|
||||
related='mrp_production_request_id.bom_id', readonly=True)
|
||||
comodel_name="mrp.production.request", readonly=True
|
||||
)
|
||||
bom_id = fields.Many2one(related="mrp_production_request_id.bom_id", readonly=True)
|
||||
mo_qty = fields.Float(
|
||||
string="Quantity",
|
||||
digits=dp.get_precision("Product Unit of Measure"))
|
||||
string="Quantity", digits=dp.get_precision("Product Unit of Measure")
|
||||
)
|
||||
pending_qty = fields.Float(
|
||||
related="mrp_production_request_id.pending_qty",
|
||||
digits=dp.get_precision("Product Unit of Measure"))
|
||||
product_uom_id = fields.Many2one(
|
||||
related="mrp_production_request_id.product_uom_id")
|
||||
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",
|
||||
inverse_name="mrp_production_request_create_mo_id", readonly=True)
|
||||
date_planned_start = fields.Datetime(
|
||||
string="Deadline Start",
|
||||
required=True,
|
||||
)
|
||||
date_planned_finished = fields.Datetime(
|
||||
string="Deadline End",
|
||||
required=True,
|
||||
inverse_name="mrp_production_request_create_mo_id",
|
||||
readonly=True,
|
||||
)
|
||||
date_planned_start = fields.Datetime(string="Deadline Start", required=True,)
|
||||
date_planned_finished = fields.Datetime(string="Deadline End", required=True,)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
rec = super().default_get(fields)
|
||||
active_ids = self._context.get('active_ids')
|
||||
active_model = self._context.get('active_model')
|
||||
active_ids = self._context.get("active_ids")
|
||||
active_model = self._context.get("active_model")
|
||||
if not active_ids:
|
||||
raise UserError(_(
|
||||
raise UserError(
|
||||
_(
|
||||
"Programming error: wizard action executed without "
|
||||
"active_ids in context."))
|
||||
"active_ids in context."
|
||||
)
|
||||
)
|
||||
request = self.env[active_model].browse(active_ids)
|
||||
rec.update({
|
||||
'mrp_production_request_id': active_ids[0],
|
||||
'date_planned_start': request[0].date_planned_start,
|
||||
'date_planned_finished': request[0].date_planned_finished,
|
||||
})
|
||||
rec.update(
|
||||
{
|
||||
"mrp_production_request_id": active_ids[0],
|
||||
"date_planned_start": request[0].date_planned_start,
|
||||
"date_planned_finished": request[0].date_planned_finished,
|
||||
}
|
||||
)
|
||||
return rec
|
||||
|
||||
def _prepare_product_line(self, pl):
|
||||
return {
|
||||
'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,
|
||||
"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,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
@@ -103,35 +107,34 @@ class MrpProductionRequestCreateMo(models.TransientModel):
|
||||
self.ensure_one()
|
||||
request_id = self.mrp_production_request_id
|
||||
return {
|
||||
'product_id': request_id.product_id.id,
|
||||
'bom_id': request_id.bom_id.id,
|
||||
'product_qty': self.mo_qty,
|
||||
'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,
|
||||
'date_planned_start': self.date_planned_start,
|
||||
'date_planned_finished': self.date_planned_finished,
|
||||
'procurement_group_id': request_id.procurement_group_id.id,
|
||||
'propagate': request_id.propagate,
|
||||
'company_id': request_id.company_id.id,
|
||||
"product_id": request_id.product_id.id,
|
||||
"bom_id": request_id.bom_id.id,
|
||||
"product_qty": self.mo_qty,
|
||||
"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,
|
||||
"date_planned_start": self.date_planned_start,
|
||||
"date_planned_finished": self.date_planned_finished,
|
||||
"procurement_group_id": request_id.procurement_group_id.id,
|
||||
"propagate": request_id.propagate,
|
||||
"company_id": request_id.company_id.id,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def create_mo(self):
|
||||
self.ensure_one()
|
||||
vals = self._prepare_manufacturing_order()
|
||||
mo = self.env['mrp.production'].create(vals)
|
||||
mo = self.env["mrp.production"].create(vals)
|
||||
# Open resulting MO:
|
||||
action = self.env.ref('mrp.mrp_production_action').read()[0]
|
||||
res = self.env.ref('mrp.mrp_production_form_view')
|
||||
action.update({
|
||||
'res_id': mo and mo.id,
|
||||
'views': [(res and res.id or False, 'form')],
|
||||
})
|
||||
action = self.env.ref("mrp.mrp_production_action").read()[0]
|
||||
res = self.env.ref("mrp.mrp_production_form_view")
|
||||
action.update(
|
||||
{"res_id": mo and mo.id, "views": [(res and res.id or False, "form")],}
|
||||
)
|
||||
return action
|
||||
|
||||
|
||||
@@ -143,11 +146,13 @@ class MrpProductionRequestCreateMoLine(models.TransientModel):
|
||||
def _compute_available_qty(self):
|
||||
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']
|
||||
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)
|
||||
product_available, rec.product_uom_id
|
||||
)
|
||||
rec.available_qty = res
|
||||
|
||||
@api.multi
|
||||
@@ -157,19 +162,23 @@ class MrpProductionRequestCreateMoLine(models.TransientModel):
|
||||
rec.bottle_neck_factor = rec.available_qty / rec.product_qty
|
||||
|
||||
product_id = fields.Many2one(
|
||||
comodel_name='product.product', string='Product', required=True)
|
||||
comodel_name="product.product", string="Product", required=True
|
||||
)
|
||||
product_qty = fields.Float(
|
||||
string='Quantity Required', required=True,
|
||||
digits=dp.get_precision('Product Unit of Measure'))
|
||||
string="Quantity Required",
|
||||
required=True,
|
||||
digits=dp.get_precision("Product Unit of Measure"),
|
||||
)
|
||||
product_uom_id = fields.Many2one(
|
||||
comodel_name='uom.uom', string='UoM', required=True)
|
||||
comodel_name="uom.uom", string="UoM", required=True
|
||||
)
|
||||
mrp_production_request_create_mo_id = fields.Many2one(
|
||||
comodel_name='mrp.production.request.create.mo')
|
||||
comodel_name="mrp.production.request.create.mo"
|
||||
)
|
||||
available_qty = fields.Float(
|
||||
string='Quantity Available', compute='_compute_available_qty',
|
||||
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)
|
||||
string="Quantity Available",
|
||||
compute="_compute_available_qty",
|
||||
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)
|
||||
|
||||
@@ -2,20 +2,25 @@
|
||||
<!-- Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
|
||||
<odoo>
|
||||
|
||||
<record id="mrp_production_request_create_mo_view" model="ir.ui.view">
|
||||
<field name="name">mrp.production.request.create.mo.form</field>
|
||||
<field name="model">mrp.production.request.create.mo</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Select event to register">
|
||||
<group name="origin" string="Manufacture Request status"
|
||||
col="6">
|
||||
<group name="origin" string="Manufacture Request status" col="6">
|
||||
<group colspan="2">
|
||||
<field name="mrp_production_request_id"
|
||||
options='{"no_open": True}'/>
|
||||
<field
|
||||
name="mrp_production_request_id"
|
||||
options='{"no_open": True}'
|
||||
/>
|
||||
<field name="pending_qty" />
|
||||
<button name="compute_product_line_ids" type="object"
|
||||
string="Compute lines" colspan="2" icon="fa-cogs"/>
|
||||
<button
|
||||
name="compute_product_line_ids"
|
||||
type="object"
|
||||
string="Compute lines"
|
||||
colspan="2"
|
||||
icon="fa-cogs"
|
||||
/>
|
||||
</group>
|
||||
<group colspan="4">
|
||||
<field name="product_line_ids" nolabel="1">
|
||||
@@ -34,7 +39,12 @@
|
||||
<label for="mo_qty" />
|
||||
<div>
|
||||
<field name="mo_qty" class="oe_inline" />
|
||||
<field name="product_uom_id" class="oe_inline" options="{'no_open': True}" groups="uom.group_uom"/>
|
||||
<field
|
||||
name="product_uom_id"
|
||||
class="oe_inline"
|
||||
options="{'no_open': True}"
|
||||
groups="uom.group_uom"
|
||||
/>
|
||||
</div>
|
||||
</group>
|
||||
<group name="date_planned">
|
||||
@@ -42,20 +52,19 @@
|
||||
<field name="date_planned_finished" />
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<footer>
|
||||
<button name="create_mo"
|
||||
<button
|
||||
name="create_mo"
|
||||
type="object"
|
||||
string="Create MO"
|
||||
class="oe_highlight"/>
|
||||
class="oe_highlight"
|
||||
/>
|
||||
or
|
||||
<button special="cancel"
|
||||
string="Cancel"/>
|
||||
<button special="cancel" string="Cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_production_request_create_mo_action" model="ir.actions.act_window">
|
||||
<field name="name">Create Manufacturing Order</field>
|
||||
<field name="res_model">mrp.production.request.create.mo</field>
|
||||
@@ -63,5 +72,4 @@
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
1
setup/mrp_production_request/odoo/addons/mrp_production_request
Symbolic link
1
setup/mrp_production_request/odoo/addons/mrp_production_request
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../mrp_production_request
|
||||
6
setup/mrp_production_request/setup.py
Normal file
6
setup/mrp_production_request/setup.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['setuptools-odoo'],
|
||||
odoo_addon=True,
|
||||
)
|
||||
Reference in New Issue
Block a user