Compare commits

..

1 Commits

Author SHA1 Message Date
胡尧
61bcd72a41 表面工艺外协调拨单数量问题 2025-05-09 17:24:24 +08:00
34 changed files with 502 additions and 788 deletions

View File

@@ -19,9 +19,8 @@ class MrpProduction(models.Model):
# item.pr_mp_count = len(pr_ids)
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品
first_mp = self.env['mrp.production'].search(
[('origin', '=', item.origin)], limit=1, order='id asc')
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', first_mp.name), ('is_subcontract', '!=', 'True')])
[('origin', '=', item.origin)], limit=1, order='id asc')
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_mp.name)])
item.pr_mp_count = len(pr_ids)
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')])
@@ -30,10 +29,18 @@ class MrpProduction(models.Model):
采购请求
"""
self.ensure_one()
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)])
# if self.product_id.product_tmpl_id.single_manufacturing == True and not self.is_remanufacture:
# first_order = self.env['mrp.production'].search(
# [('origin', '=', self.origin), ('product_id', '=', self.product_id.id)], limit=1, order='id asc')
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_order.name)])
# else:
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)])
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品
first_mp = self.env['mrp.production'].search(
[('origin', '=', self.origin)], limit=1, order='id asc')
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', first_mp.name), ('is_subcontract', '!=', 'True')])
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_mp.name)])
action = {
'res_model': 'purchase.request',
@@ -47,7 +54,7 @@ class MrpProduction(models.Model):
else:
action.update({
'name': _("%s生成采购请求单", self.name),
'domain': [('id', 'in', pr_ids.ids)],
'domain': [('id', 'in', pr_ids)],
'view_mode': 'tree,form',
})
return action

View File

@@ -114,10 +114,7 @@ class PurchaseRequestLine(models.Model):
def _compute_qty_to_buy(self):
for pr in self:
qty_to_buy = sum(pr.mapped("product_qty"))
if pr.purchase_count > 0:
qty_to_buy -= sum(pr.mapped("purchase_lines").filtered(lambda po: po.state != 'cancel').mapped(
"product_qty"))
qty_to_buy = sum(pr.mapped("product_qty")) - sum(pr.mapped("qty_done")) - sum(pr.mapped("qty_in_progress"))
pr.qty_to_buy = qty_to_buy > 0.0
pr.pending_qty_to_receive = qty_to_buy

View File

@@ -4,7 +4,6 @@ import json
import logging
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect
from odoo.addons.sf_base.decorators.api_log import api_log
from datetime import datetime
_logger = logging.getLogger(__name__)
@@ -13,7 +12,6 @@ class WorkorderExceptionConroller(http.Controller):
@http.route('/AutoDeviceApi/BillError', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('工单对接错误', requester='中控系统')
def workder_exception(self, **kw):
"""
记录工单异常

View File

@@ -4,7 +4,6 @@ import json
import logging
from odoo import http
from odoo.http import request
from odoo.addons.sf_base.decorators.api_log import api_log
_logger = logging.getLogger(__name__)
@@ -12,7 +11,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/MachineToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('机床刀具组', requester='中控系统')
def get_maintenance_tool_groups_Info(self, **kw):
"""
机床刀具组接口

View File

@@ -27,9 +27,6 @@ def api_log(name=None, requester=None):
# 执行原始函数
result = func(*args, **kwargs)
origin_result = result
if isinstance(result, str):
result = json.loads(result)
# 计算响应时间
end_time = datetime.now()
@@ -44,7 +41,7 @@ def api_log(name=None, requester=None):
'response_data': json.dumps(result, ensure_ascii=False),
'remote_addr': remote_addr,
'response_time': response_time,
'status': result.get('code') or result.get('ErrorCode') or 500,
'status': result.get('code', 500),
'requester': requester,
'responser': '智能工厂'
}
@@ -52,7 +49,7 @@ def api_log(name=None, requester=None):
# 异步创建日志记录
request.env['api.request.log'].sudo().with_context(tracking_disable=True).create(log_vals)
return origin_result
return result
except Exception as e:
_logger.error(f"API日志记录失败: {str(e)}")

View File

@@ -1,9 +1,4 @@
from odoo import models, fields, api
import json, ast
import logging
import requests
_logger = logging.getLogger(__name__)
class ApiRequestLog(models.Model):
@@ -21,52 +16,3 @@ class ApiRequestLog(models.Model):
status = fields.Integer('状态码')
requester = fields.Char('请求方')
responser = fields.Char('响应方')
@api.model
def log_request(self, method, url, name=None, responser=None, **kwargs):
# Log the request
request_headers = kwargs.get('headers', {})
request_body = kwargs.get('json') or kwargs.get('params') or {}
_logger.info(f"Request: {method} {url} Headers: {request_headers} Body: {request_body}")
# Make the actual request
response = requests.request(method, url, **kwargs)
# Log the response
response_status = response.status_code
response_headers = response.headers
response_body = response.text
response_time = response.elapsed.total_seconds()
_logger.info(f"Response: Status: {response_status} Headers: {response_headers} Body: {response_body}")
try:
# 如果是字符串,先尝试用 ast.literal_eval 安全地转换成 Python 对象
if isinstance(response_body, str):
response_body_obj = json.loads(response_body)
else:
response_body_obj = response_body
# 再使用 json.dumps 转换成标准的 JSON 字符串
response_body = json.dumps(response_body_obj, ensure_ascii=False)
except Exception as e:
_logger.warning(f"转换 response_body 到标准 JSON 失败: {str(e)}")
# 如果转换失败,保持原样
# Save to database
self.sudo().create({
'name': name,
'path': url,
'method': method,
'request_data': request_body,
'response_data': response_body,
'remote_addr': None,
'response_time': response_time,
'status': response_status,
'requester': '智能工厂',
'responser': responser
})
return response

View File

@@ -1,16 +1,16 @@
import logging
from odoo import fields, models, api
from odoo.exceptions import UserError
from odoo.tools import str2bool
class ResMrpRoutingWorkcenter(models.Model):
_inherit = 'mrp.routing.workcenter'
def init(self):
super(ResMrpRoutingWorkcenter, self).init()
# 在模块初始化时触发计算字段的更新
records = self.search([])
if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_workcenter',default='False')):
return
records.optional_process_parameters_date()
self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_workcenter', True)
# import logging
# from odoo import fields, models, api
# from odoo.exceptions import UserError
# from odoo.tools import str2bool
#
#
# class ResMrpRoutingWorkcenter(models.Model):
# _inherit = 'mrp.routing.workcenter'
# def init(self):
# super(ResMrpRoutingWorkcenter, self).init()
# # 在模块初始化时触发计算字段的更新
# records = self.search([])
# if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_workcenter',default='False')):
# return
# records.optional_process_parameters_date()
# self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_workcenter', True)

View File

@@ -1,87 +1,85 @@
# -*- coding: utf-8 -*-
import logging
from odoo import fields, models, api
from odoo.exceptions import UserError
from odoo.tools import str2bool
class SfProductionProcessParameter(models.Model):
_inherit = 'sf.production.process.parameter'
@api.model
def create(self, vals):
# if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
# vals['code'] = '101'+self.routing_id.code +self.env['ir.sequence'].next_by_code('sf.production.process.parameter')
if vals.get('routing_id'):
# vals['gain_way'] = '外协'
routing_id = self.env['mrp.routing.workcenter'].browse(vals.get('routing_id'))
if routing_id.surface_technics_id and not vals.get('process_id'):
vals['process_id'] = routing_id.surface_technics_id.id
if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
vals['code'] = '101' + routing_id.code + self.env['ir.sequence'].next_by_code(
'sf.production.process.parameter')
obj = super(SfProductionProcessParameter, self).create(vals)
return obj
def create_service_product(self):
if not self.active:
return
service_categ = self.env.ref(
'sf_dlm.product_category_surface_technics_sf').sudo()
product_name = f"{self.process_id.name}_{self.name}"
product_id = self.env['product.template'].search(
[("name", '=', product_name)])
if product_id:
product_id.server_product_process_parameters_id = self.id
else:
res_partner = self.env['res.partner'].search([('name','=','湖南傲派自动化设备有限公司')])
self.env['product.template'].create({
'detailed_type': 'service',
'name': product_name,
'invoice_policy': 'delivery',
'categ_id': service_categ.id,
'description': f"基于{self.name}创建的服务产品",
'sale_ok': True, # 可销售
'purchase_ok': True, # 可采购
'server_product_process_parameters_id': self.id,
'seller_ids': [(0, 0, {
# 'delay': 1,
'partner_id': res_partner.id,
'price': 1, })],
})
def create_work_center(self):
production_process_parameter = self
if not production_process_parameter.process_id:
return
if not production_process_parameter.routing_id:
workcenter_id = self.env['mrp.routing.workcenter'].search(
[("surface_technics_id", '=', production_process_parameter.process_id.id)])
if not workcenter_id:
outsourcing_work_center = self.env['mrp.workcenter'].search(
[("name", '=', '外协工作中心')])
routing_id = self.env['mrp.routing.workcenter'].create({
'workcenter_ids': [(6, 0, outsourcing_work_center.ids)],
'routing_tag': 'special',
'routing_type': '表面工艺',
'is_outsource': True,
'surface_technics_id': production_process_parameter.process_id.id,
'name': production_process_parameter.process_id.name,
})
production_process_parameter.routing_id = routing_id.id
else:
production_process_parameter.routing_id = workcenter_id.id
def init(self):
super(SfProductionProcessParameter, self).init()
# 在模块初始化时触发计算字段的更新
records = self.search([])
if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_process',
default='False')):
return
for record in records:
if not record.outsourced_service_products:
record.create_service_product()
record.create_work_center()
self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_process', True)
# # -*- coding: utf-8 -*-
# import logging
# from odoo import fields, models, api
# from odoo.exceptions import UserError
# from odoo.tools import str2bool
#
#
# class SfProductionProcessParameter(models.Model):
# _inherit = 'sf.production.process.parameter'
#
#
# @api.model
# def create(self, vals):
# # if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
# # vals['code'] = '101'+self.routing_id.code +self.env['ir.sequence'].next_by_code('sf.production.process.parameter')
# if vals.get('routing_id'):
# # vals['gain_way'] = '外协'
# routing_id = self.env['mrp.routing.workcenter'].browse(vals.get('routing_id'))
# if routing_id.surface_technics_id and not vals.get('process_id'):
# vals['process_id'] = routing_id.surface_technics_id.id
# if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
# vals['code'] = '101' + routing_id.code + self.env['ir.sequence'].next_by_code(
# 'sf.production.process.parameter')
# obj = super(SfProductionProcessParameter, self).create(vals)
# return obj
# def create_service_product(self):
# service_categ = self.env.ref(
# 'sf_dlm.product_category_surface_technics_sf').sudo()
#
# product_name = f"{self.process_id.name}_{self.name}"
# product_id = self.env['product.template'].search(
# [("name", '=', product_name)])
# if product_id:
# product_id.server_product_process_parameters_id = self.id
# else:
# res_partner = self.env['res.partner'].search([('name','=','湖南傲派自动化设备有限公司')])
# self.env['product.template'].create({
# 'detailed_type': 'service',
# 'name': product_name,
# 'invoice_policy': 'delivery',
# 'categ_id': service_categ.id,
# 'description': f"基于{self.name}创建的服务产品",
# 'sale_ok': True, # 可销售
# 'purchase_ok': True, # 可采购
# 'server_product_process_parameters_id': self.id,
# 'seller_ids': [(0, 0, {
# # 'delay': 1,
# 'partner_id': res_partner.id,
# 'price': 1, })],
# })
#
# def create_work_center(self):
# production_process_parameter = self
# if not production_process_parameter.process_id:
# return
# if not production_process_parameter.routing_id:
# workcenter_id = self.env['mrp.routing.workcenter'].search(
# [("surface_technics_id", '=', production_process_parameter.process_id.id)])
# if not workcenter_id:
# outsourcing_work_center = self.env['mrp.workcenter'].search(
# [("name", '=', '外协工作中心')])
# routing_id = self.env['mrp.routing.workcenter'].create({
# 'workcenter_ids': [(6, 0, outsourcing_work_center.ids)],
# 'routing_tag': 'special',
# 'routing_type': '表面工艺',
# 'is_outsource': True,
# 'surface_technics_id': production_process_parameter.process_id.id,
# 'name': production_process_parameter.process_id.name,
# })
# production_process_parameter.routing_id = routing_id.id
# else:
# production_process_parameter.routing_id = workcenter_id.id
#
# def init(self):
# super(SfProductionProcessParameter, self).init()
# # 在模块初始化时触发计算字段的更新
# records = self.search([])
# if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_process',
# default='False')):
# return
# for record in records:
# if not record.outsourced_service_products:
# record.create_service_product()
# record.create_work_center()
# self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_process', True)

View File

@@ -41,9 +41,7 @@
attrs="{'invisible': [('categ_type', 'not in', ['成品','坯料', '原材料'])],'readonly': [('id', '!=', False)]}"/>
<field name="server_product_process_parameters_id" string="工艺参数"
options="{'no_create': True}"
attrs="{'invisible': ['|',('detailed_type', '!=', 'service'),('detailed_type', '=', False)]}"
domain="[('active', '=', 'True'),('outsourced_service_products', '!=', 'True')]"
/>
attrs="{'invisible': ['|',('detailed_type', '!=', 'service'),('detailed_type', '=', False)]}"/>
<field name="cutting_tool_material_id" class="custom_required"
options="{'no_create': True}"
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}"

View File

@@ -48,7 +48,6 @@
'views/mrp_workorder_batch_replan.xml',
'views/purchase_order_view.xml',
'views/product_template_views.xml',
'views/stock_warehouse_orderpoint.xml',
],
'assets': {

View File

@@ -6,14 +6,12 @@ from datetime import datetime
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
from odoo import http
from odoo.http import request
from odoo.addons.sf_base.decorators.api_log import api_log
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('获取工单', requester='中控系统')
def get_Work_Info(self, **kw):
"""
自动化传递工单号获取工单信息
@@ -56,7 +54,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('获取日计划', requester='中控系统')
def get_ShiftPlan(self, **kw):
"""
自动化每天获取机台日计划
@@ -110,7 +107,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/QcCheck', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('工件预调(前置三元检测)', requester='中控系统')
def get_qcCheck(self, **kw):
"""
工件预调(前置三元检测)
@@ -153,7 +149,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('工单开始', requester='中控系统')
def button_Work_START(self, **kw):
"""
工单任务开始
@@ -203,7 +198,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('工单结束', requester='中控系统')
def button_Work_End(self, **kw):
"""
工单任务结束
@@ -255,7 +249,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('零件检测(后置三元检测)', requester='中控系统')
def PartQualityInspect(self, **kw):
"""
零件质检
@@ -302,7 +295,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('CMM测量程序下载', requester='中控系统')
def CMMProgDolod(self, **kw):
"""
中控系统传递RFID编号给MES获取测量程序文件。Ftp下载文件
@@ -343,7 +335,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('CAM加工程序下载', requester='中控系统')
def NCProgDolod(self, **kw):
"""
中控系统传递RFID编号给MES获取程序单及程序文件。Ftp下载文件
@@ -385,7 +376,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/LocationChange', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('库位变更', requester='中控系统')
def LocationChange(self, **kw):
"""
库位变更
@@ -490,7 +480,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('AGV运送上产线', requester='中控系统')
def AGVToProduct(self, **kw):
"""
AGV运送上产线完成
@@ -563,7 +552,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('AGV运送下产线', requester='中控系统')
def AGVDownProduct(self, **kw):
"""
MES调度AGV搬运零件AGV托盘到产线接驳站。

View File

@@ -18,4 +18,3 @@ from . import quick_easy_order
from . import purchase_order
from . import quality_check
from . import purchase_request_line
from . import stock_warehouse_orderpoint

View File

@@ -900,43 +900,40 @@ class MrpProduction(models.Model):
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
def _create_subcontract_purchase_request(self, purchase_request_line):
sorted_list = sorted(purchase_request_line, key=itemgetter('name'))
grouped_purchase_request_line = {
k: list(g)
for k, g in groupby(sorted_list, key=itemgetter('name'))
}
for name, request_line in grouped_purchase_request_line.items():
request_line_sorted_list = sorted(request_line, key=itemgetter('product_id'))
grouped_purchase_request_line_sorted_list = {
k: list(g)
for k, g in groupby(request_line_sorted_list, key=itemgetter('product_id'))
}
purchase_request_model = self.env["purchase.request"]
origin = ", ".join({item['production_name'] for item in request_line_sorted_list if item.get('production_name')})
pr = purchase_request_model.create({
"origin": origin,
"company_id": self.company_id.id,
"picking_type_id": self.env.ref('stock.picking_type_in').id,
"group_id": request_line[0].get('group_id'),
"requested_by": self.env.context.get("uid", self.env.uid),
"assigned_to": False,
"bom_id": self[0].bom_id.id,
"is_subcontract":True,
})
self[0].bom_id.bom_line_ids.product_id.route_ids = [(4,self.env.ref(
'sf_stock.stock_route_process_outsourcing').id)]
for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items():
cur_request_line = request_line_list[0]
if cur_request_line['product_qty'] == 1:
cur_request_line['product_qty'] = len(request_line_list)
# cur_request_line['product_qty'] = cur_request_line['product_qty']
cur_request_line['request_id'] = pr.id
cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')})
cur_request_line.pop('group_id', None)
cur_request_line.pop('production_name', None)
self.env["purchase.request.line"].create(cur_request_line)
pr.button_approved()
# def _create_subcontract_purchase_request(self, purchase_request_line):
# sorted_list = sorted(purchase_request_line, key=itemgetter('name'))
# grouped_purchase_request_line = {
# k: list(g)
# for k, g in groupby(sorted_list, key=itemgetter('name'))
# }
# for name, request_line in grouped_purchase_request_line.items():
# request_line_sorted_list = sorted(request_line, key=itemgetter('product_id'))
# grouped_purchase_request_line_sorted_list = {
# k: list(g)
# for k, g in groupby(request_line_sorted_list, key=itemgetter('product_id'))
# }
# purchase_request_model = self.env["purchase.request"]
# origin = ", ".join({item['production_name'] for item in request_line_sorted_list if item.get('production_name')})
# pr = purchase_request_model.create({
# "origin": origin,
# "company_id": self.company_id.id,
# "picking_type_id": self.env.ref('stock.picking_type_in').id,
# "group_id": request_line[0].get('group_id'),
# "requested_by": self.env.context.get("uid", self.env.uid),
# "assigned_to": False,
# "bom_id": self[0].bom_id.id,
# "is_subcontract":True,
# })
# self[0].bom_id.bom_line_ids.product_id.route_ids = [(4,self.env.ref(
# 'sf_stock.stock_route_process_outsourcing').id)]
# for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items():
# cur_request_line = request_line_list[0]
# cur_request_line['product_qty'] = len(request_line_list)
# cur_request_line['request_id'] = pr.id
# cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')})
# cur_request_line.pop('group_id', None)
# cur_request_line.pop('production_name', None)
# self.env["purchase.request.line"].create(cur_request_line)
# 外协出入库单处理
def get_subcontract_pick_purchase(self):
@@ -964,14 +961,14 @@ class MrpProduction(models.Model):
if not sorted_workorders:
return
for workorders in reversed(sorted_workorders):
# self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
# self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)
purchase_request_line = purchase_request_line + self.env['purchase.order'].get_purchase_request(
workorders, production)
all_workorders += workorders
self._create_subcontract_purchase_request(purchase_request_line)
for workorder in all_workorders:
workorder._compute_pr_mp_count()
self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)
# purchase_request_line = purchase_request_line + self.env['purchase.order'].get_purchase_request(
# workorders, production)
# all_workorders += workorders
# self._create_subcontract_purchase_request(purchase_request_line)
# for workorder in all_workorders:
# workorder._compute_pr_mp_count()
# 工单排序
def _reset_work_order_sequence1(self, k):
for rec in self:

View File

@@ -1,7 +1,7 @@
import logging
from odoo import fields, models, api
from odoo.exceptions import UserError
from odoo.tools import str2bool
# from odoo.tools import str2bool
class ResMrpRoutingWorkcenter(models.Model):
@@ -25,20 +25,20 @@ class ResMrpRoutingWorkcenter(models.Model):
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
bom_id = fields.Many2one('mrp.bom', required=False)
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
optional_process_parameters = fields.One2many('sf.production.process.parameter','routing_id',string='可选工艺参数')
# optional_process_parameters = fields.One2many('sf.production.process.parameter','routing_id',string='可选工艺参数')
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
is_outsource = fields.Boolean('外协', default=False)
individuation_page_ids = fields.Many2many('sf.work.individuation.page', string='个性化记录')
@api.onchange('surface_technics_id')
def optional_process_parameters_date(self):
for record in self:
if not record.surface_technics_id:
continue
parameter_ids = self.env['sf.production.process.parameter'].search([
('process_id', '=', record.surface_technics_id.id),
])
record.optional_process_parameters = parameter_ids.ids
# @api.onchange('surface_technics_id')
# def optional_process_parameters_date(self):
# for record in self:
# if not record.surface_technics_id:
# continue
# parameter_ids = self.env['sf.production.process.parameter'].search([
# ('process_id', '=', record.surface_technics_id.id),
# ])
# record.optional_process_parameters = parameter_ids.ids
# @api.model
# def _auto_init(self):

View File

@@ -21,16 +21,16 @@ class ResWorkcenter(models.Model):
related='equipment_id.production_line_id', store=True)
is_process_outsourcing = fields.Boolean('工艺外协')
users_ids = fields.Many2many("res.users", 'users_workcenter', tracking=True)
@api.constrains('name')
def _check_unique_name_code(self):
for record in self:
# 检查是否已经存在相同的 name 和 code 组合
existing = self.search([
('name', '=', record.name),
('id', '!=', record.id) # 排除当前记录
])
if existing:
raise ValueError('记录已存在')
# @api.constrains('name')
# def _check_unique_name_code(self):
# for record in self:
# # 检查是否已经存在相同的 name 和 code 组合
# existing = self.search([
# ('name', '=', record.name),
# ('id', '!=', record.id) # 排除当前记录
# ])
# if existing:
# raise ValueError('记录已存在')
def write(self, vals):
if 'users_ids' in vals:
old_users = self.users_ids

View File

@@ -70,22 +70,21 @@ class ResMrpWorkOrder(models.Model):
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效',
tracking=True)
back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True)
pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True)
@api.depends('state')
def _compute_pr_mp_count(self):
for item in self:
if not item.is_subcontract:
item.pr_mp_count = 0
continue
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'),
('state', '!=', 'rejected')])
if pr_ids:
item.pr_mp_count = len(pr_ids)
else:
item.pr_mp_count = 0
# pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True)
#
# @api.depends('state')
# def _compute_pr_mp_count(self):
# for item in self:
# if not item.is_subcontract:
# item.pr_mp_count = 0
# continue
# pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'),
# ('state', '!=', 'rejected')])
# if pr_ids:
# item.pr_mp_count = len(pr_ids)
# else:
# item.pr_mp_count = 0
@api.depends('state')
def _compute_back_button_display(self):
for record in self:
@@ -443,39 +442,49 @@ class ResMrpWorkOrder(models.Model):
@api.depends('state', 'production_id.name')
def _compute_surface_technics_purchase_ids(self):
for order in self:
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
('state', '!=', 'rejected')])
if pr_ids.purchase_count:
order.surface_technics_purchase_count = pr_ids.purchase_count
if order.routing_type == '表面工艺' and order.state not in ['cancel']:
domain = [('purchase_type', '=', 'consignment'),
('origin', 'like', '%' + self.production_id.name + '%'),
('state', '!=', 'cancel')]
# domain = [('purchase_type', '=', 'consignment'),
# ('origin', 'like', '%' + self.production_id.name + '%'),
# ('state', '!=', 'cancel')]
purchase = self.env['purchase.order'].search(domain)
order.surface_technics_purchase_count = 0
if not purchase:
order.surface_technics_purchase_count = 0
for po in purchase:
if any(
line.product_id and line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id
for line in po.order_line):
order.surface_technics_purchase_count = 1
else:
order.surface_technics_purchase_count = 0
def action_view_pr_mrp_workorder(self):
"""
采购请求
"""
self.ensure_one()
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
('state', '!=', 'rejected')])
action = {
'res_model': 'purchase.request',
'type': 'ir.actions.act_window',
}
if len(pr_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': pr_ids[0].id,
})
else:
action.update({
'name': _("%s生成采购请求单", self.name),
'domain': [('id', 'in', pr_ids)],
'view_mode': 'tree,form',
})
return action
# def action_view_pr_mrp_workorder(self):
# """
# 采购请求
# """
# self.ensure_one()
# pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
# ('state', '!=', 'rejected')])
# action = {
# 'res_model': 'purchase.request',
# 'type': 'ir.actions.act_window',
# }
# if len(pr_ids) == 1:
# action.update({
# 'view_mode': 'form',
# 'res_id': pr_ids[0].id,
# })
# else:
# action.update({
# 'name': _("从 %s生成采购请求单", self.name),
# 'domain': [('id', 'in', pr_ids)],
# 'view_mode': 'tree,form',
# })
# return action
def action_view_surface_technics_purchase(self):
self.ensure_one()
# if self.routing_type == '表面工艺':
@@ -492,23 +501,19 @@ class ResMrpWorkOrder(models.Model):
# if technology_design.is_auto is False:
# domain = [('origin', '=', self.production_id.name)]
# else:
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
('state', '!=', 'rejected')])
# purchase_orders_id = self._get_surface_technics_purchase_ids()
# result = {
# "type": "ir.actions.act_window",
# "res_model": "purchase.order",
# "res_id": purchase_orders_id.id,
# # "domain": [['id', 'in', self.purchase_id]],
# "name": _("Purchase Orders"),
# 'view_mode': 'form',
# }
return pr_ids.action_view_purchase_order()
purchase_orders_id = self._get_surface_technics_purchase_ids()
result = {
"type": "ir.actions.act_window",
"res_model": "purchase.order",
"res_id": purchase_orders_id.id,
# "domain": [['id', 'in', self.purchase_id]],
"name": _("Purchase Orders"),
'view_mode': 'form',
}
return result
def _get_surface_technics_purchase_ids(self):
domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'),
('state', '!=', 'cancel')]
domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')]
# domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment')]
# domain = [('group_id', '=', self.production_id.procurement_group_id.id), ('purchase_type', '=', 'consignment')]
purchase_orders = self.env['purchase.order'].search(domain, order='id desc')
@@ -1238,13 +1243,6 @@ class ResMrpWorkOrder(models.Model):
}]
return workorders_values_str
def check_lot_exists(self, picking_id, lot_id):
return bool(
picking_id.move_ids.move_line_ids.filtered(
lambda line: line.lot_id.id == lot_id
)
)
def _process_compute_state(self):
sorted_workorders = sorted(self, key=lambda x: x.sequence)
for workorder in sorted_workorders:
@@ -1266,37 +1264,20 @@ class ResMrpWorkOrder(models.Model):
workorder.state = 'pending'
continue
# ================= 如果制造订单制造类型为【人工线下加工】==========================
# lot_id = workorder.production_id.move_raw_ids.move_line_ids.lot_id
# picking_ids = workorder.production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# exists = any(
# move_line.lot_id == lot_id
# for picking in picking_ids
# for move in picking.move_ids
# for move_line in move.move_line_ids
# )
if (workorder.production_id.production_type == '人工线下加工'
and workorder.production_id.programming_state == '编程'):
and workorder.production_id.schedule_state == ''
and len(workorder.production_id.picking_ids.filtered(
lambda w: w.state not in ['done', 'cancel'])) == 0):
# and workorder.production_id.programming_state == '已编程'
if workorder.is_subcontract is True:
if workorder.production_id.state == 'rework':
workorder.state = 'waiting'
continue
purchase_orders_id = self._get_surface_technics_purchase_ids()
purchase_count = 0
for purchase_order in purchase_orders_id:
for purchase_order_line in purchase_order.order_line:
if purchase_order_line.product_id.server_product_process_parameters_id.id == workorder.surface_technics_parameters_id.id:
purchase_count = purchase_order_line.product_qty
if purchase_orders_id.state == 'purchase' and purchase_count>=workorder.production_id.product_qty:
if purchase_orders_id.state == 'purchase':
workorder.state = 'ready'
picking_id = workorder.production_id.picking_ids.filtered(
lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
move_out = picking_id.move_ids
# move_out = workorder.move_subcontract_workorder_ids[1]
move_out = workorder.move_subcontract_workorder_ids[1]
for mo in move_out:
if workorder.production_id.bom_id.bom_line_ids.product_id.id != mo.product_id.id:
continue
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids:
@@ -1312,7 +1293,6 @@ class ResMrpWorkOrder(models.Model):
else:
workorder.state = 'ready'
continue
continue
# ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]==========================
if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework'
or workorder.production_id.schedule_state != '已排'
@@ -1328,18 +1308,15 @@ class ResMrpWorkOrder(models.Model):
if workorder.is_subcontract is False:
workorder.state = 'ready'
else:
if workorder.production_id.programming_state == '已编程':
if len(workorder.production_id.picking_ids.filtered(
lambda w: w.state not in ['done',
'cancel'])) == 0 and workorder.production_id.programming_state == '已编程':
purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id:
if purchase_orders_id.state == 'purchase':
workorder.state = 'ready'
picking_id = workorder.production_id.picking_ids.filtered(
lambda
wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
move_out = picking_id.move_ids
move_out = workorder.move_subcontract_workorder_ids[1]
for mo in move_out:
if workorder.production_id.bom_id.bom_line_ids.product_id.id != mo.product_id.id:
continue
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids:
@@ -1348,6 +1325,7 @@ class ResMrpWorkOrder(models.Model):
else:
workorder.state = 'waiting'
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
'production_id.programming_state')
@@ -1379,8 +1357,7 @@ class ResMrpWorkOrder(models.Model):
# 判断是否有坯料的序列号信息
boolean = False
if self.production_id.move_raw_ids:
if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料' and \
self.production_id.move_raw_ids[0].product_id.tracking == 'serial':
if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料' and self.production_id.move_raw_ids[0].product_id.tracking == 'serial':
if self.production_id.move_raw_ids[0].move_line_ids:
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
boolean = True
@@ -1388,16 +1365,7 @@ class ResMrpWorkOrder(models.Model):
boolean = True
if not boolean:
raise UserError('制造订单【%s】缺少组件的序列号信息!' % self.production_id.name)
self.pro_code = False # 默认值
if (
self.production_id
and self.production_id.move_raw_ids
and len(self.production_id.move_raw_ids) > 0
and self.production_id.move_raw_ids[0].move_line_ids
and len(self.production_id.move_raw_ids[0].move_line_ids) > 0
and self.production_id.move_raw_ids[0].move_line_ids[0].lot_id
):
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name
# cnc校验
if self.production_id.production_type == '自动化产线加工':
cnc_workorder = self.search(
@@ -1421,10 +1389,7 @@ class ResMrpWorkOrder(models.Model):
# 表面工艺外协出库单
if self.routing_type == '表面工艺':
if self.is_subcontract is True:
picking_id = self.production_id.picking_ids.filtered(
lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
move_out = picking_id.move_ids
# move_out = self.move_subcontract_workorder_ids[1]
move_out = self.move_subcontract_workorder_ids[1]
# move_out = self.env['stock.move'].search(
# [('location_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
@@ -1432,8 +1397,6 @@ class ResMrpWorkOrder(models.Model):
# [('barcode', 'ilike', 'VL-SPOC')]).id),
# ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])])
for mo in move_out:
if self.production_id.bom_id.bom_line_ids.product_id.id != mo.product_id.id:
continue
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids:
@@ -1566,8 +1529,7 @@ class ResMrpWorkOrder(models.Model):
lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
if self == subcontract_workorders[-1]:
# 给下一个库存移动就绪
if self.move_subcontract_workorder_ids:
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
# self.production_id.button_mark_done()
tem_date_planned_finished = record.date_planned_finished
tem_date_finished = record.date_finished

View File

@@ -59,88 +59,6 @@ class PurchaseOrder(models.Model):
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
purchase.production_count = len(production_id)
def process_replenish(self,production,total_qty):
record = self
bom_line_id = production.bom_id.bom_line_ids
replenish = self.env['stock.warehouse.orderpoint'].search([
('product_id', '=', bom_line_id.product_id.id),
(
'location_id', '=', self.env.ref('sf_stock.stock_location_outsourcing_material_receiving_area').id),
# ('state', 'in', ['draft', 'confirmed'])
], limit=1)
if not replenish:
replenish_model = self.env['stock.warehouse.orderpoint']
replenish = replenish_model.create({
'product_id': bom_line_id.product_id.id,
'location_id': self.env.ref(
'sf_stock.stock_location_outsourcing_material_receiving_area').id,
'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
'group_id': record.group_id.id,
'qty_to_order': total_qty,
'origin': record.name,
})
else:
replenish.write({
'product_id': bom_line_id.product_id.id,
'location_id': self.env.ref(
'sf_stock.stock_location_outsourcing_material_receiving_area').id,
'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
'group_id': record.group_id.id,
'qty_to_order': total_qty + replenish.qty_to_order,
'origin': record.name + ',' + replenish.origin,
})
replenish.action_replenish()
def outsourcing_service_replenishment(self):
record = self
if record.purchase_type != 'consignment':
return
grouped_lines = {}
for line in record.order_line:
if line.related_product.id not in grouped_lines:
grouped_lines[line.related_product.id] = []
grouped_lines[line.related_product.id].append(line)
for product_id,lines in grouped_lines.items():
production = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
if not production:
continue
total_qty = sum(line.product_qty for line in lines)
record.process_replenish(production,total_qty)
for product_id,lines in grouped_lines.items():
productions = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
if not productions:
continue
# production.bom_id.bom_line_ids.product_id
location_id = self.env['stock.location'].search([('name', '=', '制造前')])
quants = self.env['stock.quant'].search([
('product_id', '=', productions.bom_id.bom_line_ids.product_id.id),
('location_id', '=', location_id.id)
])
total_qty = sum(quants.mapped('quantity')) # 计算该位置的总库存量
is_available = total_qty > 0
if not is_available:
raise UserError('请先完成坯料入库')
for production_id in productions:
work_ids = production_id.workorder_ids.filtered(
lambda wk: wk.state not in ['done', 'rework', 'cancel'])
if not work_ids:
continue
min_sequence_wk = min(work_ids, key=lambda wk: wk.sequence)
if min_sequence_wk.is_subcontract and min_sequence_wk.state == 'ready':
picking_id = production_id.picking_ids.filtered(
lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
move_out = picking_id.move_ids
for mo in move_out:
if production_id.bom_id.bom_line_ids.product_id.id != mo.product_id.id:
continue
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids:
self.env['stock.move.line'].create(
mo.get_move_line(production_id, min_sequence_wk))
# product = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
# match = re.search(r'(S\d{5}-\d)',product.name)
# pass
def button_confirm(self):
for record in self:
for line in record.order_line:
@@ -148,14 +66,39 @@ class PurchaseOrder(models.Model):
raise UserError('请对【产品】中的【数量】进行输入')
if line.price_unit <= 0:
raise UserError('请对【产品】中的【单价】进行输入')
record.outsourcing_service_replenishment()
# if record.purchase_type == 'consignment':
# bom_line_id = record.order_line[0].purchase_request_lines.request_id.bom_id.bom_line_ids
# replenish = self.env['stock.warehouse.orderpoint'].search([
# ('product_id', '=', bom_line_id.product_id.id),
# (
# 'location_id', '=', self.env.ref('sf_stock.stock_location_outsourcing_material_receiving_area').id),
# # ('state', 'in', ['draft', 'confirmed'])
# ], limit=1)
# if not replenish:
# replenish_model = self.env['stock.warehouse.orderpoint']
# replenish = replenish_model.create({
# 'product_id': bom_line_id.product_id.id,
# 'location_id': self.env.ref(
# 'sf_stock.stock_location_outsourcing_material_receiving_area').id,
# 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
# 'group_id': record.group_id.id,
# 'qty_to_order': 1,
# 'origin': record.name,
# })
# else:
# replenish.write({
# 'product_id': bom_line_id.product_id.id,
# 'location_id': self.env.ref(
# 'sf_stock.stock_location_outsourcing_material_receiving_area').id,
# 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
# 'group_id': record.group_id.id,
# 'qty_to_order': 1 + replenish.qty_to_order,
# 'origin': record.name + ',' + replenish.origin,
# })
# replenish.action_replenish()
res = super(PurchaseOrder, self).button_confirm()
for line in self.order_line:
# 将产品不追踪序列号的行项目设置qty_done
if not line.move_ids:
continue
if line.move_ids and line.move_ids[0].product_id.tracking == 'none':
line.move_ids[0].quantity_done = line.move_ids[0].product_qty
return res
@@ -228,24 +171,6 @@ class PurchaseOrderLine(models.Model):
)
record.part_number = filtered_order_line.product_id.part_number
record.part_name = filtered_order_line.product_id.part_name
elif record.order_id.purchase_type == 'consignment':
product_name = ''
match = re.search(r'(S\d{5}-\d)', record.related_product.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
sale_order_name = ''
match_sale = re.search(r'S(\d+)', record.related_product.name)
if match_sale:
sale_order_name = match_sale.group(0)
sale_order = self.env['sale.order'].sudo().search(
[('name', '=', sale_order_name)])
if sale_order:
filtered_order_line = sale_order.order_line.filtered(
lambda order_line: re.search(f'{product_name}$', order_line.product_id.name)
)
record.part_number = filtered_order_line.product_id.part_number
record.part_name = filtered_order_line.product_id.part_name
else:
record.part_number = record.product_id.part_number
record.part_name = record.product_id.part_name

View File

@@ -1,30 +1,30 @@
# -*- coding: utf-8 -*-
import base64
import datetime
import logging
import json
import os
import re
import traceback
from operator import itemgetter
import requests
from itertools import groupby
from collections import defaultdict, namedtuple
from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request'
is_subcontract = fields.Boolean(string='是否外协',default=False)
class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request.line'
is_subcontract = fields.Boolean(string='是否外协')
class PurchaseRequest(models.Model):
_inherit = 'purchase.request'
bom_id = fields.Many2one('mrp.bom')
# # -*- coding: utf-8 -*-
# import base64
# import datetime
# import logging
# import json
# import os
# import re
# import traceback
# from operator import itemgetter
#
# import requests
# from itertools import groupby
# from collections import defaultdict, namedtuple
#
# from odoo import api, fields, models, SUPERUSER_ID, _
# from odoo.exceptions import UserError, ValidationError
# from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
#
#
# class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request'
# is_subcontract = fields.Boolean(string='是否外协',default=False)
# class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request.line'
# is_subcontract = fields.Boolean(string='是否外协')
#
#
# class PurchaseRequest(models.Model):
# _inherit = 'purchase.request'
# bom_id = fields.Many2one('mrp.bom')

View File

@@ -76,7 +76,7 @@ class SaleOrder(models.Model):
'embryo_redundancy_id': line.embryo_redundancy_id,
}
product_name = ''
match = re.search(r'(S\d{5}-\d*)', product.name)
match = re.search(r'(S\d{5}-\d)', product.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)

View File

@@ -7,73 +7,74 @@ from odoo.exceptions import UserError, ValidationError
class SfProductionProcessParameter(models.Model):
_inherit = 'sf.production.process.parameter'
service_products = fields.Many2one(
'product.template',
string='外协服务产品',compute='_compute_service_products',inverse='_inverse_service_products',
store=True
)
outsourced_service_products = fields.One2many(
'product.template', # 另一个模型的名称
'server_product_process_parameters_id', # 对应的 Many2one 字段名称
string='外协服务产品'
)
is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False)
is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False)
routing_id = fields.Many2one('mrp.routing.workcenter', string="工序")
def _inverse_service_products(self):
for record in self:
if record.service_products:
# 确保关联关系正确
record.outsourced_service_products = record.service_products.ids if record.service_products else False
else:
record.outsourced_service_products = False
@api.depends('outsourced_service_products')
def _compute_service_products(self):
for record in self:
# 假设取第一条作为主明细
record.service_products = record.outsourced_service_products[0].id if record.outsourced_service_products else False
def name_get(self):
result = []
for record in self:
name = f"{record.process_id.name} - {record.name}" # 自定义显示格式
result.append((record.id, name))
return result
@api.constrains('outsourced_service_products')
def _validate_partner_limit(self):
for record in self:
if len(record.outsourced_service_products) > 1:
raise ValidationError("工艺参数不能与多个产品关联")
@api.onchange('outsourced_service_products')
def _onchange_validate_partner_limit(self):
for record in self:
if len(record.outsourced_service_products) > 1:
raise ValidationError("工艺参数不能与多个产品关联")
@api.depends('outsourced_service_products')
def _compute_is_product_button(self):
for record in self:
if record.outsourced_service_products:
record.is_product_button = True
else:
record.is_product_button = False
def has_wksp_prefix(self):
"""
判断字符串是否以WKSP开头不区分大小写
:param text: 要检查的字符串
:return: True/False
"""
return self.code.upper().startswith('101'+self.routing_id.code)
@api.depends('outsourced_service_products','code')
def _compute_is_delete_button(self):
for record in self:
if record.outsourced_service_products and record.has_wksp_prefix():
record.is_delete_button = False
elif record.outsourced_service_products:
record.is_delete_button = True
else:
record.is_delete_button = True
# service_products = fields.Many2one(
# 'product.template',
# string='外协服务产品',compute='_compute_service_products',inverse='_inverse_service_products',
# store=True
# )
# outsourced_service_products = fields.One2many(
# 'product.template', # 另一个模型的名称
# 'server_product_process_parameters_id', # 对应的 Many2one 字段名称
# string='外协服务产品'
# )
# is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False)
# is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False)
# routing_id = fields.Many2one('mrp.routing.workcenter', string="工序")
#
# @api.depends('outsourced_service_products')
# def _compute_service_products(self):
# for record in self:
# # 假设取第一条作为主明细
# record.service_products = record.outsourced_service_products.id if record.outsourced_service_products else False
#
# def _inverse_service_products(self):
# for record in self:
# if record.service_products:
# # 确保关联关系正确
# record.outsourced_service_products = record.service_products.ids if record.service_products else False
# else:
# record.outsourced_service_products = False
# def name_get(self):
# result = []
# for record in self:
# name = f"{record.process_id.name} - {record.name}" # 自定义显示格式
# result.append((record.id, name))
# return result
# @api.constrains('outsourced_service_products')
# def _validate_partner_limit(self):
# for record in self:
# if len(record.outsourced_service_products) > 1:
# raise ValidationError("工艺参数不能与多个产品关联")
#
# @api.onchange('outsourced_service_products')
# def _onchange_validate_partner_limit(self):
# for record in self:
# if len(record.outsourced_service_products) > 1:
# raise ValidationError("工艺参数不能与多个产品关联")
# @api.depends('outsourced_service_products')
# def _compute_is_product_button(self):
# for record in self:
# if record.outsourced_service_products:
# record.is_product_button = True
# else:
# record.is_product_button = False
#
# def has_wksp_prefix(self):
# """
# 判断字符串是否以WKSP开头不区分大小写
# :param text: 要检查的字符串
# :return: True/False
# """
# return self.code.upper().startswith('101'+self.routing_id.code)
# @api.depends('outsourced_service_products','code')
# def _compute_is_delete_button(self):
# for record in self:
# if record.outsourced_service_products and record.has_wksp_prefix():
# record.is_delete_button = False
# elif record.outsourced_service_products:
# record.is_delete_button = True
# else:
# record.is_delete_button = True
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if self._context.get('route_id'):
@@ -89,19 +90,19 @@ class SfProductionProcessParameter(models.Model):
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid)
def action_create_service_product(self):
if self.id: # 如果是已存在的记录
self.write({}) # 空写入会触发保存
else: # 如果是新记录
self = self.create(self._convert_to_write(self.read()[0]))
return {
'type': 'ir.actions.act_window',
'name': '向导名称',
'res_model': 'product.creation.wizard',
'view_mode': 'form',
'target': 'new',
'context': {'default_process_parameter_id': self.id}, # 传递当前记录ID
}
# def action_create_service_product(self):
# if self.id: # 如果是已存在的记录
# self.write({}) # 空写入会触发保存
# else: # 如果是新记录
# self = self.create(self._convert_to_write(self.read()[0]))
# return {
# 'type': 'ir.actions.act_window',
# 'name': '向导名称',
# 'res_model': 'product.creation.wizard',
# 'view_mode': 'form',
# 'target': 'new',
# 'context': {'default_process_parameter_id': self.id}, # 传递当前记录ID
# }
#
# return {
# 'name': '创建服务产品',
@@ -115,6 +116,6 @@ class SfProductionProcessParameter(models.Model):
# },
# }
def action_hide_service_products(self):
# self.outsourced_service_products.active = False
self.active = False
# def action_hide_service_products(self):
# # self.outsourced_service_products.active = False
# self.active = False

View File

@@ -631,62 +631,28 @@ class StockPicking(models.Model):
move.action_clear_lines_show_details()
move.action_show_details()
res = super().button_validate()
lot_ids = None
product_ids = self.move_ids.mapped('product_id')
if not self.move_ids[0].product_id.single_manufacturing and self.move_ids[0].product_id.tracking == 'none':
lot_ids = self.move_ids.move_line_ids.mapped('lot_id')
production_ids = self.sale_order_id.mrp_production_ids if self.sale_order_id else self.env['mrp.production']
if res and self.location_id.name == '外协收料区' and self.location_dest_id.name == '制造前':
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
if res is True and self.picking_type_id.id == picking_type_in:
# 如果是最后一张外协入库单,则设置库存位置的预留数量
for production_id in production_ids:
if lot_ids:
lot_id = production_id.move_raw_ids.move_line_ids.lot_id
# picking_ids = production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
if lot_id in lot_ids:
workorder_id = production_id.workorder_ids.filtered(
lambda a: a.state == 'progress' and a.is_subcontract)
if not workorder_id:
continue
workorder_id.button_finish()
else:
workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'progress' and a.is_subcontract)
if not workorder_id:
continue
workorder_id.button_finish()
# lot_id = workorder.production_id.move_raw_ids.move_line_ids.lot_id
# picking_ids = workorder.production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# if move_in:
# workorder = move_in.subcontract_workorder_id
# workorders = workorder.production_id.workorder_ids
# subcontract_workorders = workorders.filtered(
# lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
# # if workorder == subcontract_workorders[-1]:
# # self.env['stock.quant']._update_reserved_quantity(
# # move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
# # lot_id=move_in.move_line_ids.lot_id,
# # package_id=False, owner_id=False, strict=False
# # )
# workorder.button_finish()
if res and self.location_id.name == '制造前' and self.location_dest_id.name == '外协加工区':
for production_id in production_ids:
if lot_ids:
lot_id = production_id.move_raw_ids.move_line_ids.lot_id
# picking_ids = production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
if lot_id in lot_ids:
workorder_id = production_id.workorder_ids.filtered(
lambda a: a.state == 'progress' and a.is_subcontract)
if not workorder_id:
continue
workorder_id.button_finish()
else:
workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'ready' and a.is_subcontract)
if not workorder_id:
continue
workorder_id.button_start()
move_in = self.move_ids
if move_in:
workorder = move_in.subcontract_workorder_id
workorders = workorder.production_id.workorder_ids
subcontract_workorders = workorders.filtered(
lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
# if workorder == subcontract_workorders[-1]:
# self.env['stock.quant']._update_reserved_quantity(
# move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
# lot_id=move_in.move_line_ids.lot_id,
# package_id=False, owner_id=False, strict=False
# )
workorder.button_finish()
picking_type_out = self.env.ref('sf_manufacturing.outcontract_picking_out').id
if res and self.picking_type_id.id == picking_type_out:
move_out = self.move_ids
if move_out:
workorder = move_out.subcontract_workorder_id
workorder.button_start()
if self.location_id.name == '成品存货区' and self.location_dest_id.name == '客户':
sale_id = self.env['sale.order'].sudo().search(
[('name', '=', self.origin)])
@@ -930,8 +896,6 @@ class ReStockMove(models.Model):
}
def get_move_line(self, production_id, sorted_workorders):
# if not self.move_ids[0].product_id.single_manufacturing and self.move_ids[0].product_id.tracking == 'none':
qty = production_id.product_qty
return {
'move_id': self.id,
'product_id': self.product_id.id,
@@ -939,7 +903,7 @@ class ReStockMove(models.Model):
'location_id': self.picking_id.location_id.id,
'location_dest_id': self.picking_id.location_dest_id.id,
'picking_id': self.picking_id.id,
'reserved_uom_qty': qty,
'reserved_uom_qty': self.product_uom_qty,
'lot_id': production_id.move_line_raw_ids.lot_id.id,
'company_id': self.env.company.id,
# 'workorder_id': '' if not sorted_workorders else sorted_workorders.id,

View File

@@ -1,11 +0,0 @@
# -*- coding: utf-8 -*-
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
import base64
from io import BytesIO
from odoo import api, fields, models, SUPERUSER_ID, _
class StockWarehouseOrderpoint(models.Model):
_inherit = 'stock.warehouse.orderpoint'
origin = fields.Char(string='来源')
_order = 'create_date DESC'

View File

@@ -383,7 +383,7 @@
<field name="process_parameters_id"
attrs="{'readonly': [('id', '!=', False),('routing_tag', '=', 'standard')]}"
string="参数" context="{'route_id':route_id,'production_id': production_id}"
options="{'no_create': True}" domain="[('routing_id', '=', 'route_id')]"/>
options="{'no_create': True}"/>
<field name="panel" readonly="1"/>
<field name="routing_tag" readonly="1" widget="badge"
decoration-success="routing_tag == 'standard'"

View File

@@ -22,26 +22,26 @@
<field name="is_repeat"/>
<field name="reserved_duration"/>
</field>
<xpath expr="//notebook/page[1]" position="before">
<page string="可选工艺参数">
<field name="optional_process_parameters">
<tree editable="bottom">
<field name="is_product_button" invisible="1"/>
<field name="is_delete_button" invisible="1"/>
<field name="code" attrs="{'readonly': True}"/>
<field name="name" required="1"/>
<field name="service_products" domain="[('detailed_type', '=', 'service'),('server_product_process_parameters_id', '=', False)]"/>
<!-- 按钮列 -->
<button name="action_create_service_product" string="创建服务产品" type="object"
class="btn-primary"
attrs="{'invisible': [('is_product_button', '=', True)]}" context="{'default_process_parameter_id':id}"/>
<button name="action_hide_service_products" string="删除" type="object"
class="oe_highlight"
attrs="{'invisible': [('is_delete_button', '=', True)]}"/>
</tree>
</field>
</page>
</xpath>
<!-- <xpath expr="//notebook/page[1]" position="before">-->
<!-- <page string="可选工艺参数">-->
<!-- <field name="optional_process_parameters">-->
<!-- <tree editable="bottom">-->
<!-- <field name="is_product_button" invisible="1"/>-->
<!-- <field name="is_delete_button" invisible="1"/>-->
<!-- <field name="code" attrs="{'readonly': True}"/>-->
<!-- <field name="name" required="1"/>-->
<!-- <field name="service_products" domain="[('detailed_type', '=', 'service'),('server_product_process_parameters_id', '=', False)]"/>-->
<!-- &lt;!&ndash; 按钮列 &ndash;&gt;-->
<!-- <button name="action_create_service_product" string="创建服务产品" type="object"-->
<!-- class="btn-primary"-->
<!-- attrs="{'invisible': [('is_product_button', '=', True)]}" context="{'default_process_parameter_id':id}"/>-->
<!-- <button name="action_hide_service_products" string="删除" type="object"-->
<!-- class="oe_highlight"-->
<!-- attrs="{'invisible': [('is_delete_button', '=', True)]}"/>-->
<!-- </tree>-->
<!-- </field>-->
<!-- </page>-->
<!-- </xpath>-->
</field>
</record>
</data>

View File

@@ -144,17 +144,17 @@
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button type="object" name="action_view_pr_mrp_workorder" class="oe_stat_button"
icon="fa-credit-card"
groups="base.group_user,sf_base.group_sf_order_user"
attrs="{'invisible': [('pr_mp_count', '=', 0)]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="pr_mp_count"/>
</span>
<span class="o_stat_text">采购申请</span>
</div>
</button>
<!-- <button type="object" name="action_view_pr_mrp_workorder" class="oe_stat_button"-->
<!-- icon="fa-credit-card"-->
<!-- groups="base.group_user,sf_base.group_sf_order_user"-->
<!-- attrs="{'invisible': [('pr_mp_count', '=', 0)]}">-->
<!-- <div class="o_field_widget o_stat_info">-->
<!-- <span class="o_stat_value">-->
<!-- <field name="pr_mp_count"/>-->
<!-- </span>-->
<!-- <span class="o_stat_text">采购申请</span>-->
<!-- </div>-->
<!-- </button>-->
<button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button"
icon="fa-credit-card"
groups="base.group_user,sf_base.group_sf_order_user"

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_warehouse_orderpoint_tree_editable_inherit" model="ir.ui.view">
<field name="name">补货</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_tree_editable"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='qty_to_order']" position="after">
<field name="origin"/>
</xpath>
</field>
</record>
<!-- 继承补货单的搜索视图 -->
</data>
</odoo>

View File

@@ -77,11 +77,11 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
if workorders[
0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorders[0].state = 'waiting'
pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', item.name), ('is_subcontract', '=', 'True'), ('state', '!=', 'rejected')])
if not pr_ids:
continue
if not all(pr.state == 'draft' for pr in pr_ids):
# 如果发现有记录的 state 不是 'draft',抛出异常
raise UserError("有采购申请的状态不是 '草稿'")
pr_ids.state = 'rejected'
# pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', item.name), ('is_subcontract', '=', 'True'), ('state', '!=', 'rejected')])
# if not pr_ids:
# continue
# if not all(pr.state == 'draft' for pr in pr_ids):
# # 如果发现有记录的 state 不是 'draft',抛出异常
# raise UserError("有采购申请的状态不是 '草稿'")
# pr_ids.state = 'rejected'

View File

@@ -3,9 +3,6 @@ import logging
import os
import json
import base64
import traceback
from odoo import http, fields, models
from odoo.http import request
from odoo.addons.sf_base.controllers.controllers import MultiInheritController
@@ -276,8 +273,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
except Exception as e:
res = {'status': -1, 'message': '系统解析失败'}
request.cr.rollback()
traceback_error = traceback.format_exc()
logging.error("get_cnc_processing_create error:%s" % traceback_error)
logging.info('get_cnc_processing_create error:%s' % e)
return json.JSONEncoder().encode(res)

View File

@@ -1149,7 +1149,7 @@ class sfProductionProcessParameter(models.Model):
'processing_mm': item['processing_mm'],
'gain_way':'外协',
})
production_process_parameter.create_service_product()
# production_process_parameter.create_service_product()
else:
production_process_parameter.gain_way = '外协'
production_process_parameter.name = item['name']
@@ -1161,9 +1161,9 @@ class sfProductionProcessParameter(models.Model):
[('materials_no', 'in', item['materials_model_ids_codes'])])
production_process_parameter.active = item['active']
production_process_parameter.processing_mm = item['processing_mm']
if not production_process_parameter.outsourced_service_products:
production_process_parameter.create_service_product()
production_process_parameter.create_work_center()
# if not production_process_parameter.outsourced_service_products:
# production_process_parameter.create_service_product()
# production_process_parameter.create_work_center()
else:
raise ValidationError("表面工艺可选参数认证未通过")

View File

@@ -126,15 +126,7 @@ class QualityCheck(models.Model):
# todo 需修改
val = ['0037818516']
logging.info('获取到的工单信息%s' % val)
# r = requests.post(crea_url, json=val, headers=headers)
r = self.env['api.request.log'].log_request(
'get',
crea_url,
name='零件特采',
responser='中控系统',
json=val,
headers=headers
)
r = requests.post(crea_url, json=val, headers=headers)
ret = r.json()
logging.info('_register_quality_check:%s' % ret)
if ret['Succeed']:

View File

@@ -346,10 +346,10 @@ class RePurchaseOrder(models.Model):
if purchase.order_line[0].product_id.categ_id.name == '坯料':
if purchase.order_line[0].product_id.materials_type_id.gain_way == '外协':
purchase.purchase_type = 'outsourcing'
request_lines = self.order_line.mapped('purchase_request_lines')
# 检查是否存在 is_subcontract 为 True 的行
if any(line.is_subcontract for line in request_lines):
purchase.purchase_type = 'consignment'
# request_lines = self.order_line.mapped('purchase_request_lines')
# # 检查是否存在 is_subcontract 为 True 的行
# if any(line.is_subcontract for line in request_lines):
# purchase.purchase_type = 'consignment'
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '预警'), ('overdue', '已逾期')],
@@ -384,28 +384,27 @@ class RePurchaseOrder(models.Model):
if not line.taxes_id:
raise UserError('请对【产品】中的【税】进行选择')
def get_purchase_request(self, consecutive_process_parameters, production):
result = []
for pp in consecutive_process_parameters:
server_template = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', pp.surface_technics_parameters_id.id),
('detailed_type', '=', 'service')])
# route_ids
result.append({
"product_id": server_template.product_variant_id.id,
'related_product': production.product_id.id,
"name": production.procurement_group_id.name,
"date_required": fields.Datetime.now(),
"product_uom_id":server_template.uom_id.id,
"product_qty": production.product_qty,
"request_id": False,
"move_dest_ids": False,
"orderpoint_id": False,
'is_subcontract':True,
'group_id':production.procurement_group_id.id,
'production_name':pp.production_id.name,
})
return result
# def get_purchase_request(self, consecutive_process_parameters, production):
# result = []
# for pp in consecutive_process_parameters:
# server_template = self.env['product.template'].search(
# [('server_product_process_parameters_id', '=', pp.surface_technics_parameters_id.id),
# ('detailed_type', '=', 'service')])
# # route_ids
# result.append({
# "product_id": server_template.product_variant_id.id,
# "name": production.procurement_group_id.name,
# "date_required": fields.Datetime.now(),
# "product_uom_id":server_template.uom_id.id,
# "product_qty": 1,
# "request_id": False,
# "move_dest_ids": False,
# "orderpoint_id": False,
# 'is_subcontract':True,
# 'group_id':production.procurement_group_id.id,
# 'production_name':pp.production_id.name,
# })
# return result
def get_purchase_order(self, consecutive_process_parameters, production, product_id_to_production_names):
for pp in consecutive_process_parameters:

View File

@@ -4,14 +4,12 @@ import json
import base64
from odoo import http
from odoo.http import request
from odoo.addons.sf_base.decorators.api_log import api_log
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('刀具组', requester='中控系统')
def get_functional_tool_groups_Info(self, **kw):
"""
刀具组接口
@@ -41,7 +39,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolInventory', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('功能刀具清单', requester='中控系统')
def get_functional_tool_inventory_Info(self, **kw):
"""
功能刀具清单接口
@@ -71,7 +68,6 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolEntity', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('功能刀具', requester='中控系统')
def get_functional_tool_entity_Info(self, **kw):
"""
功能刀具列表接口

View File

@@ -51,15 +51,7 @@ class SfMaintenanceEquipment(models.Model):
headers = {'Authorization': config['center_control_Authorization']}
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetToolInfos"
params = {"DeviceId": self.name}
# r = requests.get(crea_url, params=params, headers=headers)
r = self.env['api.request.log'].log_request(
'get',
crea_url,
name='机床刀库',
responser='中控系统',
params=params,
headers=headers
)
r = requests.get(crea_url, params=params, headers=headers)
ret = r.json()
logging.info('机床刀库register_equipment_tool():%s' % ret)
datas = ret['Datas']

View File

@@ -514,15 +514,7 @@ class ShelfLocation(models.Model):
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetLocationInfos"
params = {'DeviceId': device_id}
# r = requests.get(crea_url, params=params, headers=headers)
r = self.env['api.request.log'].log_request(
'get',
crea_url,
name='库位信息',
responser='中控系统',
params=params,
headers=headers
)
r = requests.get(crea_url, params=params, headers=headers)
ret = r.json()