mirror of
https://github.com/bringout/oca-ocb-mrp.git
synced 2026-04-25 15:12:02 +02:00
19.0 vanilla
This commit is contained in:
parent
accf5918df
commit
6e65e8c877
688 changed files with 225434 additions and 199401 deletions
|
|
@ -1,19 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _, tools
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import float_round, float_is_zero
|
||||
|
||||
|
||||
class MrpRoutingWorkcenter(models.Model):
|
||||
_name = 'mrp.routing.workcenter'
|
||||
_description = 'Work Center Usage'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
_order = 'bom_id, sequence, id'
|
||||
_check_company_auto = True
|
||||
|
||||
name = fields.Char('Operation', required=True)
|
||||
active = fields.Boolean(default=True)
|
||||
workcenter_id = fields.Many2one('mrp.workcenter', 'Work Center', required=True, check_company=True)
|
||||
workcenter_id = fields.Many2one('mrp.workcenter', 'Work Center', required=True, check_company=True, tracking=True, index=True)
|
||||
sequence = fields.Integer(
|
||||
'Sequence', default=100,
|
||||
help="Gives the sequence order when displaying a list of routing Work Centers.")
|
||||
|
|
@ -21,25 +24,18 @@ class MrpRoutingWorkcenter(models.Model):
|
|||
'mrp.bom', 'Bill of Material',
|
||||
index=True, ondelete='cascade', required=True, check_company=True)
|
||||
company_id = fields.Many2one('res.company', 'Company', related='bom_id.company_id')
|
||||
worksheet_type = fields.Selection([
|
||||
('pdf', 'PDF'), ('google_slide', 'Google Slide'), ('text', 'Text')],
|
||||
string="Worksheet", default="text"
|
||||
)
|
||||
note = fields.Html('Description')
|
||||
worksheet = fields.Binary('PDF')
|
||||
worksheet_google_slide = fields.Char('Google Slide', help="Paste the url of your Google Slide. Make sure the access to the document is public.")
|
||||
time_mode = fields.Selection([
|
||||
('auto', 'Compute based on tracked time'),
|
||||
('manual', 'Set duration manually')], string='Duration Computation',
|
||||
default='manual')
|
||||
('manual', 'Fixed'),
|
||||
('auto', 'Computed')], string='Duration Computation',
|
||||
default='manual', tracking=True)
|
||||
time_mode_batch = fields.Integer('Based on', default=10)
|
||||
time_computed_on = fields.Char('Computed on last', compute='_compute_time_computed_on')
|
||||
time_cycle_manual = fields.Float(
|
||||
'Manual Duration', default=60,
|
||||
'Manual Duration', default=60, tracking=True,
|
||||
help="Time in minutes:"
|
||||
"- In manual mode, time used"
|
||||
"- In automatic mode, supposed first time when there aren't any work orders yet")
|
||||
time_cycle = fields.Float('Duration', compute="_compute_time_cycle")
|
||||
"- In fixed mode, time used"
|
||||
"- In computed mode, supposed first time when there aren't any work orders yet")
|
||||
time_cycle = fields.Float('Cycles', compute="_compute_time_cycle")
|
||||
workorder_count = fields.Integer("# Work Orders", compute="_compute_workorder_count")
|
||||
workorder_ids = fields.One2many('mrp.workorder', 'operation_id', string="Work Orders")
|
||||
possible_bom_product_template_attribute_value_ids = fields.Many2many(related='bom_id.possible_product_template_attribute_value_ids')
|
||||
|
|
@ -58,20 +54,33 @@ class MrpRoutingWorkcenter(models.Model):
|
|||
string="Blocks", help="Operations that cannot start before this operation is completed.",
|
||||
domain="[('allow_operation_dependencies', '=', True), ('id', '!=', id), ('bom_id', '=', bom_id)]",
|
||||
copy=False)
|
||||
cycle_number = fields.Integer("Repetitions", compute="_compute_time_cycle")
|
||||
time_total = fields.Float('Total Duration', compute="_compute_time_cycle")
|
||||
show_time_total = fields.Boolean('Show Total Duration?', compute="_compute_time_cycle")
|
||||
cost_mode = fields.Selection([('actual', 'Actual time'), ('estimated', 'Theorical time')],
|
||||
string='Cost based on', default='actual', tracking=True,
|
||||
help="Determines the way Odoo calculates the cost of the operation:\n"
|
||||
"- Based on Actual time: the cost will be calculated based on tracked time and real employee costs.\n"
|
||||
"- Based on Estimated time: the cost will be calculated based on estimated time and costs.")
|
||||
cost = fields.Float('Cost', compute="_compute_cost")
|
||||
|
||||
@api.depends('time_mode', 'time_mode_batch')
|
||||
def _compute_time_computed_on(self):
|
||||
for operation in self:
|
||||
operation.time_computed_on = _('%i work orders') % operation.time_mode_batch if operation.time_mode != 'manual' else False
|
||||
operation.time_computed_on = _('%i work orders', operation.time_mode_batch) if operation.time_mode != 'manual' else False
|
||||
|
||||
@api.depends('time_cycle_manual', 'time_mode', 'workorder_ids')
|
||||
@api.depends('time_cycle_manual', 'time_mode', 'workorder_ids',
|
||||
'bom_id.product_id', 'bom_id.product_qty',
|
||||
'workcenter_id.time_start', 'workcenter_id.time_stop', 'workcenter_id.capacity_ids'
|
||||
)
|
||||
@api.depends_context('product', 'quantity', 'unit', 'workcenter')
|
||||
def _compute_time_cycle(self):
|
||||
manual_ops = self.filtered(lambda operation: operation.time_mode == 'manual')
|
||||
for operation in manual_ops:
|
||||
operation.time_cycle = operation.time_cycle_manual
|
||||
for operation in self - manual_ops:
|
||||
data = self.env['mrp.workorder'].search([
|
||||
('operation_id', '=', operation.id),
|
||||
('operation_id', 'in', operation.ids),
|
||||
('qty_produced', '>', 0),
|
||||
('state', '=', 'done')],
|
||||
limit=operation.time_mode_batch,
|
||||
|
|
@ -85,33 +94,74 @@ class MrpRoutingWorkcenter(models.Model):
|
|||
cycle_number = 0 # Never 0 unless infinite item['workcenter_id'].capacity
|
||||
for item in data:
|
||||
total_duration += item['duration']
|
||||
capacity = item['workcenter_id']._get_capacity(item.product_id)
|
||||
qty_produced = item.product_uom_id._compute_quantity(item['qty_produced'], item.product_id.uom_id)
|
||||
cycle_number += tools.float_round((qty_produced / capacity or 1.0), precision_digits=0, rounding_method='UP')
|
||||
(capacity, _setup, _cleanup) = item['workcenter_id']._get_capacity(item.product_id, item.product_uom_id, operation.bom_id.product_qty or 1)
|
||||
cycle_number += float_round((item['qty_produced'] / capacity), precision_digits=0, rounding_method='UP')
|
||||
if cycle_number:
|
||||
operation.time_cycle = total_duration / cycle_number
|
||||
else:
|
||||
operation.time_cycle = operation.time_cycle_manual
|
||||
|
||||
for operation in self:
|
||||
workcenter = self.env.context.get('workcenter', operation.workcenter_id)
|
||||
product = self.env.context.get('product', operation.bom_id.product_id or operation.bom_id.product_tmpl_id.product_variant_ids)
|
||||
if len(product) > 1:
|
||||
operation.cycle_number = 1
|
||||
operation.time_total = workcenter.time_start + workcenter.time_stop + operation.time_cycle_manual
|
||||
operation.show_time_total = False
|
||||
continue
|
||||
quantity = self.env.context.get('quantity', operation.bom_id.product_qty or 1)
|
||||
unit = self.env.context.get('unit', operation.bom_id.product_uom_id)
|
||||
(capacity, setup, cleanup) = workcenter._get_capacity(product, unit, operation.bom_id.product_qty or 1)
|
||||
operation.cycle_number = float_round(quantity / capacity, precision_digits=0, rounding_method="UP")
|
||||
operation.time_total = setup + cleanup + operation.cycle_number * operation.time_cycle * 100.0 / (workcenter.time_efficiency or 100.0)
|
||||
operation.show_time_total = operation.cycle_number > 1 or not float_is_zero(setup + cleanup, precision_digits=0)
|
||||
|
||||
def _compute_workorder_count(self):
|
||||
data = self.env['mrp.workorder']._read_group([
|
||||
('operation_id', 'in', self.ids),
|
||||
('state', '=', 'done')], ['operation_id'], ['operation_id'])
|
||||
count_data = dict((item['operation_id'][0], item['operation_id_count']) for item in data)
|
||||
('state', '=', 'done')], ['operation_id'], ['__count'])
|
||||
count_data = {operation.id: count for operation, count in data}
|
||||
for operation in self:
|
||||
operation.workorder_count = count_data.get(operation.id, 0)
|
||||
|
||||
@api.depends('time_total', 'workcenter_id')
|
||||
@api.depends_context('product', 'quantity', 'unit', 'workcenter')
|
||||
def _compute_cost(self):
|
||||
for operation in self:
|
||||
operation.cost = (operation.time_total / 60.0) * operation.workcenter_id.costs_hour
|
||||
|
||||
@api.constrains('blocked_by_operation_ids')
|
||||
def _check_no_cyclic_dependencies(self):
|
||||
if not self._check_m2m_recursion('blocked_by_operation_ids'):
|
||||
if self._has_cycle('blocked_by_operation_ids'):
|
||||
raise ValidationError(_("You cannot create cyclic dependency."))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
res = super().create(vals_list)
|
||||
res.bom_id._set_outdated_bom_in_productions()
|
||||
return res
|
||||
|
||||
def write(self, vals):
|
||||
self.bom_id._set_outdated_bom_in_productions()
|
||||
if 'bom_id' in vals:
|
||||
for op in self:
|
||||
op.bom_id.bom_line_ids.filtered(lambda line: line.operation_id == op).operation_id = False
|
||||
op.bom_id.byproduct_ids.filtered(lambda byproduct: byproduct.operation_id == op).operation_id = False
|
||||
op.bom_id.operation_ids.filtered(lambda operation: operation.blocked_by_operation_ids == op).blocked_by_operation_ids = False
|
||||
return super().write(vals)
|
||||
|
||||
def action_archive(self):
|
||||
res = super().action_archive()
|
||||
bom_lines = self.env['mrp.bom.line'].search([('operation_id', 'in', self.ids)])
|
||||
bom_lines.write({'operation_id': False})
|
||||
byproduct_lines = self.env['mrp.bom.byproduct'].search([('operation_id', 'in', self.ids)])
|
||||
byproduct_lines.write({'operation_id': False})
|
||||
self.bom_id._set_outdated_bom_in_productions()
|
||||
return res
|
||||
|
||||
def action_unarchive(self):
|
||||
res = super().action_unarchive()
|
||||
self.bom_id._set_outdated_bom_in_productions()
|
||||
return res
|
||||
|
||||
def copy_to_bom(self):
|
||||
|
|
@ -132,15 +182,15 @@ class MrpRoutingWorkcenter(models.Model):
|
|||
'type': 'ir.actions.act_window',
|
||||
'name': _('Select Operations to Copy'),
|
||||
'res_model': 'mrp.routing.workcenter',
|
||||
'view_mode': 'tree,form',
|
||||
'view_mode': 'list,form',
|
||||
'domain': ['|', ('bom_id', '=', False), ('bom_id.active', '=', True)],
|
||||
'context' : {
|
||||
'bom_id': self.env.context["bom_id"],
|
||||
'tree_view_ref': 'mrp.mrp_routing_workcenter_copy_to_bom_tree_view',
|
||||
'list_view_ref': 'mrp.mrp_routing_workcenter_copy_to_bom_tree_view',
|
||||
}
|
||||
}
|
||||
|
||||
def _skip_operation_line(self, product):
|
||||
def _skip_operation_line(self, product, never_attribute_values=False):
|
||||
""" Control if a operation should be processed, can be inherited to add
|
||||
custom control.
|
||||
"""
|
||||
|
|
@ -148,20 +198,14 @@ class MrpRoutingWorkcenter(models.Model):
|
|||
# skip operation line if archived
|
||||
if not self.active:
|
||||
return True
|
||||
if product._name == 'product.template':
|
||||
if not product or product._name == 'product.template':
|
||||
return False
|
||||
return not product._match_all_variant_values(self.bom_product_template_attribute_value_ids)
|
||||
|
||||
def _get_comparison_values(self):
|
||||
if not self:
|
||||
return False
|
||||
self.ensure_one()
|
||||
return tuple(self[key] for key in ('name', 'company_id', 'workcenter_id', 'time_mode', 'time_cycle_manual', 'bom_product_template_attribute_value_ids'))
|
||||
return self.env['mrp.bom']._skip_for_no_variant(product, self.bom_product_template_attribute_value_ids, never_attribute_values)
|
||||
|
||||
def write(self, values):
|
||||
if 'bom_id' in values:
|
||||
for op in self:
|
||||
op.bom_id.bom_line_ids.filtered(lambda line: line.operation_id == op).operation_id = False
|
||||
op.bom_id.byproduct_ids.filtered(lambda byproduct: byproduct.operation_id == op).operation_id = False
|
||||
op.bom_id.operation_ids.filtered(lambda operation: operation.blocked_by_operation_ids == op).blocked_by_operation_ids = False
|
||||
return super().write(values)
|
||||
def action_open_operation_form(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'mrp.routing.workcenter',
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue