Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化

This commit is contained in:
mgw
2025-04-25 16:10:09 +08:00
12 changed files with 158 additions and 62 deletions

View File

@@ -22,7 +22,7 @@ class MaintenancePrinting(models.Model):
# 切换成A4打印机
try:
self.env['jikimo.printing'].print_qr_code(self.id)
self.env['jikimo.printing'].print_qr_code(self.MTcode)
except Exception as e:
raise UserError(f"打印失败: {str(e)}")

View File

@@ -32,6 +32,7 @@ class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
line.company_id,
line.request_id.origin,
)
# po_data.update({'related_product':line.related_product.id})
purchase = purchase_obj.create(po_data)
# 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)
if item.keep_description:
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_product_uom_qty = po_line.product_uom._compute_quantity(
po_line.product_uom_qty, alloc_uom

View File

@@ -1,7 +1,7 @@
import json
from odoo import http
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
class MainController(http.Controller):
@@ -18,12 +18,11 @@ class MainController(http.Controller):
if not maintenance_equipment_id or not model_id:
return {'code': 400, 'message': '参数错误'}
try:
maintenance_equipment_id = int(maintenance_equipment_id)
model_id = int(model_id)
except Exception as e:
return {'code': 400, 'message': '参数类型错误'}
maintenance_equipment = request.env['maintenance.equipment'].sudo().search(
[('id', '=', maintenance_equipment_id), ('category_id.equipment_type', '=', '机床')],
[('MTcode', '=', maintenance_equipment_id), ('category_id.equipment_type', '=', '机床')],
limit=1
)
if not maintenance_equipment:
@@ -55,15 +54,15 @@ class MainController(http.Controller):
}
# 传输nc文件
try:
result = transfer_nc_files(
result = transfer_files(
source_ftp_info,
target_ftp_info,
'/' + str(model_id),
'/',
match_str=r'^\d*_\d*-' + tool_groups_id.name + r'-\w{2}-all\.nc$'
)
if result:
return {'code': 200, 'message': 'success'}
if len(result) > 0:
return {'code': 200, 'message': '传输成功', 'file_list': result}
else:
return {'code': 404, 'message': '未找到编程文件'}
except Exception as e:

View File

@@ -1300,9 +1300,6 @@ class Sf_Dashboard_Connect(http.Controller):
machine_list = ast.literal_eval(kw['machine_list'])
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):
"""辅助函数:将查询结果转为字典"""
columns = [desc[0] for desc in cursor.description]
@@ -1311,6 +1308,9 @@ class Sf_Dashboard_Connect(http.Controller):
# 获取当前时间的时间戳
current_timestamp = datetime.now().timestamp()
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)])
# 机床上线时间段
first_online_duration = current_timestamp - euipment_obj.first_online_time.timestamp()

View File

@@ -9,9 +9,13 @@ _logger = logging.getLogger(__name__)
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):
"""List a directory in long form.
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 != ".."]
# 去除. ..
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接口类
@@ -133,7 +180,7 @@ class FtpController:
def transfer_nc_files(
def transfer_files(
source_ftp_info,
target_ftp_info,
source_dir,
@@ -151,7 +198,7 @@ def transfer_nc_files(
target_dir: str, 目标FTP上的目标目录
keep_dir: bool, 是否保持目录结构,默认False
"""
trans_status = [False]
transfered_file_list = []
try:
# 连接源FTP
source_ftp = FtpController(
@@ -217,7 +264,7 @@ def transfer_nc_files(
with open(temp_path, 'rb') as f:
target_ftp.ftp.storbinary(f'STOR {target_path}', f)
trans_status[0] = True
transfered_file_list.append(item)
# 删除临时文件
os.remove(temp_path)
logging.info(f"已传输文件: {item}")
@@ -259,7 +306,7 @@ def transfer_nc_files(
traverse_dir(source_dir)
logging.info("所有文件传输完成")
return trans_status[0]
return transfered_file_list
except Exception as e:
logging.error(f"传输过程出错: {str(e)}")

View File

@@ -845,7 +845,7 @@ class SfMaintenanceEquipment(models.Model):
box_size=10,
border=4,
)
qr.add_data(record.id)
qr.add_data(record.MTcode)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")

View File

@@ -934,6 +934,7 @@ class MrpProduction(models.Model):
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 get_subcontract_pick_purchase(self):
@@ -961,7 +962,7 @@ 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['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)

View File

@@ -1243,7 +1243,8 @@ class ResMrpWorkOrder(models.Model):
return workorders_values_str
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):
continue
@@ -1302,13 +1303,6 @@ class ResMrpWorkOrder(models.Model):
workorder.state = 'ready'
elif 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.is_subcontract is False:
workorder.state = 'ready'
@@ -1330,6 +1324,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')

View File

@@ -59,6 +59,86 @@ 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', '=', product_id),
('location_id', '=', location_id.id)
])
total_qty = sum(quants.mapped('quantity')) # 计算该位置的总库存量
is_available = total_qty > 0
if not is_available:
continue
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:
picking_id = production_id.picking_ids.filtered(
lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
move_out = picking_id.move_id
for mo in move_out:
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:
@@ -66,37 +146,7 @@ class PurchaseOrder(models.Model):
raise UserError('请对【产品】中的【数量】进行输入')
if line.price_unit <= 0:
raise UserError('请对【产品】中的【单价】进行输入')
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()
record.outsourcing_service_replenishment()
return super(PurchaseOrder, self).button_confirm()

View File

@@ -224,11 +224,11 @@ class sf_production_plan(models.Model):
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:
if not record.production_line_id:
raise ValidationError("未选择生产线")

View File

@@ -40,5 +40,5 @@ class Action_Plan_All_Wizard(models.TransientModel):
self.plan_ids.date_planned_start = self.date_planned_start
# 在这里添加您的逻辑来处理这些ID
# 判断能否排成
self.plan_ids.do_production_schedule(self.date_planned_start)
self.plan_ids.do_production_schedule()
_logger.info('处理计划: %s 完成', self.plan_ids.ids)

View File

@@ -323,7 +323,7 @@ class RePurchaseOrder(models.Model):
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')
def _compute_purchase_type(self):
@@ -390,6 +390,7 @@ class RePurchaseOrder(models.Model):
# 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,