Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化
This commit is contained in:
@@ -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)}")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)}")
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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("未选择生产线")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user