合并企业版代码(未测试,先提交到测试分支)
This commit is contained in:
5
mrp_workorder/wizard/__init__.py
Normal file
5
mrp_workorder/wizard/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from . import change_production_qty
|
||||
from . import additional_product
|
||||
from . import propose_change
|
||||
86
mrp_workorder/wizard/additional_product.py
Normal file
86
mrp_workorder/wizard/additional_product.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, api, fields
|
||||
|
||||
|
||||
class MrpWorkorderAdditionalProduct(models.TransientModel):
|
||||
_name = "mrp_workorder.additional.product"
|
||||
_description = "Additional Product"
|
||||
|
||||
product_id = fields.Many2one(
|
||||
'product.product',
|
||||
'Product',
|
||||
required=True,
|
||||
domain="[('company_id', 'in', (company_id, False)), ('type', '!=', 'service')]")
|
||||
product_tracking = fields.Selection(related='product_id.tracking')
|
||||
product_qty = fields.Float('Quantity', default=1, required=True)
|
||||
product_uom_id = fields.Many2one('uom.uom', domain="[('category_id', '=', product_uom_category_id)]")
|
||||
product_uom_category_id = fields.Many2one(related='product_id.uom_id.category_id')
|
||||
type = fields.Selection([
|
||||
('component', 'Component'),
|
||||
('byproduct', 'By-Product')])
|
||||
workorder_id = fields.Many2one(
|
||||
'mrp.workorder', required=True,
|
||||
default=lambda self: self.env.context.get('active_id', None),
|
||||
)
|
||||
company_id = fields.Many2one(related='workorder_id.company_id')
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_id(self):
|
||||
if self.product_id:
|
||||
self.product_uom_id = self.product_id.uom_id
|
||||
if self.product_tracking == 'serial':
|
||||
self.product_qty = 1
|
||||
|
||||
def add_product(self):
|
||||
"""Create workorder line for the additional product."""
|
||||
wo = self.workorder_id
|
||||
if self.type == 'component':
|
||||
test_type = self.env.ref('mrp_workorder.test_type_register_consumed_materials')
|
||||
move = self.env['stock.move'].create(
|
||||
wo.production_id._get_move_raw_values(
|
||||
self.product_id,
|
||||
self.product_qty,
|
||||
self.product_id.uom_id,
|
||||
operation_id=wo.operation_id.id,
|
||||
)
|
||||
)
|
||||
else:
|
||||
test_type = self.env.ref('mrp_workorder.test_type_register_byproducts')
|
||||
move = self.env['stock.move'].create(
|
||||
wo.production_id._get_move_finished_values(
|
||||
self.product_id.id,
|
||||
self.product_qty,
|
||||
self.product_id.uom_id.id,
|
||||
operation_id=wo.operation_id.id,
|
||||
)
|
||||
)
|
||||
|
||||
move = move._action_confirm()
|
||||
check = {
|
||||
'workorder_id': wo.id,
|
||||
'component_id': self.product_id.id,
|
||||
'product_id': wo.product_id.id,
|
||||
'company_id': self.company_id.id,
|
||||
'team_id': self.env['quality.alert.team'].search([], limit=1).id,
|
||||
'finished_product_sequence': wo.qty_produced,
|
||||
'test_type_id': test_type.id,
|
||||
'qty_done': self.product_qty,
|
||||
'move_id': move.id,
|
||||
}
|
||||
additional_check = self.env['quality.check'].create(check)
|
||||
|
||||
# Insert the quality check in the chain. The process is slightly different
|
||||
# if we are between two quality checks or at the summary step.
|
||||
if wo.current_quality_check_id:
|
||||
additional_check._insert_in_chain('before', wo.current_quality_check_id)
|
||||
wo._change_quality_check(position='previous')
|
||||
else:
|
||||
last_check = wo.check_ids.filtered(
|
||||
lambda c: not c.next_check_id and
|
||||
c.finished_product_sequence == wo.qty_produced and
|
||||
c != additional_check
|
||||
)
|
||||
additional_check._insert_in_chain('after', last_check)
|
||||
wo._change_quality_check(position='last')
|
||||
30
mrp_workorder/wizard/additional_product_views.xml
Normal file
30
mrp_workorder/wizard/additional_product_views.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_mrp_workorder_additional_product_wizard" model="ir.ui.view">
|
||||
<field name="name">MRP Additional Product</field>
|
||||
<field name="model">mrp_workorder.additional.product</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Add product">
|
||||
<group>
|
||||
<group>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="product_tracking" invisible="1"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="product_uom_category_id" invisible="1"/>
|
||||
<field name="product_id"/>
|
||||
<label for="product_qty" string="Quantity" />
|
||||
<div class="o_row">
|
||||
<field name="product_qty" attrs="{'readonly': [('product_tracking', '=', 'serial')]}"/>
|
||||
<field name="product_uom_id" groups="uom.group_uom" attrs="{'readonly': [('product_tracking', '=', 'serial')]}"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="add_product" type="object" string="Add Component" attrs="{'invisible': [('type', '=', 'byproduct')]}" class="btn-primary" data-hotkey="q"/>
|
||||
<button name="add_product" type="object" string="Add By-Product" attrs="{'invisible': [('type', '=', 'component')]}" class="btn-primary" data-hotkey="q"/>
|
||||
<button string="Discard" class="btn-default btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
22
mrp_workorder/wizard/change_production_qty.py
Normal file
22
mrp_workorder/wizard/change_production_qty.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
class ChangeProductionQty(models.TransientModel):
|
||||
_inherit = "change.production.qty"
|
||||
|
||||
def change_prod_qty(self):
|
||||
super(ChangeProductionQty, self).change_prod_qty()
|
||||
# if there are quality checks, we should create new checks/update the quantities, etc.
|
||||
# if we want to make this feature work, a task should be done to deal with all the possible cases:
|
||||
# types of quality check, quantity processed, etc. In the meantime, we block this feature.
|
||||
for wizard in self:
|
||||
done_checks = self.env['quality.check'].search([
|
||||
'&', ('control_date', '!=', False),
|
||||
'|', ('production_id', '=', wizard.mo_id.id), ('workorder_id', 'in', wizard.mo_id.workorder_ids.ids),
|
||||
])
|
||||
if done_checks:
|
||||
raise ValidationError(_("You cannot update the quantity to do of an ongoing "
|
||||
"manufacturing order for which quality checks have been performed."))
|
||||
99
mrp_workorder/wizard/propose_change.py
Normal file
99
mrp_workorder/wizard/propose_change.py
Normal file
@@ -0,0 +1,99 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import SUPERUSER_ID, api, fields, models, _
|
||||
|
||||
|
||||
class ProposeChange(models.TransientModel):
|
||||
_name = 'propose.change'
|
||||
_description = 'Propose a change in the production'
|
||||
|
||||
workorder_id = fields.Many2one(
|
||||
'mrp.workorder', 'Workorder', required=True, ondelete='cascade')
|
||||
title = fields.Char('title')
|
||||
step_id = fields.Many2one('quality.check', 'Step to change')
|
||||
note = fields.Html('New Instruction')
|
||||
comment = fields.Char('Comment')
|
||||
picture = fields.Binary('Picture')
|
||||
change_type = fields.Selection([
|
||||
('update_step', 'Update Current Step'),
|
||||
('remove_step', 'Remove Current Step'),
|
||||
('set_picture', 'Set Picture')], 'Type of Change')
|
||||
|
||||
def default_get(self, fields_list):
|
||||
defaults = super().default_get(fields_list)
|
||||
if 'step_id' in defaults:
|
||||
step = self.env['quality.check'].browse(defaults.get('step_id'))
|
||||
defaults['title'] = step.title
|
||||
return defaults
|
||||
|
||||
def process(self):
|
||||
for wizard in self:
|
||||
if wizard.title != wizard.step_id.title:
|
||||
wizard.step_id.title = wizard.title
|
||||
if wizard.change_type == 'update_step':
|
||||
wizard._do_update_step()
|
||||
elif wizard.change_type == 'remove_step':
|
||||
wizard._do_remove_step()
|
||||
elif wizard.change_type == 'set_picture':
|
||||
wizard._do_set_picture()
|
||||
|
||||
def _workorder_name(self):
|
||||
return self.env.user.name
|
||||
|
||||
def _do_update_step(self, notify_bom=True):
|
||||
self.ensure_one()
|
||||
self.step_id.note = self.note
|
||||
if notify_bom and self.workorder_id.production_id.bom_id:
|
||||
body = Markup(_("<b>New Instruction suggested by %s</b><br/>%s<br/><b>Reason: %s</b>")) % (self._workorder_name(), self.note, self.comment)
|
||||
self.env['mail.activity'].sudo().create({
|
||||
'res_model_id': self.env.ref('mrp.model_mrp_bom').id,
|
||||
'res_id': self.workorder_id.production_id.bom_id.id,
|
||||
'user_id': self.workorder_id.product_id.responsible_id.id or SUPERUSER_ID,
|
||||
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
|
||||
'summary': _('BoM feedback %s (%s)', self.step_id.title, self.workorder_id.production_id.name),
|
||||
'note': body,
|
||||
})
|
||||
|
||||
def _do_remove_step(self, notify_bom=True):
|
||||
self.ensure_one()
|
||||
if not self.step_id.point_id and not(self.step_id.test_type.startswith('register_')):
|
||||
# remove additionmal step
|
||||
self.step_id.workorder_id._change_quality_check('next')
|
||||
self.step_id.unlink()
|
||||
|
||||
self.step_id.is_deleted = True
|
||||
bom = self.step_id.workorder_id.production_id.bom_id
|
||||
if notify_bom and bom:
|
||||
body = Markup(_("<b>%s suggests to delete this instruction</b>")) % self._workorder_name()
|
||||
self.env['mail.activity'].sudo().create({
|
||||
'res_model_id': self.env.ref('mrp.model_mrp_bom').id,
|
||||
'res_id': bom.id,
|
||||
'user_id': self.workorder_id.product_id.responsible_id.id or SUPERUSER_ID,
|
||||
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
|
||||
'summary': _('BoM feedback %s (%s)', self.step_id.title, self.workorder_id.production_id.name),
|
||||
'note': body,
|
||||
})
|
||||
|
||||
@api.model
|
||||
def image_url(self, record, field):
|
||||
""" Returns a local url that points to the image field of a given browse record. """
|
||||
return '/web/image/%s/%s/%s' % (record._name, record.id, field)
|
||||
|
||||
def _do_set_picture(self, notify_bom):
|
||||
self.ensure_one()
|
||||
self.step_id.worksheet_document = self.picture
|
||||
bom = self.step_id.workorder_id.production_id.bom_id
|
||||
if notify_bom and bom:
|
||||
body = Markup(_("<b>%s suggests to use this document as instruction</b><br/><img style='max-width: 75%%' class='img-fluid' src=%s/>"))\
|
||||
% (self._workorder_name(), self.image_url(self, 'picture'))
|
||||
self.env['mail.activity'].sudo().create({
|
||||
'res_model_id': self.env.ref('mrp.model_mrp_bom').id,
|
||||
'res_id': bom.id,
|
||||
'user_id': self.workorder_id.product_id.responsible_id.id or SUPERUSER_ID,
|
||||
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
|
||||
'summary': _('BoM feedback %s (%s)', self.step_id.title, self.workorder_id.production_id.name),
|
||||
'note': body,
|
||||
})
|
||||
25
mrp_workorder/wizard/propose_change_views.xml
Normal file
25
mrp_workorder/wizard/propose_change_views.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_propose_change_wizard" model="ir.ui.view">
|
||||
<field name="name">workorder_propose_change</field>
|
||||
<field name="model">propose.change</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Propose Change">
|
||||
<group>
|
||||
<group>
|
||||
<field name="change_type" invisible="1"/>
|
||||
<field name="step_id" invisible="1"/>
|
||||
<field name="title" attrs="{'invisible': [('change_type', '!=', 'update_step')]}"/>
|
||||
<field name="note" attrs="{'invisible': [('change_type', '!=', 'update_step')]}"/>
|
||||
<field name="comment" attrs="{'invisible': [('change_type', '=', 'set_picture')]}"/>
|
||||
<field name="picture" attrs="{'invisible': [('change_type', '!=', 'set_picture')]}" widget="image"/>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="process" class="btn btn-primary" type="object" string="Propose Change"/>
|
||||
<button string="Discard" class="btn-default btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user