@@ -85,7 +85,7 @@
-
+
-->
+ options="{'horizontal': true}"/>
-
-
+
+
@@ -237,7 +237,7 @@
-
+
@@ -252,21 +252,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -979,31 +979,58 @@
-
-
+
+
+
+
+
+
+
+
+
+
+ days
+
+
+
+
+ hours
+
+
+
+
+
+
+
+
+
+
+
+
+ days
+
+
+
+
+ hours
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
- days
-
-
-
-
-
- hours
-
-
-
-
1
@@ -1189,7 +1216,7 @@
-
+
diff --git a/sf_maintenance/wizard/__init__.py b/sf_maintenance/wizard/__init__.py
new file mode 100644
index 00000000..d8d470dc
--- /dev/null
+++ b/sf_maintenance/wizard/__init__.py
@@ -0,0 +1 @@
+from . import maintenance_request_wizard
\ No newline at end of file
diff --git a/sf_maintenance/wizard/maintenance_request_wizard.py b/sf_maintenance/wizard/maintenance_request_wizard.py
new file mode 100644
index 00000000..e0f8d8fa
--- /dev/null
+++ b/sf_maintenance/wizard/maintenance_request_wizard.py
@@ -0,0 +1,26 @@
+from odoo import fields, models
+
+
+class MaintenanceRequestWizard(models.TransientModel):
+ _name = 'maintenance.request.wizard'
+ _description = '维保二次确认弹窗'
+
+ name = fields.Char('')
+
+ def submit(self):
+ context = self.env.context
+ equipment_id = self.env['maintenance.equipment'].sudo().search([('id', '=', context['equipment_id'])])
+ request_ids = self.env['maintenance.request'].search([('stage_id.done', '=', False),
+ ('equipment_id', '=', equipment_id.id),
+ ('maintenance_type', '=', 'preventive'),
+ ('sf_maintenance_type', '=', context['type'])])
+ request_ids.write({'active': False})
+ return equipment_id.create_maintenance_request(context['type'])
+
+ def cancel(self):
+ context = self.env.context
+ equipment_id = self.env['maintenance.equipment'].sudo().search([('id', '=', context['equipment_id'])])
+ if context['type'] == '保养':
+ equipment_id.initial_action_date = equipment_id.initial_action_date_old
+ elif context['type'] == '检修':
+ equipment_id.initial_overhaul_date = equipment_id.initial_overhaul_date_old
diff --git a/sf_maintenance/wizard/maintenance_request_wizard.xml b/sf_maintenance/wizard/maintenance_request_wizard.xml
new file mode 100644
index 00000000..632c21d9
--- /dev/null
+++ b/sf_maintenance/wizard/maintenance_request_wizard.xml
@@ -0,0 +1,29 @@
+
+
+
+ 维保计划
+ maintenance.request.wizard
+ form
+ new
+
+
+
+ maintenance.request.wizard.form.view
+ maintenance.request.wizard
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py
index dc8e82c4..573bb73f 100644
--- a/sf_manufacturing/models/mrp_production.py
+++ b/sf_manufacturing/models/mrp_production.py
@@ -18,7 +18,7 @@ class MrpProduction(models.Model):
_inherit = 'mrp.production'
_description = "制造订单"
_order = 'create_date desc'
-
+ deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery')
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
request_ids = fields.One2many('maintenance.request', 'production_id')
@@ -34,6 +34,29 @@ class MrpProduction(models.Model):
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
+ @api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
+ def _compute_deadline_of_delivery(self):
+ for production in self:
+ # 确保 procurement_group_id 和相关字段存在
+ if production.procurement_group_id:
+ # 获取相关的 sale_id
+ sale_order_id = production.procurement_group_id.mrp_production_ids.mapped(
+ 'move_dest_ids.group_id.sale_id')
+
+ # 确保 sale_order_id 是有效的 ID 列表
+ if sale_order_id:
+ # 获取 sale.order 记录
+ sale_id = self.env['sale.order'].sudo().browse(sale_order_id.ids) # 使用 mapped 返回的 ID 列表
+
+ # 处理 sale_id
+ if sale_id:
+ # 假设我们只需要第一个 sale_id
+ production.deadline_of_delivery = sale_id[0].deadline_of_delivery if sale_id else False
+ else:
+ production.deadline_of_delivery = False
+ else:
+ production.deadline_of_delivery = False
+
@api.depends('workorder_ids.tool_state_remark')
def _compute_tool_state_remark(self):
for item in self:
@@ -118,10 +141,12 @@ class MrpProduction(models.Model):
], string='工序状态', default='待装夹')
# 零件图号
- part_number = fields.Char('零件图号')
+ part_number = fields.Char('零件图号', readonly=True)
# 上传零件图纸
- part_drawing = fields.Binary('零件图纸')
+ part_drawing = fields.Binary('零件图纸', readonly=True)
+
+ quality_standard = fields.Binary('质检标准', readonly=True)
@api.depends('product_id.manual_quotation')
def _compute_manual_quotation(self):
diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py
index e896ca40..23129578 100644
--- a/sf_manufacturing/models/mrp_workorder.py
+++ b/sf_manufacturing/models/mrp_workorder.py
@@ -225,6 +225,9 @@ class ResMrpWorkOrder(models.Model):
material_height = fields.Float(string='高')
# 零件图号
part_number = fields.Char(related='production_id.part_number', string='零件图号')
+ machining_drawings = fields.Binary('2D加工图纸', related='production_id.part_drawing', readonly=True)
+ quality_standard = fields.Binary('质检标准', related='production_id.quality_standard', readonly=True)
+
# 工序状态
process_state = fields.Selection([
('待装夹', '待装夹'),
@@ -1048,6 +1051,17 @@ class ResMrpWorkOrder(models.Model):
if workorder.production_id.tool_state in ['1', '2'] and workorder.state == 'ready':
workorder.state = 'waiting'
continue
+ if (workorder.production_id.tool_state in ['1', '2']
+ and not workorder.production_id.workorder_ids.filtered(lambda a: a.sequence == 0)
+ and workorder.production_id.programming_state == '编程中' and workorder.name == '装夹预调'):
+ if workorder.state == 'pending' and workorder == self.search(
+ [('production_id', '=', workorder.production_id.id),
+ ('routing_type', '=', '装夹预调'),
+ ('state', 'not in', ['rework', 'done', 'cancel'])],
+ limit=1,
+ order="sequence"):
+ workorder.state = 'waiting'
+ continue
# elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'cancel', 'progress',
# 'rework']:
diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py
index 6c6b1bf8..3b8ed0a6 100644
--- a/sf_manufacturing/models/product_template.py
+++ b/sf_manufacturing/models/product_template.py
@@ -774,8 +774,10 @@ class ResProductMo(models.Model):
# bfm下单
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
part_number = fields.Char(string='零件图号', readonly=True)
- # machining_drawings = fields.Binary('2D加工图纸', readonly=True)
- # quality_standard = fields.Binary('质检标准', readonly=True)
+ machining_drawings = fields.Binary('2D加工图纸', readonly=True)
+ quality_standard = fields.Binary('质检标准', readonly=True)
+ machining_drawings_name = fields.Char('2D加工图纸名', readonly=True)
+ quality_standard_name = fields.Char('质检标准名', readonly=True)
@api.constrains('tool_length')
def _check_tool_length_size(self):
@@ -837,6 +839,11 @@ class ResProductMo(models.Model):
else:
return self.env.ref('sf_dlm.product_uom_cubic_millimeter')
+ def attachment_update(self, name, res_id, res_field):
+ attachment_info = self.env['ir.attachment'].sudo().search(
+ [('res_id', '=', res_id), ('res_field', '=', res_field)], limit=1)
+ attachment_info.write({'name': name})
+
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品
def product_create(self, product_id, item, order_id, order_number, i):
copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
@@ -875,8 +882,11 @@ class ResProductMo(models.Model):
'manual_quotation': item['manual_quotation'] or False,
'part_number': item.get('part_number') or '',
'active': True,
- # 'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(item['machining_drawings']),
- # 'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
+ 'machining_drawings_name': item['machining_drawings_name'],
+ 'quality_standard_name': item['quality_standard_name'],
+ 'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(
+ item['machining_drawings']),
+ 'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
}
tax_id = self.env['account.tax'].sudo().search(
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
@@ -884,6 +894,12 @@ class ResProductMo(models.Model):
vals.update({'taxes_id': [(6, 0, [int(tax_id)])]})
copy_product_id.sudo().write(vals)
product_id.product_tmpl_id.active = False
+ if item['machining_drawings'] and item['machining_drawings_name']:
+ self.attachment_update(item['machining_drawings_name'], copy_product_id.product_tmpl_id.id,
+ 'machining_drawings')
+ if item['quality_standard'] and item['quality_standard_name']:
+ self.attachment_update(item['quality_standard_name'], copy_product_id.product_tmpl_id.id,
+ 'quality_standard')
return copy_product_id
def _get_ids(self, param):
diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py
index daf049eb..02161b4a 100644
--- a/sf_manufacturing/models/stock.py
+++ b/sf_manufacturing/models/stock.py
@@ -182,6 +182,11 @@ class StockRule(models.Model):
moves._action_confirm()
return True
+ def attachment_update(self, name, res_id, res_field):
+ attachment_info = self.env['ir.attachment'].sudo().search(
+ [('res_id', '=', res_id), ('res_field', '=', res_field)], limit=1)
+ attachment_info.write({'name': name})
+
@api.model
def _run_manufacture(self, procurements):
productions_values_by_company = defaultdict(list)
@@ -272,6 +277,16 @@ class StockRule(models.Model):
if quick_easy_order:
production.write({'part_number': quick_easy_order.part_drawing_number,
'part_drawing': quick_easy_order.machining_drawings})
+ else:
+ production.write({'part_number': production.product_id.part_number,
+ 'part_drawing': production.product_id.machining_drawings,
+ 'quality_standard': production.product_id.quality_standard})
+ if production.product_id.machining_drawings and production.product_id.machining_drawings_name:
+ self.attachment_update(production.product_id.machining_drawings_name, production.id,
+ 'part_drawing')
+ if production.product_id.quality_standard and production.product_id.quality_standard_name:
+ self.attachment_update(production.product_id.quality_standard_name, production.id,
+ 'quality_standard')
if sale_order:
# sale_order.write({'schedule_status': 'to schedule'})
self.env['sf.production.plan'].sudo().with_company(company_id).create({
diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml
index 670dea7c..4c06c544 100644
--- a/sf_manufacturing/views/mrp_production_addional_change.xml
+++ b/sf_manufacturing/views/mrp_production_addional_change.xml
@@ -98,9 +98,9 @@
-
+
@@ -328,6 +328,16 @@
投料状态
+
+
+
+
+
+
+
+
+
+
diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml
index 79b4e97a..a34c5c51 100644
--- a/sf_manufacturing/views/mrp_workorder_view.xml
+++ b/sf_manufacturing/views/mrp_workorder_view.xml
@@ -590,7 +590,18 @@
mrp.group_mrp_manager,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user,sf_base.group_sf_order_user
-
+
+
+
+
+
+
+
+
+
+
diff --git a/sf_message/__manifest__.py b/sf_message/__manifest__.py
index 2e8dfd30..8178973b 100644
--- a/sf_message/__manifest__.py
+++ b/sf_message/__manifest__.py
@@ -17,7 +17,7 @@
'data/cron_data.xml',
'data/template_data.xml',
'security/ir.model.access.csv',
-
+ 'views/mrp_workorder_views.xml',
],
'test': [
],
diff --git a/sf_message/models/sf_message_maintenance_logs.py b/sf_message/models/sf_message_maintenance_logs.py
index 288043ff..94a4c5c5 100644
--- a/sf_message/models/sf_message_maintenance_logs.py
+++ b/sf_message/models/sf_message_maintenance_logs.py
@@ -1,14 +1,15 @@
+# -*- coding: utf-8 -*-
from odoo import models, fields, api
class SFMessageMaintenanceLogs(models.Model):
_name = 'sf.maintenance.logs'
_inherit = ['sf.maintenance.logs', 'jikimo.message.dispatch']
- @api._model_create_multi
+ @api.model_create_multi
def create(self, vals_list):
res = super(SFMessageMaintenanceLogs, self).create(vals_list)
for rec in res:
- rec.add_queue()
+ rec.add_queue('设备故障')
return res
def _get_message(self, message_queue_ids):
diff --git a/sf_message/models/sf_message_sale.py b/sf_message/models/sf_message_sale.py
index 13a5f796..3fd57f8a 100644
--- a/sf_message/models/sf_message_sale.py
+++ b/sf_message/models/sf_message_sale.py
@@ -103,39 +103,58 @@ class SFMessageSale(models.Model):
today = datetime.today().date()
deadline_check = today + timedelta(days=1)
logging.info(f"today: {today}, deadline_check: {deadline_check}")
- sale_order = self.sudo().search([('state', 'in', ['sale']), ('deadline_of_delivery', '!=', False)])
+ sale_order = self.sudo().search(
+ [('state', 'in', ['sale']), ('deadline_of_delivery', '!=', False), ('delivery_status', '!=', 'full')])
for item in sale_order:
production = self.env['mrp.production'].search([('origin', '=', item.name)])
production_not_done = production.filtered(lambda p: p.state not in ['done', 'scrap', 'cancel'])
production_done_count = len(production.filtered(lambda p: p.state in ['done', 'scrap', 'cancel']))
- if len(production_not_done) != item.mrp_production_count:
- if deadline_check == item.deadline_of_delivery:
+ if (len(production_not_done) >= 1 and len(production_not_done) != item.mrp_production_count) or len(
+ production_not_done) != production_done_count:
+ logging.info("-----不等于----")
+ logging.info(f"name: {item.name}")
+ logging.info(
+ f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}")
+ logging.info(f"deadline_of_delivery: {item.deadline_of_delivery}")
+ if deadline_check == item.deadline_of_delivery and item.delivery_warning not in ['warning']:
item.delivery_warning = 'warning'
- elif today == item.deadline_of_delivery:
+ elif today >= item.deadline_of_delivery and item.delivery_warning not in ['overdue']:
item.delivery_warning = 'overdue'
elif production_done_count == item.mrp_production_count:
+ logging.info("-----等于----")
+ logging.info(f"name: {item.name}")
+ logging.info(
+ f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}")
+ logging.info(f"deadline_of_delivery: {item.deadline_of_delivery}")
if item.delivery_status in ['pending', 'partial']:
- if deadline_check == item.deadline_of_delivery:
+ if deadline_check == item.deadline_of_delivery and item.delivery_warning not in ['warning']:
item.delivery_warning = 'warning'
- elif today == item.deadline_of_delivery:
+ elif today >= item.deadline_of_delivery and item.delivery_warning not in ['overdue']:
item.delivery_warning = 'overdue'
else:
+ logging.info("-----1111111----")
+ logging.info(f"name: {item.name}")
+ logging.info(
+ f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}")
continue
+ # 获取业务节点
+ business_node_ids = {
+ 'warning': self.env.ref('sf_message.bussiness_sale_order_overdue_warning').id,
+ 'overdue': self.env.ref('sf_message.bussiness_sale_order_overdue').id
+ }
overdue_orders = self.sudo().search([('delivery_warning', 'in', ['warning', 'overdue'])])
for wo in overdue_orders:
- message_template = self.env["jikimo.message.template"].search([
- ("model", "=", self._name),
- ("bussiness_node_id", "=", self.env.ref('sf_message.bussiness_sale_order_overdue_warning').id)
- ])
- sale_order_has = self.env['jikimo.message.queue'].search([
- ('res_id', '=', wo.id),
- ('message_status', '=', 'pending'),
- ('message_template_id', '=', message_template.id)
- ])
- if not sale_order_has:
- if wo.delivery_warning == 'warning':
- wo.add_queue('销售订单逾期预警')
- elif wo.delivery_warning == 'overdue':
- wo.add_queue('销售订单已逾期')
-
-
+ business_node_id = business_node_ids.get(wo.delivery_warning)
+ if business_node_id:
+ message_template = self.env["jikimo.message.template"].search([
+ ("model", "=", self._name),
+ ("bussiness_node_id", "=", business_node_id)
+ ], limit=1)
+ sale_order_has = self.env['jikimo.message.queue'].search([
+ ('res_id', '=', wo.id),
+ ('message_status', '=', 'pending'),
+ ('message_template_id', '=', message_template.id)
+ ])
+ if not sale_order_has:
+ message_name = '销售订单逾期预警' if wo.delivery_warning == 'warning' else '销售订单已逾期'
+ wo.add_queue(message_name)
diff --git a/sf_message/models/sf_message_workorder.py b/sf_message/models/sf_message_workorder.py
index 3ad17d93..c531e013 100644
--- a/sf_message/models/sf_message_workorder.py
+++ b/sf_message/models/sf_message_workorder.py
@@ -73,7 +73,7 @@ class SFMessageWork(models.Model):
if record:
i += 1
if i >= 1:
- action_id = self.env.ref('sf_manufacturing.mrp_workorder_action_tablet').id
+ action_id = self.env.ref('sf_message.mrp_workorder_action_notify').id
url_with_id = f"{url}/web#view_type=list&action={action_id}"
content_template = content.replace('{{url}}', url_with_id)
if bussiness_node in template_names['预警']:
@@ -85,7 +85,7 @@ class SFMessageWork(models.Model):
def request_url(self):
url = self.env['ir.config_parameter'].get_param('web.base.url')
- action_id = self.env.ref('sf_manufacturing.mrp_workorder_action_tablet').id
+ action_id = self.env.ref('sf_message.mrp_workorder_action_notify').id
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_stock_dropshipping')]).id
# 查询参数
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder',
@@ -115,12 +115,13 @@ class SFMessageWork(models.Model):
item.date_planned_finished.strftime("%Y-%m-%d %H:%M:%S"))
date_planned_finished = datetime.strptime(date_planned_finished_str, '%Y-%m-%d %H:%M:%S')
twelve_hours_ago = current_time_datetime - timedelta(hours=12)
- if current_time_datetime >= date_planned_finished:
+ if current_time_datetime >= date_planned_finished and item.delivery_warning not in ['overdue']:
logging.info("------overdue-------")
logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, "
f"Planned Finish: {date_planned_finished}")
item.delivery_warning = 'overdue'
- elif twelve_hours_ago <= current_time_datetime <= date_planned_finished:
+ elif twelve_hours_ago <= current_time_datetime <= date_planned_finished and item.delivery_warning not in [
+ 'warning']:
logging.info("------warning-------")
logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, "
f"Planned Finish: {date_planned_finished}")
diff --git a/sf_message/views/mrp_workorder_views.xml b/sf_message/views/mrp_workorder_views.xml
new file mode 100644
index 00000000..1e31fc6d
--- /dev/null
+++ b/sf_message/views/mrp_workorder_views.xml
@@ -0,0 +1,34 @@
+
+
+
+ 工单
+ ir.actions.act_window
+ mrp.workorder
+ tree,form
+
+
+
+ current
+ [('state', '!=', 'cancel'),('schedule_state', '=', '已排')]
+ {'search_default_product': 1, 'search_default_workcenter_id':
+ active_id,'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1,
+ 'search_default_ready': 1, 'search_default_progress': 1}
+
+
+
+ 没有工单要做!
+
+
+ 工作订单是作为制造订单的一部分执行的操作。
+ 工序在物料清单中定义或直接添加到制造订单中。
+
+
+ 使用工作台工作中心控制面板直接登记车间中的操作.
+ 平板电脑为您的工人提供工作表,并允许他们报废产品,跟踪时间,
+ 发起维护请求,执行质量测试等.
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_mrs_connect/models/res_config_setting.py b/sf_mrs_connect/models/res_config_setting.py
index 6ec49f74..5243ccb2 100644
--- a/sf_mrs_connect/models/res_config_setting.py
+++ b/sf_mrs_connect/models/res_config_setting.py
@@ -84,6 +84,8 @@ class ResConfigSettings(models.TransientModel):
_logger.info("同步刀具物料切削速度完成")
self.env['sf.feed.per.tooth'].sync_all_feed_per_tooth()
_logger.info("同步刀具物料每齿走刀量完成")
+ self.env['sf.machining.accuracy'].sync_machining_accuracy_all()
+ _logger.info("同步加工精度完成")
except Exception as e:
_logger.info("sf_all_sync error: %s" % e)
diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py
index 615c69a0..ae1930c6 100644
--- a/sf_mrs_connect/models/sync_common.py
+++ b/sf_mrs_connect/models/sync_common.py
@@ -74,6 +74,8 @@ class MrStaticResourceDataSync(models.Model):
_logger.info("同步刀具物料切削速度完成")
self.env['sf.feed.per.tooth'].sync_feed_per_tooth_yesterday()
_logger.info("同步刀具物料每齿走刀量完成")
+ self.env['sf.machining.accuracy'].sync_machining_accuracy_all()
+ _logger.info("同步加工精度完成")
except Exception as e:
traceback_error = traceback.format_exc()
logging.error("同步静态资源库失败:%s" % traceback_error)
@@ -3133,3 +3135,36 @@ class CuttingToolBasicParameters(models.Model):
})
else:
raise ValidationError("刀具物料基本参数认证未通过")
+
+
+class MachiningAccuracySync(models.Model):
+ _inherit = 'sf.machining.accuracy'
+ _description = '加工精度'
+ url = '/api/machining_accuracy/list'
+
+ def sync_machining_accuracy_all(self):
+ config = self.env['res.config.settings'].get_values()
+ headers = Common.get_headers(self, config['token'], config['sf_secret_key'])
+ strUrl = config['sf_url'] + self.url
+ r = requests.post(strUrl, json={}, data=None, headers=headers)
+ r = r.json()
+ result = json.loads(r['result'])
+ _logger.info('加工精度:%s' % result)
+ if result['status'] == 1:
+ machining_accuracy_all_list = result['machining_accuracy_all_list']
+ # 获取同步的id集合
+ ids = [obj['id'] for obj in machining_accuracy_all_list]
+ self.env['sf.machining.accuracy'].sudo().search(
+ [('sync_id', 'not in', ids)]).unlink()
+ for time in machining_accuracy_all_list:
+ machining_accuracy = self.env['sf.machining.accuracy'].sudo().search(
+ [('sync_id', '=', time['id'])])
+ if machining_accuracy:
+ machining_accuracy.name = time['name']
+ machining_accuracy.standard_tolerance = time['standard_tolerance']
+ else:
+ self.env['sf.machining.accuracy'].sudo().create({
+ "sync_id": time['id'],
+ "name": time['name'],
+ "discount": time['discount'],
+ })
diff --git a/sf_plan/models/custom_plan.py b/sf_plan/models/custom_plan.py
index 60587dec..492ef525 100644
--- a/sf_plan/models/custom_plan.py
+++ b/sf_plan/models/custom_plan.py
@@ -219,7 +219,7 @@ class sf_production_plan(models.Model):
return num
- def do_production_schedule(self, count=1):
+ def do_production_schedule(self):
"""
排程方法
"""
@@ -227,29 +227,26 @@ 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, count)
- if not is_schedule:
- raise ValidationError("排程失败")
- workorder_id_list = record.production_id.workorder_ids.ids
- if record.production_id:
- if record.production_id.workorder_ids:
- for item in record.production_id.workorder_ids:
- if item.name == 'CNC加工':
- item.date_planned_finished = datetime.now() + timedelta(days=100)
- item.date_planned_start = self.date_planned_start if self.date_planned_start else datetime.now()
- record.sudo().production_id.plan_start_processing_time = item.date_planned_start
- item.date_planned_finished = item.date_planned_start + timedelta(
- minutes=record.env['mrp.routing.workcenter'].sudo().search(
- [('name', '=', 'CNC加工')]).time_cycle)
- item.duration_expected = record.env['mrp.routing.workcenter'].sudo().search(
- [('name', '=', 'CNC加工')]).time_cycle
- record.calculate_plan_time_before(item, workorder_id_list)
- record.calculate_plan_time_after(item, workorder_id_list)
- record.date_planned_start, record.date_planned_finished = \
- item.date_planned_start, item.date_planned_finished
+ if record.production_id.workorder_ids:
+ last_cnc_start = record.date_planned_start if record.date_planned_start else datetime.now()
+ for item in record.production_id.workorder_ids:
+ if item.name == 'CNC加工':
+ # 将同一个面的所有工单筛选出来
+ workorder_list = record.production_id.workorder_ids.filtered(lambda x: x.processing_panel == item.processing_panel)
+ routing_workcenter = record.env['mrp.routing.workcenter'].sudo().search(
+ [('name', '=', 'CNC加工')], limit=1)
+ # 设置一个小的开始时间
+ item.date_planned_start = datetime.now() - timedelta(days=100)
+ item.date_planned_finished = last_cnc_start + timedelta(
+ minutes=routing_workcenter.time_cycle)
+ item.date_planned_start = last_cnc_start
+ record.sudo().production_id.plan_start_processing_time = item.date_planned_start
+ item.duration_expected = routing_workcenter.time_cycle
+ pre_duration , next_duration = record.calculate_plan_time(item, workorder_list)
+ record.date_planned_finished = item.date_planned_finished
+ # 计算下一个cnc工单的开始时间
+ last_cnc_start = workorder_list[-1].date_planned_finished + timedelta(minutes=pre_duration)
record.state = 'done'
- record.date_planned_finished = record.date_planned_start + timedelta(
- minutes=60) if not record.date_planned_finished else record.date_planned_finished
# record.production_id.schedule_state = '已排'
record.sudo().production_id.schedule_state = '已排'
record.sudo().production_id.process_state = '待装夹'
@@ -282,54 +279,66 @@ class sf_production_plan(models.Model):
}
# 处理是否可排程
- 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:
- raise UserError('生产线没有配置工作中心')
- production_lines = workcenter_ids.filtered(lambda b: "自动生产线" in b.name)
- if not production_lines: # 判断是否配置了自动生产线
- raise UserError('生产线没有配置自动生产线')
- if date_planned_start < datetime.now(): # 判断计划开始时间是否小于当前时间
- raise UserError('计划开始时间不能小于当前时间')
- if all(not production_line.deal_with_workcenter_calendar(date_planned_start) for production_line in
- production_lines): # 判断计划开始时间是否在配置的工作中心的工作日历内
- 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, count): # 判断生产线是否可排程
- raise UserError('当前计划开始时间不能预约排程,生产线该时间段没有可排程的资源')
+ def deal_processing_schedule(self, date_planned_start,):
+ count = len(self)
+ workcenter_ids = self.production_line_id.mrp_workcenter_ids
+ if not workcenter_ids:
+ raise UserError('生产线没有配置工作中心')
+ production_lines = workcenter_ids.filtered(lambda b: "自动生产线" in b.name)
+ if not production_lines: # 判断是否配置了自动生产线
+ raise UserError('生产线没有配置自动生产线')
+ if date_planned_start < datetime.now(): # 判断计划开始时间是否小于当前时间
+ raise UserError('计划开始时间不能小于当前时间')
+ if all(not production_line.deal_with_workcenter_calendar(date_planned_start) for production_line in
+ production_lines): # 判断计划开始时间是否在配置的工作中心的工作日历内
+ 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, count): # 判断生产线是否可排程
+ raise UserError('当前计划开始时间不能预约排程,生产线该时间段没有可排程的资源')
return True
- def calculate_plan_time_before(self, item, workorder_id_list):
+ def calculate_plan_time(self, item, workorder_list):
"""
根据CNC工单的时间去计算之前的其他工单的开始结束时间
"""
- sequence = workorder_id_list.index(item.id) - 1
- # 计算CNC加工之前工单的开始结束时间
- for i in range(1 if sequence == 0 else sequence):
- current_workorder_id = (item.id - (i + 1))
- current_workorder_obj = self.env['mrp.workorder'].sudo().search(
- [('id', '=', current_workorder_id)])
- old_workorder_obj = self.env['mrp.workorder'].sudo().search(
- [('id', '=', (current_workorder_id + 1))])
- work_order = self.env['mrp.workorder'].sudo().search(
- [('production_id', '=', self.production_id.id), ('id', '=', current_workorder_id)])
- work_order.date_planned_finished = datetime.now() + timedelta(days=100)
- work_order.date_planned_start = old_workorder_obj.date_planned_start - timedelta(
- minutes=self.env['mrp.routing.workcenter'].sudo().search(
- [('name', '=', current_workorder_obj.name)]).time_cycle)
- work_order.date_planned_finished = old_workorder_obj.date_planned_start
- work_order.duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
- [('name', '=', current_workorder_obj.name)]).time_cycle
- first_workorder = self.env['mrp.workorder'].sudo().search([('id', '=', workorder_id_list[0])])
- second_workorder = self.env['mrp.workorder'].sudo().search([('id', '=', workorder_id_list[1])])
- if second_workorder.date_planned_start < first_workorder.date_planned_finished:
- item.date_planned_start += timedelta(minutes=60)
- item.date_planned_finished += timedelta(minutes=60)
- item.duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
- [('name', '=', 'CNC加工')]).time_cycle
- self.calculate_plan_time_before(item, workorder_id_list)
+ item_position = 0
+ for index, workorder in enumerate(workorder_list):
+ if workorder.id == item.id:
+ item_position = index
+ break
+ routing_workcenters = self.env['mrp.routing.workcenter'].sudo().search([])
+ # 记录所有前序工序时长
+ previous_workorder_duration = 0
+ for i in range(item_position, -1, -1):
+ if i < 1:
+ break
+ current_workorder = workorder_list[i]
+ next_workorder = workorder_list[i - 1]
+ routing_workcenter = routing_workcenters.filtered(lambda x: x.name == next_workorder.name)[0]
+ # 设置一个小的开始时间
+ next_workorder.date_planned_start = datetime.now() - timedelta(days=100)
+ next_workorder.date_planned_finished = current_workorder.date_planned_start
+ next_workorder.date_planned_start = next_workorder.date_planned_finished - timedelta(
+ minutes=routing_workcenter.time_cycle)
+ next_workorder.duration_expected = routing_workcenter.time_cycle
+ previous_workorder_duration += routing_workcenter.time_cycle
+ # 记录所有后续工序时长
+ next_workorder_duration = 0
+ for i in range(item_position, len(workorder_list) - 1):
+ if i > len(workorder_list) - 1:
+ break
+ current_workorder = workorder_list[i]
+ next_workorder = workorder_list[i + 1]
+ routing_workcenter = routing_workcenters.filtered(lambda x: x.name == next_workorder.name)[0]
+ # 设置一个小的开始时间
+ next_workorder.date_planned_start = datetime.now() - timedelta(days=100)
+ next_workorder.date_planned_finished = current_workorder.date_planned_finished + timedelta(
+ minutes=routing_workcenter.time_cycle)
+ next_workorder.date_planned_start = current_workorder.date_planned_finished
+ next_workorder.duration_expected = routing_workcenter.time_cycle
+ next_workorder_duration += routing_workcenter.time_cycle
+ return previous_workorder_duration, next_workorder_duration
def calculate_plan_time_after(self, item, workorder_id_list):
"""
diff --git a/sf_plan/wizard/action_plan_some.py b/sf_plan/wizard/action_plan_some.py
index 23545516..1c08ff0f 100644
--- a/sf_plan/wizard/action_plan_some.py
+++ b/sf_plan/wizard/action_plan_some.py
@@ -31,22 +31,24 @@ class Action_Plan_All_Wizard(models.TransientModel):
# 确认排程按钮
def action_plan_all(self):
# 使用传递过来的计划ID
- temp_plan_ids = self.plan_ids
+ self.plan_ids.production_line_id = self.production_line_id.id
+ self.plan_ids.date_planned_start = self.date_planned_start
# 在这里添加您的逻辑来处理这些ID
- count = len(temp_plan_ids) + 1
- for plan in temp_plan_ids:
- count = count - 1
- # 处理每个计划
- # 比如更新计划状态、分配资源等
- # 示例:plan.state = 'scheduled'
- print('处理计划:', plan.id)
- # 拿到计划对象
- 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(count)
+ # 判断能否排成
+ self.plan_ids.deal_processing_schedule(self.date_planned_start)
+ self.plan_ids.do_production_schedule()
+ # for plan in temp_plan_ids:
+ # # 处理每个计划
+ # # 比如更新计划状态、分配资源等
+ # # 示例:plan.state = 'scheduled'
+ # print('处理计划:', plan.id)
+ # # 拿到计划对象
+ # 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.state = 'done'
- print('处理计划:', plan.id, '完成')
+ _logger.info('处理计划: %s 完成', self.plan_ids.ids)
# # 获取当前生产线
# production_line_id = self.production_line_id
@@ -73,4 +75,14 @@ class Action_Plan_All_Wizard(models.TransientModel):
# if all(production_order_plan_state == '已取消' for production_order_plan_state in production_order_plan_state_list):
# raise UserError('当前生产线的所有生产订单都已取消,请勿重复排程!')
# # 如果当前生产线的所有生产订单的排程状态都是已暂停,则报错
- # if all(production_order_plan_state == '已暂停' for production_order_plan_state in production
+ # if all(production_order_plan_state == '已暂停' for production_order_plan_state in production_order_plan_state_list):
+ # raise UserError('当前生产线的所有生产订单都已暂停,请勿重复排程!')
+ # # 如果当前生产线的所有生产订单的排程状态都是已完成,则报错
+ # if all(production_order_plan_state == '已完成' for production_order_plan_state in production_order_plan_state_list):
+ # raise UserError('当前生产线的所有生产订单都已完成,请勿重复排程!')
+ # # 如果当前生产线的所有生产订单的排程状态都是已取消,则报错
+ # if all(production_order_plan_state == '已取消' for production_order_plan_state in production_order_plan_state_list):
+ # raise UserError('当前生产线的所有生产订单都已取消,请勿重复排程!')
+ # # 如果当前生产线的所有生产订单的排程状态都是已暂停,则报错
+ # if all(production_order_plan_state == '已暂停' for production_order_plan_state in production_order_plan_state_list):
+ # raise UserError('当前生产线的所有生产订单都已暂停,请勿重复排程!')
diff --git a/sf_quality/models/quality_cnc_test.py b/sf_quality/models/quality_cnc_test.py
index f911fb43..d0301a7f 100644
--- a/sf_quality/models/quality_cnc_test.py
+++ b/sf_quality/models/quality_cnc_test.py
@@ -31,8 +31,8 @@ class SfQualityCncTest(models.Model):
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
detailed_reason = fields.Text('详细原因')
- # machining_drawings = fields.Binary(related='workorder_id.machining_drawings', string='2D加工图纸', readonly=True)
- # quality_standard = fields.Binary(related='workorder_id.quality_standard', string='质检标准', readonly=True)
+ machining_drawings = fields.Binary('2D加工图纸', related='workorder_id.machining_drawings', readonly=True)
+ quality_standard = fields.Binary('质检标准', related='workorder_id.quality_standard', readonly=True)
def submit_pass(self):
if self.test_results in ['返工', '报废']:
diff --git a/sf_quality/views/quality_cnc_test_view.xml b/sf_quality/views/quality_cnc_test_view.xml
index 85f92bfb..2cfc8cda 100644
--- a/sf_quality/views/quality_cnc_test_view.xml
+++ b/sf_quality/views/quality_cnc_test_view.xml
@@ -107,10 +107,10 @@
-
+
-
+
diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py
index f28a76c4..d3ec1aa0 100644
--- a/sf_sale/models/sale_order.py
+++ b/sf_sale/models/sale_order.py
@@ -57,7 +57,6 @@ class ReSaleOrder(models.Model):
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效')
-
# 业务平台分配工厂后在智能工厂先创建销售订单
def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address,
deadline_of_delivery, payments_way, pay_way):
@@ -128,7 +127,9 @@ class ReSaleOrder(models.Model):
'price_unit': product.list_price,
'product_uom_qty': item['number'],
'model_glb_file': base64.b64decode(item['model_file']),
- 'remark': item.get('remark')
+ 'remark': item.get('remark'),
+ 'is_incoming_material': item.get('is_incoming_material'),
+ 'incoming_size': item.get('incoming_size'),
}
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
@@ -169,6 +170,9 @@ class ResaleOrderLine(models.Model):
check_status = fields.Selection(related='order_id.check_status')
remark = fields.Char('备注')
+ is_incoming_material = fields.Boolean('是否带料', default=False)
+ incoming_size = fields.Char('带料尺寸')
+
@api.depends('product_template_id')
def _compute_model_glb_file(self):
for line in self:
diff --git a/sf_sale/views/purchase_order_view.xml b/sf_sale/views/purchase_order_view.xml
index 445bded6..7a9836c5 100644
--- a/sf_sale/views/purchase_order_view.xml
+++ b/sf_sale/views/purchase_order_view.xml
@@ -6,6 +6,25 @@
purchase.order
+
+
+
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
diff --git a/sf_sale/views/sale_order_view.xml b/sf_sale/views/sale_order_view.xml
index 61b53b08..a84a2b44 100644
--- a/sf_sale/views/sale_order_view.xml
+++ b/sf_sale/views/sale_order_view.xml
@@ -95,7 +95,7 @@
{'readonly': [('state', 'in', ['cancel','sale'])]}
-
+
@@ -118,6 +118,8 @@
+
+
{'readonly': [('state', 'in', ['cancel','sale'])]}
@@ -162,6 +164,11 @@
拒绝接单
+
+
+
+
+
@@ -265,7 +272,6 @@
-
{"search_default_categ_id":1,
"search_default_filter_to_sell":1,"sale_multi_pricelist_product_template": 1}
diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py
index e8523cd3..78a652f4 100644
--- a/sf_tool_management/models/functional_tool.py
+++ b/sf_tool_management/models/functional_tool.py
@@ -51,6 +51,8 @@ class FunctionalCuttingToolEntity(models.Model):
string='位置', compute='_compute_current_location_id', store=True)
image = fields.Binary('图片', readonly=True)
+ stock_num = fields.Integer('库存变更次数', default=0)
+
safe_inventory_id = fields.Many2one('sf.real.time.distribution.of.functional.tools',
string='功能刀具安全库存', readonly=True)
@@ -71,7 +73,7 @@ class FunctionalCuttingToolEntity(models.Model):
})
@api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status',
- 'current_shelf_location_id')
+ 'current_shelf_location_id', 'stock_num')
def _compute_current_location_id(self):
for record in self:
if record.functional_tool_status == '已拆除':
diff --git a/sf_tool_management/models/stock.py b/sf_tool_management/models/stock.py
index 9f0d9f82..92198239 100644
--- a/sf_tool_management/models/stock.py
+++ b/sf_tool_management/models/stock.py
@@ -53,6 +53,13 @@ class StockMoveLine(models.Model):
[('barcode_id', '=', line_id.lot_id.id),
('functional_tool_status', '=', '正常')]).cnc_function_tool_use_verify()
+ for move_line in move_lines:
+ if move_line.lot_id:
+ tool_id = self.env['sf.functional.cutting.tool.entity'].sudo().search(
+ [('barcode_id', '=', move_line.lot_id.id),
+ ('functional_tool_status', '=', '正常')])
+ tool_id.stock_num += tool_id.stock_num
+
class StockPicking(models.Model):
_inherit = 'stock.picking'