diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 34185d4e..27fdb37c 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -231,13 +231,13 @@ class ResWorkcenter(models.Model): default_capacity = round( self.production_line_hour_capacity * date_planned_working_hours, 2) _logger.info('排程日期:%s,计划数量:%s,日产能:%s,日工时:%s' % ( - date_planned, sum_qty, default_capacity, date_planned_working_hours)) + date_planned, sum_qty, default_capacity, date_planned_working_hours)) if sum_qty >= default_capacity: return False return True # 处理排程是否超过小时产能 - def deal_available_single_machine_capacity(self, date_planned): + def deal_available_single_machine_capacity(self, date_planned, count): date_planned_start = date_planned.strftime('%Y-%m-%d %H:00:00') date_planned_end = date_planned + timedelta(hours=1) @@ -249,7 +249,11 @@ class ResWorkcenter(models.Model): if plan_ids: sum_qty = sum([p.product_qty for p in plan_ids]) - if sum_qty >= self.production_line_hour_capacity: + production_line_hour_capacity = self.production_line_hour_capacity + if sum_qty >= production_line_hour_capacity: + message = '当前计划开始时间不能预约排程,超过生产线小时产能(%d件)%d件' % ( + production_line_hour_capacity, count) + raise UserError(message) return False return True diff --git a/sf_message/__manifest__.py b/sf_message/__manifest__.py index e767ec2d..416b2641 100644 --- a/sf_message/__manifest__.py +++ b/sf_message/__manifest__.py @@ -11,7 +11,7 @@ """, 'category': 'sf', 'website': 'https://www.sf.jikimo.com', - 'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock'], + 'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'mrp'], 'data': [ 'data/bussiness_node.xml', # 'data/cron_data.xml', diff --git a/sf_message/data/bussiness_node.xml b/sf_message/data/bussiness_node.xml index f4a6ddf4..66b24aa7 100644 --- a/sf_message/data/bussiness_node.xml +++ b/sf_message/data/bussiness_node.xml @@ -48,6 +48,15 @@ 工单已下发通知 mrp.workorder + + + 生产完工入库提醒 + mrp.production + + + 订单发货提醒 + stock.picking + diff --git a/sf_message/data/template_data.xml b/sf_message/data/template_data.xml index b08a3fdd..f4e342c4 100644 --- a/sf_message/data/template_data.xml +++ b/sf_message/data/template_data.xml @@ -106,5 +106,31 @@ 单号:组装任务单[{{name}}]({{tool_assembly_special_url}}) 事项:{{use_tool_time}}前完成组装 + + + + 生产完工入库提醒 + + mrp.production + + markdown + normal + ### 生产完工入库提醒: +单号:生产入库单[{{name}}]({{request_url}}) +事项:销售订单{{sale_order_name}}已全部产出,请入库处理 + + + + + 订单发货提醒 + + stock.picking + + markdown + normal + ### 订单发货提醒: +单号:发料出库单[{{name}}]({{request_url}}) +事项:销售订单{{sale_order_name}}已全部产出并出库,请及时发货 + \ No newline at end of file diff --git a/sf_message/models/__init__.py b/sf_message/models/__init__.py index 77a7ce17..2ff5461b 100644 --- a/sf_message/models/__init__.py +++ b/sf_message/models/__init__.py @@ -7,3 +7,4 @@ from . import sf_message_functional_tool_assembly from . import sf_message_purchase from . import sf_message_workorder from . import sf_message_functional_tool_dismantle +from . import sf_message_mrp_production diff --git a/sf_message/models/sf_message_mrp_production.py b/sf_message/models/sf_message_mrp_production.py new file mode 100644 index 00000000..44a100f3 --- /dev/null +++ b/sf_message/models/sf_message_mrp_production.py @@ -0,0 +1,55 @@ +import re +from odoo import models, fields, api, _ +from urllib.parse import urlencode + + +class SFMessageMrpProduction(models.Model): + _name = 'mrp.production' + _description = "制造订单" + _inherit = ['mrp.production', 'jikimo.message.dispatch'] + + @api.depends( + 'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', + 'workorder_ids.state', 'product_qty', 'qty_producing') + def _compute_state(self): + super(SFMessageMrpProduction, self)._compute_state() + for record in self: + if record.state in ['scrap', 'done']: + # 查询制造订单下的所有未完成的生产订单 + mrp_production = record.env['mrp.production'].search( + [('origin', '=', record.origin), ('state', 'not in', ['scrap', 'done'])]) + if not mrp_production: + mrp_production_queue = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)]) + if not mrp_production_queue: + record.add_queue('生产完工入库提醒') + + # 获取发送消息内容 + def _get_message(self, message_queue_ids): + contents = [] + for message_queue_id in message_queue_ids: + if message_queue_id.message_template_id.name == '生产完工入库提醒': + content = message_queue_id.message_template_id.content + mrp_production = self.env['mrp.production'].search([('id', '=', int(message_queue_id.res_id))]) + if mrp_production and len(mrp_production) > 0: + stock_picking_sfp = self.env['stock.picking'].search( + [('origin', '=', mrp_production.origin), ('picking_type_id.sequence_code', '=', 'SFP'), + ('state', '=', 'assigned')], limit=1) + if stock_picking_sfp: + url = self.request_url() + content = content.replace('{{name}}', stock_picking_sfp.name).replace( + '{{sale_order_name}}', mrp_production.origin).replace('{{request_url}}', url) + contents.append(content) + return contents + + def request_url(self): + url = self.env['ir.config_parameter'].get_param('web.base.url') + action_id = self.env.ref('mrp.mrp_production_action').id + menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id + # 查询参数 + params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.production', + 'view_type': 'kanban'} + # 拼接查询参数 + query_string = urlencode(params) + # 拼接URL + full_url = url + "/web#" + query_string + return full_url diff --git a/sf_message/models/sf_message_stock_picking.py b/sf_message/models/sf_message_stock_picking.py index c0dac130..b673dbe0 100644 --- a/sf_message/models/sf_message_stock_picking.py +++ b/sf_message/models/sf_message_stock_picking.py @@ -23,6 +23,27 @@ class SFMessageStockPicking(models.Model): if record.state == 'assigned' and record.check_in == 'PC': record.add_queue('坯料发料提醒') + if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done': + stock_picking_sfp = record.env['stock.picking'].search( + [('origin', '=', record.origin), ('state', '!=', 'done'), + ('picking_type_id.sequence_code', '=', 'SFP')]) + if not stock_picking_sfp: + stock_picking_send = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)]) + if not stock_picking_send: + record.add_queue('订单发货提醒') + + def deal_stock_picking_sfp(self, message_queue_id): # 处理订单发货提醒 + content = message_queue_id.message_template_id.content + stock_picking = self.env['stock.picking'].search([('id', '=', int(message_queue_id.res_id))]) + stock_picking_out = self.env['stock.picking'].search( + [('origin', '=', stock_picking.origin), ('state', '=', 'assigned'), + ('picking_type_id.sequence_code', '=', 'OUT')]) + if stock_picking_out and len(stock_picking_out) > 0: + url = self.request_url() + content = content.replace('{{name}}', stock_picking_out.name).replace( + '{{sale_order_name}}', stock_picking_out.origin).replace('{{request_url}}', url) + return content + def _get_message(self, message_queue_ids): contents = [] product_id = [] @@ -46,17 +67,18 @@ class SFMessageStockPicking(models.Model): '{{number}}', str(i)).replace('{{request_url}}', url) product_id.append(mrp_production_info.product_id.id) contents.append(content) - return contents - else: - res = super(SFMessageStockPicking, self)._get_message(message_queue_id) - return res - def get_special_url(self,id,tmplate_name,special_name,model_id): + elif message_queue_id.message_template_id.name == '订单发货提醒': + content = self.deal_stock_picking_sfp(message_queue_id) + contents.append(content) + return contents + + def get_special_url(self, id, tmplate_name, special_name, model_id): menu_id = 0 action_id = 0 - if tmplate_name=='调拨入库' and special_name== 'transfer_inventory_special_url': + if tmplate_name == '调拨入库' and special_name == 'transfer_inventory_special_url': menu_id = self.env.ref('stock.menu_stock_root').id action_id = self.env.ref('stock.action_picking_tree_ready').id - return super(SFMessageStockPicking, self).get_url(id, menu_id, action_id,model_id) + return super(SFMessageStockPicking, self).get_url(id, menu_id, action_id, model_id) else: return super(SFMessageStockPicking, self).get_special_url(id, tmplate_name, special_name, model_id) diff --git a/sf_plan/models/custom_plan.py b/sf_plan/models/custom_plan.py index fb8176fe..60587dec 100644 --- a/sf_plan/models/custom_plan.py +++ b/sf_plan/models/custom_plan.py @@ -75,7 +75,7 @@ class sf_production_plan(models.Model): if self.date_planned_start: self.date_planned_finished = self.date_planned_start + timedelta(hours=1) - #处理计划状态非待排程,计划结束时间为空的数据处理 + # 处理计划状态非待排程,计划结束时间为空的数据处理 def deal_no_date_planned_finished(self): plans = self.env['sf.production.plan'].search( [('date_planned_finished', '=', False), ('state', 'in', ['processing', 'done', 'finished'])]) @@ -90,6 +90,7 @@ class sf_production_plan(models.Model): for item in plans: if item.date_planned_start: item.order_deadline = item.date_planned_start + timedelta(days=7) + @api.model def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None): @@ -218,7 +219,7 @@ class sf_production_plan(models.Model): return num - def do_production_schedule(self): + def do_production_schedule(self, count=1): """ 排程方法 """ @@ -226,7 +227,7 @@ class sf_production_plan(models.Model): if not record.production_line_id: raise ValidationError("未选择生产线") else: - is_schedule = self.deal_processing_schedule(record.date_planned_start) + is_schedule = self.deal_processing_schedule(record.date_planned_start, count) if not is_schedule: raise ValidationError("排程失败") workorder_id_list = record.production_id.workorder_ids.ids @@ -281,7 +282,7 @@ class sf_production_plan(models.Model): } # 处理是否可排程 - def deal_processing_schedule(self, date_planned_start): + def deal_processing_schedule(self, date_planned_start, count): for record in self: workcenter_ids = record.production_line_id.mrp_workcenter_ids if not workcenter_ids: @@ -296,7 +297,7 @@ class sf_production_plan(models.Model): raise UserError('当前计划开始时间不能预约排程,请在工作时间内排程') if not production_lines.deal_available_default_capacity(date_planned_start): # 判断生产线是否可排程 raise UserError('当前计划开始时间不能预约排程,生产线今日没有可排程的资源') - if not production_lines.deal_available_single_machine_capacity(date_planned_start): # 判断生产线是否可排程 + if not production_lines.deal_available_single_machine_capacity(date_planned_start, count): # 判断生产线是否可排程 raise UserError('当前计划开始时间不能预约排程,生产线该时间段没有可排程的资源') return True diff --git a/sf_plan/wizard/action_plan_some.py b/sf_plan/wizard/action_plan_some.py index 52b9a249..23545516 100644 --- a/sf_plan/wizard/action_plan_some.py +++ b/sf_plan/wizard/action_plan_some.py @@ -33,7 +33,9 @@ class Action_Plan_All_Wizard(models.TransientModel): # 使用传递过来的计划ID temp_plan_ids = self.plan_ids # 在这里添加您的逻辑来处理这些ID + count = len(temp_plan_ids) + 1 for plan in temp_plan_ids: + count = count - 1 # 处理每个计划 # 比如更新计划状态、分配资源等 # 示例:plan.state = 'scheduled' @@ -42,7 +44,7 @@ class Action_Plan_All_Wizard(models.TransientModel): plan_obj = self.env['sf.production.plan'].browse(plan.id) plan_obj.production_line_id = self.production_line_id.id plan.date_planned_start = self.date_planned_start - plan_obj.do_production_schedule() + plan_obj.do_production_schedule(count) # plan_obj.state = 'done' print('处理计划:', plan.id, '完成')