Compare commits

..

2 Commits

33 changed files with 625 additions and 565 deletions

View File

@@ -3,12 +3,8 @@ import qrcode
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4 from reportlab.lib.pagesizes import A4
from PIL import Image from PIL import Image
import logging
from reportlab.lib.utils import ImageReader from reportlab.lib.utils import ImageReader
from odoo import models, fields, api from odoo import models, fields, api
import base64
_logger = logging.getLogger(__name__)
class JikimoPrinting(models.AbstractModel): class JikimoPrinting(models.AbstractModel):
_name = 'jikimo.printing' _name = 'jikimo.printing'
@@ -17,11 +13,6 @@ class JikimoPrinting(models.AbstractModel):
""" """
打印二维码 打印二维码
""" """
printer = self.env['printing.printer'].get_default()
if not printer:
_logger.error("未找到默认打印机")
return False
# 生成二维码 # 生成二维码
qr = qrcode.QRCode(version=1, box_size=10, border=5) qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(data) qr.add_data(data)
@@ -50,38 +41,16 @@ class JikimoPrinting(models.AbstractModel):
# 获取PDF内容并打印 # 获取PDF内容并打印
pdf_content = pdf_buffer.getvalue() pdf_content = pdf_buffer.getvalue()
# _logger.info(f"打印内容: {pdf_content}") printer = self.env['printing.printer'].get_default()
printer.print_document(report=None, content=pdf_content, doc_format='pdf') printer.print_document(report=None, content=pdf_content, doc_format='pdf')
# 清理资源 # 清理资源
pdf_buffer.close() pdf_buffer.close()
temp_image.close() temp_image.close()
return True
def print_pdf(self, pdf_data): def print_pdf(self, pdf_data):
""" """
打印PDF 打印PDF
""" """
printer = self.env['printing.printer'].get_default() printer = self.env['printing.printer'].get_default()
if not printer: printer.print_document(report=None, content = pdf_data, doc_format='pdf')
_logger.error("未找到默认打印机")
return False
pdf_data_str = pdf_data.decode('ascii', errors='ignore')
decoded_data = base64.b64decode(pdf_data_str)
# 处理二进制数据
pdf_buffer = BytesIO()
pdf_buffer.write(decoded_data)
pdf_buffer.seek(0)
# 获取PDF内容
pdf_content = pdf_buffer.getvalue()
printer.print_document(report=None, content=pdf_content, doc_format='pdf')
# 清理资源
pdf_buffer.close()
_logger.info("成功打印PDF")
return True

View File

@@ -21,6 +21,8 @@ class MrpWorkorder(models.Model):
# 执行打印 # 执行打印
self.env['jikimo.printing'].print_pdf(pdf_data) self.env['jikimo.printing'].print_pdf(pdf_data)
wo.production_id.product_id.is_print_program = True wo.production_id.product_id.is_print_program = True
_logger.info(f"工单 {wo.name} 的PDF已成功打印")
except Exception as e: except Exception as e:
_logger.error(f"工单 {wo.name} 的PDF打印失败: {str(e)}") _logger.error(f"工单 {wo.name} 的PDF打印失败: {str(e)}")

View File

@@ -9,8 +9,7 @@ class MrpProduction(models.Model):
@api.depends('state') @api.depends('state')
def _compute_pr_mp_count(self): def _compute_pr_mp_count(self):
for item in self: for item in self:
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name)]) pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')])
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')])
if pr_ids: if pr_ids:
item.pr_mp_count = len(pr_ids) item.pr_mp_count = len(pr_ids)
else: else:
@@ -21,8 +20,7 @@ class MrpProduction(models.Model):
采购请求 采购请求
""" """
self.ensure_one() self.ensure_one()
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)]) pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)])
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)])
action = { action = {
'res_model': 'purchase.request', 'res_model': 'purchase.request',
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',

View File

@@ -6,7 +6,7 @@ from odoo.addons.sf_base.decorators.api_log import api_log
class MainController(http.Controller): class MainController(http.Controller):
@http.route('/api/manual_download_program', type='json', methods=['POST'], auth='wechat_token', cors='*') @http.route('/api/manual_download_program', type='json', methods=['POST'], auth='public', cors='*')
@api_log('人工线下加工编程文件传输', requester='报工系统') @api_log('人工线下加工编程文件传输', requester='报工系统')
def manual_download_program(self): def manual_download_program(self):
""" """
@@ -59,7 +59,7 @@ class MainController(http.Controller):
target_ftp_info, target_ftp_info,
'/' + str(model_id), '/' + str(model_id),
'/', '/',
match_str=r'^\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 len(result) > 0: if len(result) > 0:
return {'code': 200, 'message': '传输成功', 'file_list': result} return {'code': 200, 'message': '传输成功', 'file_list': result}

View File

@@ -141,7 +141,7 @@ class QualityCheck(models.Model):
# # 出厂检验报告编号 # # 出厂检验报告编号
# report_number = fields.Char('出厂检验报告编号', compute='_compute_report_number', readonly=True) # report_number = fields.Char('出厂检验报告编号', compute='_compute_report_number', readonly=True)
# 总数量值为调拨单_产品明细_数量 # 总数量值为调拨单_产品明细_数量
total_qty = fields.Char('总数量', compute='_compute_total_qty') total_qty = fields.Char('总数量', compute='_compute_total_qty', readonly=False, store=True)
column_nums = fields.Integer('测量值列数', default=1) column_nums = fields.Integer('测量值列数', default=1)
@@ -281,7 +281,42 @@ class QualityCheck(models.Model):
""" """
pass pass
def check_total_quality(self):
"""
校验总数量是否等于拣货数量
"""
for record in self:
if record.picking_id:
for move in record.picking_id.move_ids_without_package:
if move.product_id == record.product_id:
picking_qty = int(move.product_uom_qty)
if int(record.total_qty) != picking_qty:
return True
return False
def do_publish(self): def do_publish(self):
"""打开二次确认弹窗"""
self.ensure_one()
if self.check_total_quality():
return self._show_confirmation_wizard()
else:
return self._do_actual_publish()
def _show_confirmation_wizard(self):
"""显示确认向导的通用方法"""
return {
'name': _('发布确认'),
'type': 'ir.actions.act_window',
'res_model': 'quality.check.publish.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_check_id': self.id,
# 其他默认字段...
}
}
def _do_actual_publish(self):
"""发布出厂检验报告""" """发布出厂检验报告"""
self.ensure_one() self.ensure_one()
self._check_part_number() self._check_part_number()

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from odoo import api, fields, models, _ from odoo import api, fields, models, _
class QualityCheckPublishWizard(models.TransientModel): class QualityCheckPublishWizard(models.TransientModel):
_name = 'quality.check.publish.wizard' _name = 'quality.check.publish.wizard'
_description = '质检报告发布确认' _description = '质检报告发布确认'
@@ -12,7 +13,8 @@ class QualityCheckPublishWizard(models.TransientModel):
measure_count = fields.Integer('测量件数', readonly=True) measure_count = fields.Integer('测量件数', readonly=True)
item_count = fields.Integer('检验项目数', readonly=True) item_count = fields.Integer('检验项目数', readonly=True)
old_report_name = fields.Char('旧出厂检验报告编号', readonly=True) old_report_name = fields.Char('旧出厂检验报告编号', readonly=True)
publish_status = fields.Selection([('draft', '草稿'), ('published', '已发布'), ('canceled', '已撤销')], string='发布状态', readonly=True) publish_status = fields.Selection([('draft', '草稿'), ('published', '已发布'), ('canceled', '已撤销')], string='发布状态',
readonly=True)
def action_confirm_publish(self): def action_confirm_publish(self):
"""确认发布""" """确认发布"""

View File

@@ -1,7 +1,3 @@
pystrich pystrich
cpca==0.5.5 cpca
wechatpy==1.8.18 pycryptodome==3.20
pycryptodome==3.22.0
openupgradelib==3.10.0
opcua==0.98.13
openpyxl

View File

@@ -134,7 +134,7 @@ class PrintingUtils(models.AbstractModel):
# 注册中文字体 # 注册中文字体
font_paths = [ font_paths = [
"/usr/share/fonts/chinese/simsun.ttc", # Windows系统宋体 "/usr/share/fonts/windows/simsun.ttc", # Windows系统宋体
"c:/windows/fonts/simsun.ttc", # Windows系统宋体另一个位置 "c:/windows/fonts/simsun.ttc", # Windows系统宋体另一个位置
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", # Linux Droid字体 "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", # Linux Droid字体
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", # 文泉驿正黑 "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", # 文泉驿正黑
@@ -167,10 +167,10 @@ class PrintingUtils(models.AbstractModel):
# 设置字体 # 设置字体
if font_found: if font_found:
c.setFont('SimSun', 10) # 增大字体大小到14pt c.setFont('SimSun', 14) # 增大字体大小到14pt
else: else:
# 如果没有找到中文字体,使用默认字体 # 如果没有找到中文字体,使用默认字体
c.setFont('Helvetica', 10) c.setFont('Helvetica', 14)
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", 10) # 准确计算文字宽度 text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 14) # 准确计算文字宽度
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)

View File

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

View File

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

View File

@@ -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:

View File

@@ -261,50 +261,8 @@ def transfer_files(
target_path = f"{target_dir}/{relative_path}/{item}" target_path = f"{target_dir}/{relative_path}/{item}"
else: else:
target_path = f"{target_dir}/{item}" target_path = f"{target_dir}/{item}"
# 规范化路径
target_path = target_path.replace('\\', '/').strip('/')
# 确保目标目录存在
target_dir_path = '/'.join(target_path.split('/')[:-1])
try:
target_ftp.ftp.cwd('/') # 回到根目录
for dir_part in target_dir_path.split('/'):
if dir_part:
try:
target_ftp.ftp.cwd(dir_part)
except:
try:
target_ftp.ftp.mkd(dir_part)
target_ftp.ftp.cwd(dir_part)
except Exception as e:
logging.error(f"创建目录失败 {dir_part}: {str(e)}")
raise
except Exception as e:
logging.error(f"处理目标目录失败: {str(e)}")
raise
# 检查FTP连接状态
try:
target_ftp.ftp.voidcmd('NOOP')
except:
logging.error("FTP连接已断开尝试重新连接")
target_ftp.ftp.connect(target_ftp_info['host'], target_ftp_info['port'])
target_ftp.ftp.login(target_ftp_info['username'], target_ftp_info['password'])
# 上传文件
try:
with open(temp_path, 'rb') as f: with open(temp_path, 'rb') as f:
# 检查文件是否可读
content = f.read()
if not content:
raise Exception("临时文件为空")
f.seek(0) # 重置文件指针
target_ftp.ftp.storbinary(f'STOR {target_path}', f) target_ftp.ftp.storbinary(f'STOR {target_path}', f)
except Exception as e:
logging.error(f"上传文件失败: {str(e)}")
logging.error(f"目标路径: {target_path}")
raise
transfered_file_list.append(item) transfered_file_list.append(item)
# 删除临时文件 # 删除临时文件
@@ -312,37 +270,37 @@ def transfer_files(
logging.info(f"已传输文件: {item}") logging.info(f"已传输文件: {item}")
# 清空目标目录下的所有内容 # 清空目标目录下的所有内容
# try: try:
# target_ftp.ftp.cwd(target_dir) target_ftp.ftp.cwd(target_dir)
# files = target_ftp.ftp.nlst() files = target_ftp.ftp.nlst()
# for f in files: for f in files:
# try: try:
# # 尝试删除文件 # 尝试删除文件
# target_ftp.ftp.delete(f) target_ftp.ftp.delete(f)
# except: except:
# try: try:
# # 如果删除失败,可能是目录,递归删除目录 # 如果删除失败,可能是目录,递归删除目录
# def remove_dir(path): def remove_dir(path):
# target_ftp.ftp.cwd(path) target_ftp.ftp.cwd(path)
# sub_files = target_ftp.ftp.nlst() sub_files = target_ftp.ftp.nlst()
# for sf in sub_files: for sf in sub_files:
# try: try:
# target_ftp.ftp.delete(sf) target_ftp.ftp.delete(sf)
# except: except:
# remove_dir(f"{path}/{sf}") remove_dir(f"{path}/{sf}")
# target_ftp.ftp.cwd('..') target_ftp.ftp.cwd('..')
# target_ftp.ftp.rmd(path) target_ftp.ftp.rmd(path)
# remove_dir(f"{target_dir}/{f}") remove_dir(f"{target_dir}/{f}")
# except: except:
# logging.error(f"无法删除 {f}") logging.error(f"无法删除 {f}")
# pass pass
# logging.info(f"已清空目标目录 {target_dir}") logging.info(f"已清空目标目录 {target_dir}")
# except Exception as e: except Exception as e:
# logging.error(f"清空目标目录失败: {str(e)}") logging.error(f"清空目标目录失败: {str(e)}")
# raise Exception(f"清空目标目录失败: {str(e)}") raise Exception(f"清空目标目录失败: {str(e)}")
# 开始遍历 # 开始遍历
traverse_dir(source_dir) traverse_dir(source_dir)

View File

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

View File

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

View File

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

View File

@@ -70,21 +70,21 @@ class ResMrpWorkOrder(models.Model):
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效', delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效',
tracking=True) tracking=True)
back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=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) pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True)
#
# @api.depends('state') @api.depends('state')
# def _compute_pr_mp_count(self): def _compute_pr_mp_count(self):
# for item in self: for item in self:
# if not item.is_subcontract: if not item.is_subcontract:
# item.pr_mp_count = 0 item.pr_mp_count = 0
# continue continue
# pr_ids = self.env['purchase.request'].sudo().search( pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'), [('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'),
# ('state', '!=', 'rejected')]) ('state', '!=', 'rejected')])
# if pr_ids: if pr_ids:
# item.pr_mp_count = len(pr_ids) item.pr_mp_count = len(pr_ids)
# else: else:
# item.pr_mp_count = 0 item.pr_mp_count = 0
@api.depends('state') @api.depends('state')
def _compute_back_button_display(self): def _compute_back_button_display(self):
for record in self: for record in self:
@@ -443,12 +443,11 @@ class ResMrpWorkOrder(models.Model):
def _compute_surface_technics_purchase_ids(self): def _compute_surface_technics_purchase_ids(self):
for order in self: for order in self:
if order.routing_type == '表面工艺' and order.state not in ['cancel']: if order.routing_type == '表面工艺' and order.state not in ['cancel']:
# domain = [('group_id', '=', self.production_id.procurement_group_id.id),
# ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')]
domain = [('purchase_type', '=', 'consignment'), domain = [('purchase_type', '=', 'consignment'),
('origin', 'like', '%' + self.production_id.name + '%'), ('origin', 'like', '%' + self.production_id.name + '%'),
('state', '!=', 'cancel')] ('state', '!=', 'cancel')]
# domain = [('purchase_type', '=', 'consignment'),
# ('origin', 'like', '%' + self.production_id.name + '%'),
# ('state', '!=', 'cancel')]
purchase = self.env['purchase.order'].search(domain) purchase = self.env['purchase.order'].search(domain)
order.surface_technics_purchase_count = 0 order.surface_technics_purchase_count = 0
if not purchase: if not purchase:
@@ -461,30 +460,30 @@ class ResMrpWorkOrder(models.Model):
else: else:
order.surface_technics_purchase_count = 0 order.surface_technics_purchase_count = 0
# def action_view_pr_mrp_workorder(self): def action_view_pr_mrp_workorder(self):
# """ """
# 采购请求 采购请求
# """ """
# self.ensure_one() self.ensure_one()
# pr_ids = self.env['purchase.request'].sudo().search( pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'), [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
# ('state', '!=', 'rejected')]) ('state', '!=', 'rejected')])
# action = { action = {
# 'res_model': 'purchase.request', 'res_model': 'purchase.request',
# 'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
# } }
# if len(pr_ids) == 1: if len(pr_ids) == 1:
# action.update({ action.update({
# 'view_mode': 'form', 'view_mode': 'form',
# 'res_id': pr_ids[0].id, 'res_id': pr_ids[0].id,
# }) })
# else: else:
# action.update({ action.update({
# 'name': _("从 %s生成采购请求单", self.name), 'name': _("%s生成采购请求单", self.name),
# 'domain': [('id', 'in', pr_ids)], 'domain': [('id', 'in', pr_ids)],
# 'view_mode': 'tree,form', 'view_mode': 'tree,form',
# }) })
# return action return action
def action_view_surface_technics_purchase(self): def action_view_surface_technics_purchase(self):
self.ensure_one() self.ensure_one()
# if self.routing_type == '表面工艺': # if self.routing_type == '表面工艺':
@@ -513,10 +512,10 @@ class ResMrpWorkOrder(models.Model):
return result return result
def _get_surface_technics_purchase_ids(self): 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')]
# domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment')]
# domain = [('group_id', '=', self.production_id.procurement_group_id.id), ('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') purchase_orders = self.env['purchase.order'].search(domain, order='id desc', # 按创建时间降序(最新的在前)
limit=1)
purchase_orders_id = self.env['purchase.order'] purchase_orders_id = self.env['purchase.order']
for po in purchase_orders: for po in purchase_orders:
for line in po.order_line: for line in po.order_line:

View File

@@ -59,6 +59,86 @@ class PurchaseOrder(models.Model):
production_id = self.env['mrp.production'].search([('origin', 'in', origins)]) production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
purchase.production_count = len(production_id) 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): def button_confirm(self):
for record in self: for record in self:
for line in record.order_line: for line in record.order_line:
@@ -66,37 +146,7 @@ class PurchaseOrder(models.Model):
raise UserError('请对【产品】中的【数量】进行输入') raise UserError('请对【产品】中的【数量】进行输入')
if line.price_unit <= 0: if line.price_unit <= 0:
raise UserError('请对【产品】中的【单价】进行输入') raise UserError('请对【产品】中的【单价】进行输入')
# if record.purchase_type == 'consignment': record.outsourcing_service_replenishment()
# 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()
return super(PurchaseOrder, self).button_confirm() return super(PurchaseOrder, self).button_confirm()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -213,11 +213,11 @@ class ReworkWizard(models.TransientModel):
self.production_id.get_new_program(panel_name) self.production_id.get_new_program(panel_name)
if self.reprogramming_num >= 0 and self.programming_state == '已下发': if self.reprogramming_num >= 0 and self.programming_state == '已下发':
# ============= 处理CNC加工加工工单的 CNC程序和cmm程序 信息============= # ============= 处理CNC加工加工工单的 CNC程序和cmm程序 信息=============
for cnc_work in new_work_ids.filtered(lambda wk: wk.name == 'CNC加工' or wk.name == '人工线下加工'): for cnc_work in new_work_ids.filtered(lambda wk: wk.name == 'CNC加工'):
ret = {'programming_list': []} ret = {'programming_list': []}
old_cnc_rework = max(self.production_id.workorder_ids.filtered( old_cnc_rework = max(self.production_id.workorder_ids.filtered(
lambda crw: crw.processing_panel == cnc_work.processing_panel lambda crw: crw.processing_panel == cnc_work.processing_panel
and crw.state == 'rework' and (crw.routing_type == 'CNC加工' or crw.routing_type == '人工线下加工')), and crw.state == 'rework' and crw.routing_type == 'CNC加工'),
key=lambda w: w.create_date key=lambda w: w.create_date
) )
# 获取当前工单的CNC程序和cmm程序 # 获取当前工单的CNC程序和cmm程序
@@ -259,7 +259,7 @@ class ReworkWizard(models.TransientModel):
new_cnc_workorder = self.production_id.workorder_ids.filtered( new_cnc_workorder = self.production_id.workorder_ids.filtered(
lambda ap1: ap1.processing_panel == cnc_work.processing_panel lambda ap1: ap1.processing_panel == cnc_work.processing_panel
and ap1.state not in ( and ap1.state not in (
'rework', 'done') and (ap1.routing_type == 'CNC加工' or ap1.routing_type == '人工线下加工') 'rework', 'done') and ap1.routing_type == 'CNC加工'
) )
if not new_cnc_workorder.cnc_ids: if not new_cnc_workorder.cnc_ids:
new_cnc_workorder.write({ new_cnc_workorder.write({
@@ -303,22 +303,18 @@ class ReworkWizard(models.TransientModel):
@api.onchange('production_id') @api.onchange('production_id')
def onchange_processing_panel_id(self): def onchange_processing_panel_id(self):
for item in self: for item in self:
panel_ids = []
domain = [('id', '=', False)] domain = [('id', '=', False)]
production_id = item.production_id production_id = item.production_id
if production_id: if production_id:
if self.env.user.has_group('sf_base.group_sf_order_user'): if self.env.user.has_group('sf_base.group_sf_order_user'):
panel_ids = []
panel_arr = production_id.product_id.model_processing_panel panel_arr = production_id.product_id.model_processing_panel
if panel_arr is False: if panel_arr is False:
break break
for p in production_id.detection_result_ids.filtered( for p in production_id.detection_result_ids.filtered(
lambda ap1: ap1.handle_result == '待处理'): lambda ap1: ap1.handle_result == '待处理'):
if p.processing_panel is not False and p.processing_panel not in panel_arr: if p.processing_panel is not False and p.processing_panel not in panel_arr:
if len(panel_arr)>0:
panel_arr += ','.join(p.processing_panel) panel_arr += ','.join(p.processing_panel)
else:
panel_arr = p.processing_panel
for item in panel_arr.split(','): for item in panel_arr.split(','):
panel = self.env['sf.processing.panel'].search( panel = self.env['sf.processing.panel'].search(
[('name', 'ilike', item)]) [('name', 'ilike', item)])

View File

@@ -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, "模型ID%s" % model_id) request.env['printing.utils'].add_qr_code_to_pdf(panel_file_path, 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'

View File

@@ -1135,6 +1135,8 @@ class sfProductionProcessParameter(models.Model):
[("code", '=', item['code']), ('active', 'in', [True, False])]) [("code", '=', item['code']), ('active', 'in', [True, False])])
process = self.env['sf.production.process'].search( process = self.env['sf.production.process'].search(
[('code', '=', item['process_id_code'])], limit=1) [('code', '=', item['process_id_code'])], limit=1)
production_process_parameter = self.search(
[("code", '=', item['code']), ('active', 'in', [True, False])])
if not production_process_parameter: if not production_process_parameter:
production_process_parameter = self.create({ production_process_parameter = self.create({
"name": item['name'], "name": item['name'],
@@ -1149,7 +1151,7 @@ class sfProductionProcessParameter(models.Model):
'processing_mm': item['processing_mm'], 'processing_mm': item['processing_mm'],
'gain_way':'外协', 'gain_way':'外协',
}) })
# production_process_parameter.create_service_product() production_process_parameter.create_service_product()
else: else:
production_process_parameter.gain_way = '外协' production_process_parameter.gain_way = '外协'
production_process_parameter.name = item['name'] production_process_parameter.name = item['name']
@@ -1161,9 +1163,9 @@ class sfProductionProcessParameter(models.Model):
[('materials_no', 'in', item['materials_model_ids_codes'])]) [('materials_no', 'in', item['materials_model_ids_codes'])])
production_process_parameter.active = item['active'] production_process_parameter.active = item['active']
production_process_parameter.processing_mm = item['processing_mm'] production_process_parameter.processing_mm = item['processing_mm']
# if not production_process_parameter.outsourced_service_products: if not production_process_parameter.outsourced_service_products:
# production_process_parameter.create_service_product() production_process_parameter.create_service_product()
# production_process_parameter.create_work_center() production_process_parameter.create_work_center()
else: else:
raise ValidationError("表面工艺可选参数认证未通过") raise ValidationError("表面工艺可选参数认证未通过")

View File

@@ -228,7 +228,7 @@ class sf_production_plan(models.Model):
""" """
排程方法 排程方法
""" """
self.deal_processing_schedule(self[0].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("未选择生产线")

View File

@@ -26,6 +26,7 @@
'views/quality_check_view.xml', 'views/quality_check_view.xml',
'views/quality_company.xml', 'views/quality_company.xml',
'wizard/check_picking_wizard_view.xml', 'wizard/check_picking_wizard_view.xml',
'wizard/confirmation_wizard_views.xml',
], ],
'assets': { 'assets': {

View File

@@ -74,5 +74,6 @@ access_quality_cnc_test_group_quality_director,quality_cnc_test_group_quality_di
access_quality_cnc_test_group_sf_equipment_user,quality_cnc_test_group_sf_equipment_user,model_quality_cnc_test,sf_base.group_sf_equipment_user,1,1,0,0 access_quality_cnc_test_group_sf_equipment_user,quality_cnc_test_group_sf_equipment_user,model_quality_cnc_test,sf_base.group_sf_equipment_user,1,1,0,0
access_picking_validate_check_wizard,access.picking_validate_check_wizard,model_picking_validate_check_wizard,quality.group_quality_user,1,1,1,0 access_picking_validate_check_wizard,access.picking_validate_check_wizard,model_picking_validate_check_wizard,quality.group_quality_user,1,1,1,0
access_confirmation_wizard,access.confirmation.wizard,model_confirmation_wizard,quality.group_quality_user,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
74
75
76
77
78
79

View File

@@ -1 +1,2 @@
from . import check_picking_wizard from . import check_picking_wizard
from . import confirmation_wizard

View File

@@ -0,0 +1,32 @@
from odoo import models, fields, api
class ConfirmationWizard(models.TransientModel):
_name = 'confirmation.wizard'
_description = '二次确认向导'
# 可根据需要传递上下文参数
check_id = fields.Many2one('quality.check', string='质检单', required=True)
picking_name = fields.Char(string='拣货单', related='check_id.picking_id.name', store=True)
number = fields.Char(string='数量', related='check_id.total_qty', store=True)
picking_num = fields.Integer(string='拣货数量', compute='_compute_picking_num', store=True)
@api.depends('check_id.picking_id')
def _compute_picking_num(self):
for record in self.check_id:
if record.picking_id:
for move in record.picking_id.move_ids_without_package:
if move.product_id == record.product_id:
self.picking_num = int(move.product_uom_qty)
else:
self.picking_num = 0
button_text = fields.Char(string='确认按钮文字')
def action_confirm(self):
self.ensure_one()
# 获取原始记录
check = self.env['quality.check'].browse(self.check_id.id)
# 调用实际发布方法
return check._do_actual_publish()

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_confirmation_wizard_form" model="ir.ui.view">
<field name="name">confirmation.wizard.form</field>
<field name="model">confirmation.wizard</field>
<field name="arch" type="xml">
<form>
<div class="alert alert-warning">
<p>拣货调拨单号<strong><field name="picking_name" class="oe_inline"/></strong>需求数量为<strong><field name="picking_num" class="oe_inline"/></strong>,当前质量检查单产品数量为<strong><field name="number" class="oe_inline"/></strong>,数量不一致,是否确认继续?</p>
</div>
<footer>
<button name="action_confirm" string="确定" type="object" class="btn-primary"/>
<button string="取消" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>

View File

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