改造制造订单里的生成工单
This commit is contained in:
@@ -10,18 +10,18 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'YZ',
|
'category': 'YZ',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['account', 'base', 'mrp'],
|
'depends': ['account', 'base', 'mrp', 'sale'],
|
||||||
'data': [
|
'data': [
|
||||||
'security/group_security.xml',
|
'security/group_security.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'data/product_data.xml',
|
'data/product_data.xml',
|
||||||
'data/process_data.xml',
|
'data/process_data.xml',
|
||||||
'views/product_template_view.xml',
|
|
||||||
'views/sale_order_view.xml',
|
|
||||||
'views/mrs_base_view.xml',
|
'views/mrs_base_view.xml',
|
||||||
'views/mrs_common_view.xml',
|
'views/mrs_common_view.xml',
|
||||||
'views/menu_view.xml',
|
'views/menu_view.xml',
|
||||||
'views/mrp_routing_workcenter_view.xml',
|
'views/mrp_routing_workcenter_view.xml',
|
||||||
|
'views/sale_order_view.xml',
|
||||||
|
'views/product_template_view.xml',
|
||||||
|
|
||||||
],
|
],
|
||||||
'demo': [
|
'demo': [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from. import sf_base
|
from . import sf_base
|
||||||
from. import sf_common
|
from . import sf_common
|
||||||
from . import sale_order
|
|
||||||
from . import product_template
|
from . import product_template
|
||||||
|
from . import sale_order
|
||||||
from . import process
|
from . import process
|
||||||
|
|||||||
@@ -277,6 +277,18 @@
|
|||||||
</record>
|
</record>
|
||||||
|
|
||||||
#------------------托盘------------------
|
#------------------托盘------------------
|
||||||
|
<record id="action_sf_tray" model="ir.actions.act_window">
|
||||||
|
<field name="name">托盘</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.tray</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
创建托盘吧
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="view_sf_tray_search" model="ir.ui.view">
|
<record id="view_sf_tray_search" model="ir.ui.view">
|
||||||
<field name="name">sf.tray.search</field>
|
<field name="name">sf.tray.search</field>
|
||||||
<field name="model">sf.tray</field>
|
<field name="model">sf.tray</field>
|
||||||
@@ -285,9 +297,9 @@
|
|||||||
<field name="name" string="名称" filter_domain="[('name','ilike',self)]"/>
|
<field name="name" string="名称" filter_domain="[('name','ilike',self)]"/>
|
||||||
<field name="code" string="编码" filter_domain="[('code','ilike',self)]"/>
|
<field name="code" string="编码" filter_domain="[('code','ilike',self)]"/>
|
||||||
</search>
|
</search>
|
||||||
<group string="分组">
|
<!-- <group string="分组">-->
|
||||||
<filter name="state" string="状态" domain="[]" context="{'group_by': 'state'}"/>
|
<!-- <filter name="state" string="状态" domain="[]" context="{'group_by': 'state'}"/>-->
|
||||||
</group>
|
<!-- </group>-->
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -321,17 +333,7 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_sf_tray" model="ir.actions.act_window">
|
|
||||||
<field name="name">托盘</field>
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">sf.tray</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="help" type="html">
|
|
||||||
<p class="o_view_nocontent_smiling_face">
|
|
||||||
创建托盘吧
|
|
||||||
</p>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -1 +1,2 @@
|
|||||||
from. import sf_production
|
from . import sf_production
|
||||||
|
from . import mrp_workorder
|
||||||
|
|||||||
124
sf_manufacturing_orders/models/mrp_workorder.py
Normal file
124
sf_manufacturing_orders/models/mrp_workorder.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||||
|
from odoo.osv import expression
|
||||||
|
from odoo.addons.stock.models.stock_rule import ProcurementException
|
||||||
|
from odoo.tools import float_compare, OrderedSet
|
||||||
|
|
||||||
|
|
||||||
|
class StockRule(models.Model):
|
||||||
|
_inherit = 'stock.rule'
|
||||||
|
|
||||||
|
|
||||||
|
def _run_manufacture(self, procurements):
|
||||||
|
productions_values_by_company = defaultdict(list)
|
||||||
|
errors = []
|
||||||
|
for procurement, rule in procurements:
|
||||||
|
if float_compare(procurement.product_qty, 0, precision_rounding=procurement.product_uom.rounding) <= 0:
|
||||||
|
# If procurement contains negative quantity, don't create a MO that would be for a negative value.
|
||||||
|
continue
|
||||||
|
bom = rule._get_matching_bom(procurement.product_id, procurement.company_id, procurement.values)
|
||||||
|
|
||||||
|
productions_values_by_company[procurement.company_id.id].append(rule._prepare_mo_vals(*procurement, bom))
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ProcurementException(errors)
|
||||||
|
|
||||||
|
for company_id, productions_values in productions_values_by_company.items():
|
||||||
|
# create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
|
||||||
|
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
|
||||||
|
productions_values)
|
||||||
|
self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||||
|
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||||
|
# 查出产品的加工面板并对根据面板的数量循环生成工序
|
||||||
|
|
||||||
|
productions._create_workorder()
|
||||||
|
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
|
||||||
|
(
|
||||||
|
p.move_dest_ids.procure_method != 'make_to_order' and not p.move_raw_ids and not p.workorder_ids)).action_confirm()
|
||||||
|
|
||||||
|
for production in productions:
|
||||||
|
origin_production = production.move_dest_ids and production.move_dest_ids[
|
||||||
|
0].raw_material_production_id or False
|
||||||
|
orderpoint = production.orderpoint_id
|
||||||
|
if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
|
||||||
|
production.message_post(
|
||||||
|
body=_('This production order has been created from Replenishment Report.'),
|
||||||
|
message_type='comment',
|
||||||
|
subtype_xmlid='mail.mt_note')
|
||||||
|
elif orderpoint:
|
||||||
|
production.message_post_with_view(
|
||||||
|
'mail.message_origin_link',
|
||||||
|
values={'self': production, 'origin': orderpoint},
|
||||||
|
subtype_id=self.env.ref('mail.mt_note').id)
|
||||||
|
elif origin_production:
|
||||||
|
production.message_post_with_view(
|
||||||
|
'mail.message_origin_link',
|
||||||
|
values={'self': production, 'origin': origin_production},
|
||||||
|
subtype_id=self.env.ref('mail.mt_note').id)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ResMrpWorkOrder(models.Model):
|
||||||
|
_inherit = 'mrp.workorder'
|
||||||
|
|
||||||
|
workcenter_id = fields.Many2one('mrp.workcenter', required=False)
|
||||||
|
processing_panel = fields.Char('加工面')
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, values):
|
||||||
|
res = super().create(values)
|
||||||
|
if self.env.context.get('skip_confirm'):
|
||||||
|
return res
|
||||||
|
to_confirm = res.filtered(lambda wo: wo.production_id.state in ("confirmed", "progress", "to_close"))
|
||||||
|
to_confirm = to_confirm.production_id.workorder_ids
|
||||||
|
to_confirm._action_confirm()
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _action_confirm(self):
|
||||||
|
workorders_by_production = defaultdict(lambda: self.env['mrp.workorder'])
|
||||||
|
for workorder in self:
|
||||||
|
workorders_by_production[workorder.production_id] |= workorder
|
||||||
|
|
||||||
|
for production, workorders in workorders_by_production.items():
|
||||||
|
workorders_by_bom = defaultdict(lambda: self.env['mrp.workorder'])
|
||||||
|
bom = self.env['mrp.bom']
|
||||||
|
moves = production.move_raw_ids | production.move_finished_ids
|
||||||
|
|
||||||
|
for workorder in workorders:
|
||||||
|
bom = workorder.operation_id.bom_id or workorder.production_id.bom_id
|
||||||
|
previous_workorder = workorders_by_bom[bom][-1:]
|
||||||
|
previous_workorder.next_work_order_id = workorder.id
|
||||||
|
workorders_by_bom[bom] |= workorder
|
||||||
|
|
||||||
|
moves.filtered(lambda m: m.operation_id == workorder.operation_id).write({
|
||||||
|
'workorder_id': workorder.id
|
||||||
|
})
|
||||||
|
|
||||||
|
exploded_boms, dummy = production.bom_id.explode(production.product_id, 1,
|
||||||
|
picking_type=production.bom_id.picking_type_id)
|
||||||
|
exploded_boms = {b[0]: b[1] for b in exploded_boms}
|
||||||
|
for move in moves:
|
||||||
|
if move.workorder_id:
|
||||||
|
continue
|
||||||
|
bom = move.bom_line_id.bom_id
|
||||||
|
while bom and bom not in workorders_by_bom:
|
||||||
|
bom_data = exploded_boms.get(bom, {})
|
||||||
|
bom = bom_data.get('parent_line') and bom_data['parent_line'].bom_id or False
|
||||||
|
if bom in workorders_by_bom:
|
||||||
|
move.write({
|
||||||
|
'workorder_id': workorders_by_bom[bom][-1:].id
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
move.write({
|
||||||
|
'workorder_id': workorders_by_bom[production.bom_id][-1:].id
|
||||||
|
})
|
||||||
|
|
||||||
|
for workorders in workorders_by_bom.values():
|
||||||
|
if not workorders:
|
||||||
|
continue
|
||||||
|
if workorders[0].state == 'pending':
|
||||||
|
workorders[0].state = 'ready' if workorders[0].production_availability == 'assigned' else 'waiting'
|
||||||
|
for workorder in workorders:
|
||||||
|
workorder._start_nextworkorder()
|
||||||
@@ -29,10 +29,54 @@ class MrpProduction(models.Model):
|
|||||||
_inherit = 'mrp.production'
|
_inherit = 'mrp.production'
|
||||||
_description = "制造订单"
|
_description = "制造订单"
|
||||||
|
|
||||||
|
# 重载根据工序生成工单的程序:如果产品BOM中没有工序时,
|
||||||
|
# 根据产品对应的模板类型中工序,去生成工单;
|
||||||
|
# 工单对应的工作中心,根据工序中的工作中心去匹配,
|
||||||
|
# 如果只配置了一个工作中心,则默认采用该工作中心;
|
||||||
|
# 如果有多个工作中心,
|
||||||
|
# 则根据该工作中心的工单个数进行分配(优先分配给工单个数最少的);
|
||||||
|
# CNC加工工序的选取规则:
|
||||||
|
# 如果自动报价有带过来预分配的机床,
|
||||||
|
# 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制;
|
||||||
|
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
|
||||||
|
|
||||||
|
def _create_workorder(self):
|
||||||
|
for production in self:
|
||||||
|
print(production.product_id.model_type_id)
|
||||||
|
if not production.bom_id or not production.product_id:
|
||||||
|
continue
|
||||||
|
workorders_values = []
|
||||||
|
|
||||||
|
product_qty = production.product_uom_id._compute_quantity(production.product_qty,
|
||||||
|
production.bom_id.product_uom_id)
|
||||||
|
exploded_boms, dummy = production.bom_id.explode(production.product_id,
|
||||||
|
product_qty / production.bom_id.product_qty,
|
||||||
|
picking_type=production.bom_id.picking_type_id)
|
||||||
|
|
||||||
|
for bom, bom_data in exploded_boms:
|
||||||
|
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
|
||||||
|
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
|
||||||
|
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
|
||||||
|
continue
|
||||||
|
for operation in bom.operation_ids:
|
||||||
|
if operation._skip_operation_line(bom_data['product']):
|
||||||
|
continue
|
||||||
|
workorders_values += [{
|
||||||
|
'name': operation.name,
|
||||||
|
'production_id': production.id,
|
||||||
|
'workcenter_id': operation.workcenter_id.id,
|
||||||
|
'product_uom_id': production.product_uom_id.id,
|
||||||
|
'operation_id': operation.id,
|
||||||
|
'state': 'pending',
|
||||||
|
}]
|
||||||
|
production.workorder_ids = [(5, 0)] + [(0, 0, value) for value in workorders_values]
|
||||||
|
for workorder in production.workorder_ids:
|
||||||
|
workorder.duration_expected = workorder._get_duration_expected()
|
||||||
|
|
||||||
|
|
||||||
class StockRule(models.Model):
|
class StockRule(models.Model):
|
||||||
_inherit = 'stock.rule'
|
_inherit = 'stock.rule'
|
||||||
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _run_pull(self, procurements):
|
def _run_pull(self, procurements):
|
||||||
moves_values_by_company = defaultdict(list)
|
moves_values_by_company = defaultdict(list)
|
||||||
@@ -142,6 +186,7 @@ class StockRule(models.Model):
|
|||||||
moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(moves_values)
|
moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(moves_values)
|
||||||
# Since action_confirm launch following procurement_group we should activate it.
|
# Since action_confirm launch following procurement_group we should activate it.
|
||||||
moves._action_confirm()
|
moves._action_confirm()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# @api.model
|
# @api.model
|
||||||
@@ -256,5 +301,3 @@ class StockRule(models.Model):
|
|||||||
# mo_lists.append(vals)
|
# mo_lists.append(vals)
|
||||||
#
|
#
|
||||||
# return mo_lists
|
# return mo_lists
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user