299 lines
12 KiB
Python
299 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import base64
|
|
|
|
from ast import literal_eval
|
|
from datetime import datetime
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import fields, models, _, api
|
|
from odoo.exceptions import UserError
|
|
from odoo.tools import file_open
|
|
|
|
|
|
class MrpProduction(models.Model):
|
|
_inherit = 'mrp.production'
|
|
_start_name = "date_start"
|
|
_stop_name = "date_finished"
|
|
|
|
check_ids = fields.One2many('quality.check', 'production_id', string="Checks")
|
|
|
|
employee_ids = fields.Many2many('hr.employee', string="working employees", compute='_compute_employee_ids', compute_sudo=True)
|
|
|
|
log_note = fields.Text(string="Log note")
|
|
|
|
picking_type_auto_close = fields.Boolean(related='picking_type_id.auto_close_production')
|
|
|
|
state_color = fields.Integer("State Color", compute='_compute_state_color')
|
|
|
|
@api.depends('state')
|
|
def _compute_state_color(self):
|
|
colors = {'draft': 0, 'confirmed': 4, 'progress': 3, 'to_close': 5, 'done': 10, 'cancel': 1}
|
|
for production in self:
|
|
production.state_color = colors[production.state]
|
|
|
|
def write(self, vals):
|
|
res = super().write(vals)
|
|
if 'lot_producing_ids' in vals and self._has_workorders():
|
|
self.workorder_ids.check_ids.filtered(lambda c: c.test_type_id.technical_name == 'register_production').write({'lot_ids': self.lot_producing_ids.ids})
|
|
return res
|
|
|
|
def action_add_byproduct(self):
|
|
self.ensure_one()
|
|
action = self.env['stock.move'].with_context(order_id=self.id).action_add_from_catalog_byproduct()
|
|
action['target'] = 'new'
|
|
action['context']['dialog_size'] = 'extra-large'
|
|
return action
|
|
|
|
def action_add_component(self):
|
|
self.ensure_one()
|
|
action = self.env['stock.move'].with_context(order_id=self.id).action_add_from_catalog_raw()
|
|
action['target'] = 'new'
|
|
action['context']['dialog_size'] = 'extra-large'
|
|
return action
|
|
|
|
def action_add_workorder(self):
|
|
default_blocking_wo_id = self.workorder_ids[-1].id if self.workorder_ids else False
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'mrp_production.additional.workorder',
|
|
'views': [[self.env.ref('mrp_workorder.view_mrp_production_additional_workorder_wizard').id, 'form']],
|
|
'name': _('Add Work Order'),
|
|
'target': 'new',
|
|
'context': {
|
|
'default_production_id': self.id,
|
|
'default_blocked_by_workorder_id': default_blocking_wo_id,
|
|
}
|
|
}
|
|
|
|
def action_log_note(self):
|
|
self.ensure_one()
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'res_id': self.id,
|
|
'res_model': 'mrp.production',
|
|
'views': [[self.env.ref('mrp_workorder.mrp_production_view_form_log_note').id, 'form']],
|
|
'name': _('Add log note'),
|
|
'target': 'new',
|
|
'context': {
|
|
'default_production_id': self.id,
|
|
}
|
|
}
|
|
|
|
def action_open_shop_floor(self):
|
|
""" Opens shop floor on Overview workcenter with a filter for current MO """
|
|
self.ensure_one()
|
|
action = self.env['ir.actions.actions']._for_xml_id('mrp_workorder.action_mrp_display')
|
|
action['context'] = literal_eval(action['context']) | {
|
|
'search_default_name': self.name,
|
|
'workcenter_id': 0, # Sets active WC to Overview
|
|
'search_default_blocked': True,
|
|
'shouldHideNewWorkcenterButton': True,
|
|
'shouldKeepMoFilter': True,
|
|
}
|
|
del action['context']['search_default_filter_ready']
|
|
return action
|
|
|
|
@api.depends('workorder_ids', 'workorder_ids.employee_ids')
|
|
def _compute_employee_ids(self):
|
|
for record in self:
|
|
record.employee_ids = record.workorder_ids.employee_ids
|
|
|
|
def pre_button_mark_done(self):
|
|
res = super().pre_button_mark_done()
|
|
if res is True:
|
|
self.workorder_ids.verify_quality_checks()
|
|
return res
|
|
|
|
def _update_catalog_line_quantity(self, line, quantity, **kwargs):
|
|
super()._update_catalog_line_quantity(line, quantity)
|
|
if kwargs.get('from_shop_floor'):
|
|
line.quantity = quantity
|
|
line.manual_consumption = True
|
|
|
|
def _get_new_catalog_line_values(self, product_id, quantity, **kwargs):
|
|
values = super()._get_new_catalog_line_values(product_id, quantity)
|
|
if kwargs.get('from_shop_floor'):
|
|
values.update({
|
|
'additional': True,
|
|
'manual_consumption': True,
|
|
})
|
|
if workorder_id := kwargs.get('workorder_id'):
|
|
workorder = self.env['mrp.workorder'].browse(workorder_id)
|
|
values['workorder_id'] = workorder_id
|
|
values['operation_id'] = workorder.operation_id.id
|
|
return values
|
|
|
|
def can_load_samples(self):
|
|
return self.sudo().env['mrp.production'].search_count([]) == 0
|
|
|
|
def action_load_samples(self):
|
|
if not self.can_load_samples():
|
|
raise UserError(_('Unable to load samples when you already have existing manufacturing orders'))
|
|
|
|
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
|
|
|
|
def read_file_datas(path):
|
|
with file_open(path, "rb") as f:
|
|
return base64.b64encode(f.read())
|
|
|
|
products = [{'xml_id': xmlid, 'noupdate': True, 'values': {
|
|
'name': name,
|
|
'is_storable': True,
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
'description': desc,
|
|
'default_code': code,
|
|
'image_1920': read_file_datas(img),
|
|
}} for (xmlid, name, desc, code, img) in (
|
|
(
|
|
'mrp.product_product_computer_desk',
|
|
'Table',
|
|
'Solid wood table',
|
|
'SAMPLE_TABLE',
|
|
'mrp/static/img/table.png',
|
|
),
|
|
(
|
|
'mrp.product_product_computer_desk_head',
|
|
'Table Top',
|
|
'Solid wood is a durable natural material.',
|
|
'SAMPLE_TABLE_TOP',
|
|
'mrp/static/img/table_top.png'
|
|
),
|
|
(
|
|
'mrp.product_product_computer_desk_leg',
|
|
'Table Leg',
|
|
'18″ x 2½″ Square Leg',
|
|
'SAMPLE_TABLE_LEG',
|
|
'mrp/static/img/table_leg.png'
|
|
),
|
|
)]
|
|
table, tabletop, tableleg = self.env['product.product']._load_records(products, True)
|
|
|
|
quants = [{'xml_id': xmlid, 'noupdate': True, 'values': {
|
|
'product_id': prod,
|
|
'inventory_quantity': qty,
|
|
'location_id': warehouse.lot_stock_id.id,
|
|
}} for (xmlid, prod, qty) in (
|
|
('mrp.mrp_inventory_1', tabletop.id, 1),
|
|
('mrp.mrp_inventory_2', tableleg.id, 4),
|
|
)]
|
|
self.env['stock.quant']._load_records(quants, True)._apply_inventory()
|
|
|
|
bom = self.env['mrp.bom']._load_records([{'xml_id': 'mrp.mrp_bom_desk', 'noupdate': True, 'values': {
|
|
'product_tmpl_id': table.product_tmpl_id.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
'sequence': 3,
|
|
'consumption': 'flexible',
|
|
'days_to_prepare_mo': 3,
|
|
}}], True)
|
|
|
|
bom_lines = [{'xml_id': xmlid, 'noupdate': True, 'values': {
|
|
'product_id': prod,
|
|
'product_qty': qty,
|
|
'product_uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
'sequence': seq,
|
|
'bom_id': bom.id,
|
|
}} for (xmlid, prod, qty, seq) in (
|
|
('mrp.mrp_bom_desk_line_1', tabletop.id, 1, 1),
|
|
('mrp.mrp_bom_desk_line_2', tableleg.id, 4, 2),
|
|
)]
|
|
bom_lines = self.env['mrp.bom.line']._load_records(bom_lines, True)
|
|
|
|
MO = self.env['mrp.production']._load_records([{'xml_id': 'mrp.mrp_production_3', 'noupdate': True, 'values': {
|
|
'product_id': table.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
'product_qty': 1,
|
|
'date_start': datetime.today() + relativedelta(days=1),
|
|
'bom_id': bom.id,
|
|
}}], True)
|
|
self.env['stock.rule'].run_scheduler()
|
|
MO.action_confirm()
|
|
|
|
if self.env.user.has_group('mrp.group_mrp_routings'):
|
|
WC = self.env['mrp.workcenter']._load_records([{'xml_id': 'mrp.mrp_workcenter_3', 'noupdate': True, 'values': {
|
|
'name': 'Assembly line 1',
|
|
'resource_calendar_id': self.env.ref('resource.resource_calendar_std').id,
|
|
}}], True)
|
|
|
|
routing = self.env['mrp.routing.workcenter']._load_records([{
|
|
'xml_id': 'mrp.mrp_routing_workcenter_5', 'noupdate': True, 'values': {
|
|
'bom_id': bom.id,
|
|
'workcenter_id': WC.id,
|
|
'time_cycle': 120,
|
|
'sequence': 10,
|
|
'name': 'Assembly',
|
|
}
|
|
}], True)
|
|
bom_lines.operation_id = routing
|
|
|
|
quality_points = [{'xml_id': xmlid, 'noupdate': True, 'values': {
|
|
'product_ids': [table.id],
|
|
'picking_type_ids': [warehouse.manu_type_id.id],
|
|
'operation_id': routing.id,
|
|
'test_type_id': self.env.ref(testtype).id,
|
|
'note': note,
|
|
'title': title,
|
|
'sequence': seq,
|
|
'component_id': comp,
|
|
'worksheet_document': worksheet_doc and read_file_datas(worksheet_doc),
|
|
}} for (xmlid, testtype, note, title, seq, comp, worksheet_doc) in (
|
|
(
|
|
'mrp_workorder.quality_point_register_serial_production',
|
|
'mrp_workorder.test_type_register_production',
|
|
'Register the produced quantity.',
|
|
'Register production',
|
|
5,
|
|
None,
|
|
None,
|
|
),
|
|
(
|
|
'mrp_workorder.quality_point_component_registration',
|
|
'mrp_workorder.test_type_register_consumed_materials',
|
|
'Please register consumption of the table top.',
|
|
'Component Registration: Table Head',
|
|
20,
|
|
tabletop.id,
|
|
'mrp/static/img/cutting-worksheet-1.pdf',
|
|
),
|
|
(
|
|
'mrp_workorder.quality_point_instructions',
|
|
'quality.test_type_instructions',
|
|
'Please ensure you are using the new SRX679 screwdriver.',
|
|
'Choice of screwdriver',
|
|
30,
|
|
None,
|
|
'mrp/static/img/cutting-worksheet-1.pdf',
|
|
),
|
|
(
|
|
'mrp_workorder.quality_point_component_registration_2',
|
|
'mrp_workorder.test_type_register_consumed_materials',
|
|
'Please register consumption of the table legs.',
|
|
'Component Registration: Table Legs',
|
|
70,
|
|
tableleg.id,
|
|
'mrp/static/img/cutting-worksheet-4.pdf',
|
|
),
|
|
(
|
|
'mrp_workorder.quality_point_register_production',
|
|
'quality.test_type_instructions',
|
|
'Please attach the legs to the table as shown below.',
|
|
'Table Legs',
|
|
60,
|
|
None,
|
|
'mrp/static/img/cutting-worksheet-4.pdf',
|
|
),
|
|
(
|
|
'mrp_workorder.quality_point_print_labels',
|
|
'mrp_workorder.test_type_print_label',
|
|
None,
|
|
'Print Labels',
|
|
90,
|
|
None,
|
|
None,
|
|
),
|
|
)]
|
|
self.env['quality.point']._load_records(quality_points, True)
|
|
|
|
MO.action_update_bom()
|