Merge remote-tracking branch 'origin/develop' into develop
# Conflicts: # sf_manufacturing/models/mrp_production.py # sf_manufacturing/models/purchase_order.py # sf_sale/models/sale_order.py
This commit is contained in:
@@ -32,6 +32,7 @@ class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
|
|||||||
line.company_id,
|
line.company_id,
|
||||||
line.request_id.origin,
|
line.request_id.origin,
|
||||||
)
|
)
|
||||||
|
# po_data.update({'related_product':line.related_product.id})
|
||||||
purchase = purchase_obj.create(po_data)
|
purchase = purchase_obj.create(po_data)
|
||||||
|
|
||||||
# Look for any other PO line in the selected PO with same
|
# Look for any other PO line in the selected PO with same
|
||||||
@@ -63,6 +64,8 @@ class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
|
|||||||
po_line_data = self._prepare_purchase_order_line(purchase, item)
|
po_line_data = self._prepare_purchase_order_line(purchase, item)
|
||||||
if item.keep_description:
|
if item.keep_description:
|
||||||
po_line_data["name"] = item.name
|
po_line_data["name"] = item.name
|
||||||
|
if line.related_product:
|
||||||
|
po_line_data.update({'related_product': line.related_product.id})
|
||||||
po_line = po_line_obj.create(po_line_data)
|
po_line = po_line_obj.create(po_line_data)
|
||||||
po_line_product_uom_qty = po_line.product_uom._compute_quantity(
|
po_line_product_uom_qty = po_line.product_uom._compute_quantity(
|
||||||
po_line.product_uom_qty, alloc_uom
|
po_line.product_uom_qty, alloc_uom
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
from odoo.addons.sf_machine_connect.models.ftp_operate import transfer_nc_files
|
from odoo.addons.sf_machine_connect.models.ftp_operate import transfer_files
|
||||||
from odoo.addons.sf_base.decorators.api_log import api_log
|
from odoo.addons.sf_base.decorators.api_log import api_log
|
||||||
|
|
||||||
class MainController(http.Controller):
|
class MainController(http.Controller):
|
||||||
@@ -54,15 +54,15 @@ class MainController(http.Controller):
|
|||||||
}
|
}
|
||||||
# 传输nc文件
|
# 传输nc文件
|
||||||
try:
|
try:
|
||||||
result = transfer_nc_files(
|
result = transfer_files(
|
||||||
source_ftp_info,
|
source_ftp_info,
|
||||||
target_ftp_info,
|
target_ftp_info,
|
||||||
'/' + str(model_id),
|
'/' + str(model_id),
|
||||||
'/',
|
'/',
|
||||||
match_str=r'^\d*_\d*-' + tool_groups_id.name + r'-\w{2}-all\.nc$'
|
match_str=r'^\d*_\d*-' + tool_groups_id.name + r'-\w{2}-all\.nc$'
|
||||||
)
|
)
|
||||||
if result:
|
if len(result) > 0:
|
||||||
return {'code': 200, 'message': 'success'}
|
return {'code': 200, 'message': '传输成功', 'file_list': result}
|
||||||
else:
|
else:
|
||||||
return {'code': 404, 'message': '未找到编程文件'}
|
return {'code': 404, 'message': '未找到编程文件'}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -167,10 +167,10 @@ class PrintingUtils(models.AbstractModel):
|
|||||||
|
|
||||||
# 设置字体
|
# 设置字体
|
||||||
if font_found:
|
if font_found:
|
||||||
c.setFont('SimSun', 14) # 增大字体大小到14pt
|
c.setFont('SimSun', 10) # 增大字体大小到14pt
|
||||||
else:
|
else:
|
||||||
# 如果没有找到中文字体,使用默认字体
|
# 如果没有找到中文字体,使用默认字体
|
||||||
c.setFont('Helvetica', 14)
|
c.setFont('Helvetica', 10)
|
||||||
logging.warning("未找到中文字体,将使用默认字体")
|
logging.warning("未找到中文字体,将使用默认字体")
|
||||||
|
|
||||||
# 在右下角绘制二维码,预留边距
|
# 在右下角绘制二维码,预留边距
|
||||||
@@ -182,7 +182,7 @@ class PrintingUtils(models.AbstractModel):
|
|||||||
if buttom_text:
|
if buttom_text:
|
||||||
# 在二维码下方绘制文字
|
# 在二维码下方绘制文字
|
||||||
text = buttom_text
|
text = buttom_text
|
||||||
text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 14) # 准确计算文字宽度
|
text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 10) # 准确计算文字宽度
|
||||||
text_x = page_width - qr_size - margin + (qr_size - text_width) / 2 # 文字居中对齐
|
text_x = page_width - qr_size - margin + (qr_size - text_width) / 2 # 文字居中对齐
|
||||||
text_y = margin + 20 # 文字位置靠近底部
|
text_y = margin + 20 # 文字位置靠近底部
|
||||||
c.drawString(text_x, text_y, text)
|
c.drawString(text_x, text_y, text)
|
||||||
|
|||||||
@@ -1300,9 +1300,6 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
machine_list = ast.literal_eval(kw['machine_list'])
|
machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
time_threshold = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
time_threshold = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
alarm_last_24_time = 0.0
|
|
||||||
alarm_all_time = 0.0
|
|
||||||
|
|
||||||
def fetch_result_as_dict(cursor):
|
def fetch_result_as_dict(cursor):
|
||||||
"""辅助函数:将查询结果转为字典"""
|
"""辅助函数:将查询结果转为字典"""
|
||||||
columns = [desc[0] for desc in cursor.description]
|
columns = [desc[0] for desc in cursor.description]
|
||||||
@@ -1311,6 +1308,9 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
# 获取当前时间的时间戳
|
# 获取当前时间的时间戳
|
||||||
current_timestamp = datetime.now().timestamp()
|
current_timestamp = datetime.now().timestamp()
|
||||||
for item in machine_list:
|
for item in machine_list:
|
||||||
|
alarm_last_24_time = 0.0
|
||||||
|
alarm_all_time = 0.0
|
||||||
|
|
||||||
euipment_obj = request.env['maintenance.equipment'].sudo().search([('code', '=', item)])
|
euipment_obj = request.env['maintenance.equipment'].sudo().search([('code', '=', item)])
|
||||||
# 机床上线时间段
|
# 机床上线时间段
|
||||||
first_online_duration = current_timestamp - euipment_obj.first_online_time.timestamp()
|
first_online_duration = current_timestamp - euipment_obj.first_online_time.timestamp()
|
||||||
@@ -1360,9 +1360,9 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
if result[0]:
|
if result[0]:
|
||||||
if float(result[0]) >= 28800:
|
if float(result[0]) >= 28800:
|
||||||
continue
|
continue
|
||||||
alarm_last_24_time = float(result[0])
|
alarm_last_24_time += float(result[0])
|
||||||
else:
|
else:
|
||||||
alarm_last_24_time = 0.0
|
alarm_last_24_time += 0.0
|
||||||
|
|
||||||
alarm_all_nums = []
|
alarm_all_nums = []
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
|
|||||||
@@ -9,9 +9,13 @@ _logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class FTP_P(FTP):
|
class FTP_P(FTP):
|
||||||
"""
|
"""
|
||||||
重写FTP类,重写dirs方法
|
重写FTP类,重写dirs方法,增加编码处理
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, host='', user='', passwd='', acct='', timeout=None, encoding='gbk'):
|
||||||
|
"""初始化时指定编码方式"""
|
||||||
|
super().__init__(host, user, passwd, acct, timeout)
|
||||||
|
self.encoding = encoding
|
||||||
|
|
||||||
def dirs(self, *args):
|
def dirs(self, *args):
|
||||||
"""List a directory in long form.
|
"""List a directory in long form.
|
||||||
By default list current directory to stdout.
|
By default list current directory to stdout.
|
||||||
@@ -32,7 +36,50 @@ class FTP_P(FTP):
|
|||||||
tempdic['name'] = [file for file in r_files if file != "." and file != ".."]
|
tempdic['name'] = [file for file in r_files if file != "." and file != ".."]
|
||||||
# 去除. ..
|
# 去除. ..
|
||||||
return tempdic
|
return tempdic
|
||||||
# return [file for file in r_files if file != "." and file != ".."]
|
|
||||||
|
def nlst(self, *args):
|
||||||
|
"""Get a list of files in a directory."""
|
||||||
|
files = []
|
||||||
|
def append(line):
|
||||||
|
try:
|
||||||
|
if isinstance(line, bytes):
|
||||||
|
files.append(line.decode(self.encoding))
|
||||||
|
else:
|
||||||
|
files.append(line)
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
files.append(line.decode('utf-8', errors='replace'))
|
||||||
|
cmd = 'NLST'
|
||||||
|
if args:
|
||||||
|
cmd = cmd + ' ' + args[0]
|
||||||
|
self.retrlines(cmd, append)
|
||||||
|
return files
|
||||||
|
|
||||||
|
def cwd(self, dirname):
|
||||||
|
"""Change to a directory."""
|
||||||
|
try:
|
||||||
|
if isinstance(dirname, bytes):
|
||||||
|
dirname = dirname.decode(self.encoding)
|
||||||
|
return super().cwd(dirname)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
return super().cwd(dirname.encode(self.encoding).decode('utf-8'))
|
||||||
|
|
||||||
|
def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
|
||||||
|
"""Store a file in binary mode."""
|
||||||
|
try:
|
||||||
|
if isinstance(cmd, bytes):
|
||||||
|
cmd = cmd.decode(self.encoding)
|
||||||
|
return super().storbinary(cmd, fp, blocksize, callback, rest)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
return super().storbinary(cmd.encode(self.encoding).decode('utf-8'), fp, blocksize, callback, rest)
|
||||||
|
|
||||||
|
def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
|
||||||
|
"""Retrieve a file in binary mode."""
|
||||||
|
try:
|
||||||
|
if isinstance(cmd, bytes):
|
||||||
|
cmd = cmd.decode(self.encoding)
|
||||||
|
return super().retrbinary(cmd, callback, blocksize, rest)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
return super().retrbinary(cmd.encode(self.encoding).decode('utf-8'), callback, blocksize, rest)
|
||||||
|
|
||||||
|
|
||||||
# FTP接口类
|
# FTP接口类
|
||||||
@@ -133,7 +180,7 @@ class FtpController:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def transfer_nc_files(
|
def transfer_files(
|
||||||
source_ftp_info,
|
source_ftp_info,
|
||||||
target_ftp_info,
|
target_ftp_info,
|
||||||
source_dir,
|
source_dir,
|
||||||
@@ -151,7 +198,7 @@ def transfer_nc_files(
|
|||||||
target_dir: str, 目标FTP上的目标目录
|
target_dir: str, 目标FTP上的目标目录
|
||||||
keep_dir: bool, 是否保持目录结构,默认False
|
keep_dir: bool, 是否保持目录结构,默认False
|
||||||
"""
|
"""
|
||||||
trans_status = [False]
|
transfered_file_list = []
|
||||||
try:
|
try:
|
||||||
# 连接源FTP
|
# 连接源FTP
|
||||||
source_ftp = FtpController(
|
source_ftp = FtpController(
|
||||||
@@ -217,7 +264,7 @@ def transfer_nc_files(
|
|||||||
with open(temp_path, 'rb') as f:
|
with open(temp_path, 'rb') as f:
|
||||||
target_ftp.ftp.storbinary(f'STOR {target_path}', f)
|
target_ftp.ftp.storbinary(f'STOR {target_path}', f)
|
||||||
|
|
||||||
trans_status[0] = True
|
transfered_file_list.append(item)
|
||||||
# 删除临时文件
|
# 删除临时文件
|
||||||
os.remove(temp_path)
|
os.remove(temp_path)
|
||||||
logging.info(f"已传输文件: {item}")
|
logging.info(f"已传输文件: {item}")
|
||||||
@@ -259,7 +306,7 @@ def transfer_nc_files(
|
|||||||
traverse_dir(source_dir)
|
traverse_dir(source_dir)
|
||||||
|
|
||||||
logging.info("所有文件传输完成")
|
logging.info("所有文件传输完成")
|
||||||
return trans_status[0]
|
return transfered_file_list
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"传输过程出错: {str(e)}")
|
logging.error(f"传输过程出错: {str(e)}")
|
||||||
|
|||||||
@@ -1245,7 +1245,8 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
return workorders_values_str
|
return workorders_values_str
|
||||||
|
|
||||||
def _process_compute_state(self):
|
def _process_compute_state(self):
|
||||||
for workorder in self:
|
sorted_workorders = sorted(self, key=lambda x: x.sequence)
|
||||||
|
for workorder in sorted_workorders:
|
||||||
# 如果工单的工序没有进行排序则跳出循环
|
# 如果工单的工序没有进行排序则跳出循环
|
||||||
if workorder.production_id.workorder_ids.filtered(lambda wk: wk.sequence == 0):
|
if workorder.production_id.workorder_ids.filtered(lambda wk: wk.sequence == 0):
|
||||||
continue
|
continue
|
||||||
@@ -1304,13 +1305,6 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
workorder.state = 'ready'
|
workorder.state = 'ready'
|
||||||
elif workorder.state != 'waiting':
|
elif workorder.state != 'waiting':
|
||||||
workorder.state = 'waiting'
|
workorder.state = 'waiting'
|
||||||
# =========== 特殊工艺工单处理 ===================
|
|
||||||
# if workorder.routing_type == '表面工艺' and workorder.is_subcontrac:
|
|
||||||
# purchase_order = self.env['purchase.order'].search(
|
|
||||||
# [('origin', 'ilike', workorder.production_id.name)])
|
|
||||||
# if purchase_order.picking_ids.filtered(lambda p: p.state in ['waiting', 'confirmed', 'assigned']):
|
|
||||||
# workorder.state = 'waiting'
|
|
||||||
# continue
|
|
||||||
if workorder.technology_design_id.routing_tag == 'special':
|
if workorder.technology_design_id.routing_tag == 'special':
|
||||||
if workorder.is_subcontract is False:
|
if workorder.is_subcontract is False:
|
||||||
workorder.state = 'ready'
|
workorder.state = 'ready'
|
||||||
@@ -1332,6 +1326,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
else:
|
else:
|
||||||
workorder.state = 'waiting'
|
workorder.state = 'waiting'
|
||||||
|
|
||||||
|
|
||||||
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
|
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
|
||||||
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
|
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
|
||||||
'production_id.programming_state')
|
'production_id.programming_state')
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
logging.info('panel_file_path:%s' % panel_file_path)
|
logging.info('panel_file_path:%s' % panel_file_path)
|
||||||
|
|
||||||
# 向编程单中添加二维码
|
# 向编程单中添加二维码
|
||||||
request.env['printing.utils'].add_qr_code_to_pdf(panel_file_path, model_id, "扫码获取工单")
|
request.env['printing.utils'].add_qr_code_to_pdf(panel_file_path, model_id, "模型ID:%s" % model_id)
|
||||||
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
pre_workorder = productions.workorder_ids.filtered(
|
pre_workorder = productions.workorder_ids.filtered(
|
||||||
lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
|
lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
|
||||||
|
|||||||
@@ -224,11 +224,11 @@ class sf_production_plan(models.Model):
|
|||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
def do_production_schedule(self,date_planned_start):
|
def do_production_schedule(self):
|
||||||
"""
|
"""
|
||||||
排程方法
|
排程方法
|
||||||
"""
|
"""
|
||||||
self.deal_processing_schedule(date_planned_start)
|
self.deal_processing_schedule(self.date_planned_start)
|
||||||
for record in self:
|
for record in self:
|
||||||
if not record.production_line_id:
|
if not record.production_line_id:
|
||||||
raise ValidationError("未选择生产线")
|
raise ValidationError("未选择生产线")
|
||||||
|
|||||||
@@ -40,5 +40,5 @@ class Action_Plan_All_Wizard(models.TransientModel):
|
|||||||
self.plan_ids.date_planned_start = self.date_planned_start
|
self.plan_ids.date_planned_start = self.date_planned_start
|
||||||
# 在这里添加您的逻辑来处理这些ID
|
# 在这里添加您的逻辑来处理这些ID
|
||||||
# 判断能否排成
|
# 判断能否排成
|
||||||
self.plan_ids.do_production_schedule(self.date_planned_start)
|
self.plan_ids.do_production_schedule()
|
||||||
_logger.info('处理计划: %s 完成', self.plan_ids.ids)
|
_logger.info('处理计划: %s 完成', self.plan_ids.ids)
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ class RePurchaseOrder(models.Model):
|
|||||||
contract_summary = fields.Text(string='合同概况')
|
contract_summary = fields.Text(string='合同概况')
|
||||||
|
|
||||||
# 选择是否为紧急采购
|
# 选择是否为紧急采购
|
||||||
urgent_purchase = fields.Selection([('no', '否'), ('yes', '是')], string='紧急采购', default='no')
|
urgent_purchase = fields.Selection([('no', '否'), ('yes', '是')], string='紧急采购', default='yes')
|
||||||
|
|
||||||
@api.depends('origin')
|
@api.depends('origin')
|
||||||
def _compute_purchase_type(self):
|
def _compute_purchase_type(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user