diff --git a/jikimo_purchase_request/models/product_template.py b/jikimo_purchase_request/models/product_template.py
index 623c0981..c4f2a589 100644
--- a/jikimo_purchase_request/models/product_template.py
+++ b/jikimo_purchase_request/models/product_template.py
@@ -13,7 +13,11 @@ class ProductTemplate(models.Model):
template_id.purchase_request = product_id.purchase_request
return template_id
+
+class ProdcutProduct(models.Model):
+ _inherit = 'product.product'
+
def copy_template(self, product_template_id):
""" 复制成品模板时,复制采购申请 """
- super(ProductTemplate, self).copy_template(product_template_id)
+ super(ProdcutProduct, self).copy_template(product_template_id)
self.purchase_request = product_template_id.purchase_request
diff --git a/jikimo_purchase_request/models/purchase_request.py b/jikimo_purchase_request/models/purchase_request.py
index 3ecd2161..9ed11f13 100644
--- a/jikimo_purchase_request/models/purchase_request.py
+++ b/jikimo_purchase_request/models/purchase_request.py
@@ -114,7 +114,10 @@ class PurchaseRequestLine(models.Model):
def _compute_qty_to_buy(self):
for pr in self:
- qty_to_buy = sum(pr.mapped("product_qty")) - sum(pr.mapped("qty_done")) - sum(pr.mapped("qty_in_progress"))
+ qty_to_buy = sum(pr.mapped("product_qty"))
+ if pr.purchase_count > 0:
+ qty_to_buy -= sum(pr.mapped("purchase_lines").filtered(lambda po: po.state != 'cancel').mapped(
+ "product_qty"))
pr.qty_to_buy = qty_to_buy > 0.0
pr.pending_qty_to_receive = qty_to_buy
diff --git a/jikimo_sale_multiple_supply_methods/models/product_template.py b/jikimo_sale_multiple_supply_methods/models/product_template.py
index cd730998..85190cab 100644
--- a/jikimo_sale_multiple_supply_methods/models/product_template.py
+++ b/jikimo_sale_multiple_supply_methods/models/product_template.py
@@ -6,6 +6,10 @@ class ProductTemplate(models.Model):
is_manual_processing = fields.Boolean(string='人工线下加工')
is_customer_provided = fields.Boolean(string='客供料')
+
+class ProductProduct(models.Model):
+ _inherit = 'product.product'
+
def copy_template(self, product_template_id):
if not isinstance(product_template_id, ProductTemplate):
raise ValueError('%s必须是ProductTemplate类型' % product_template_id)
diff --git a/jikimo_workorder_exception/controllers/main.py b/jikimo_workorder_exception/controllers/main.py
index cf208700..6134e27f 100644
--- a/jikimo_workorder_exception/controllers/main.py
+++ b/jikimo_workorder_exception/controllers/main.py
@@ -4,6 +4,7 @@ import json
import logging
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect
+from odoo.addons.sf_base.decorators.api_log import api_log
from datetime import datetime
_logger = logging.getLogger(__name__)
@@ -12,6 +13,7 @@ class WorkorderExceptionConroller(http.Controller):
@http.route('/AutoDeviceApi/BillError', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('工单对接错误', requester='中控系统')
def workder_exception(self, **kw):
"""
记录工单异常
diff --git a/sf_base/commons/common.py b/sf_base/commons/common.py
index 2dbb4795..579125f0 100644
--- a/sf_base/commons/common.py
+++ b/sf_base/commons/common.py
@@ -103,12 +103,19 @@ class PrintingUtils(models.AbstractModel):
self.send_to_printer(host, port, zpl_code)
- def add_qr_code_to_pdf(self, pdf_path:str, content:str, buttom_text:Optional[str]=False):
+ def add_qr_code_to_pdf(
+ self,
+ pdf_path:str,
+ content:str,
+ qr_code_buttom_text:Optional[str]=False,
+ buttom_text:Optional[str]=False,
+ ):
"""
在PDF文件中添加二维码
:param pdf_path: PDF文件路径
:param content: 二维码内容
- :param buttom_text: 二维码下方文字
+ :param qr_code_buttom_text: 二维码下方文字
+ :param buttom_text: 正文下方文字
:return: 是否成功
"""
if not os.path.exists(pdf_path):
@@ -156,8 +163,9 @@ class PrintingUtils(models.AbstractModel):
existing_pdf = PdfFileReader(original_file)
output = PdfFileWriter()
- # 处理第一页
- page = existing_pdf.getPage(0)
+ # 处理最后一页
+ last_page = existing_pdf.getNumPages() - 1
+ page = existing_pdf.getPage(last_page)
# 获取页面尺寸
page_width = float(page.mediaBox.getWidth())
page_height = float(page.mediaBox.getHeight())
@@ -179,13 +187,21 @@ class PrintingUtils(models.AbstractModel):
qr_y = margin + 20 # 将二维码向上移动一点,为文字留出空间
c.drawImage(qr_temp_path, page_width - qr_size - margin, qr_y, width=qr_size, height=qr_size)
- if buttom_text:
+ if qr_code_buttom_text:
# 在二维码下方绘制文字
- text = buttom_text
+ text = qr_code_buttom_text
text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 10) # 准确计算文字宽度
text_x = page_width - qr_size - margin + (qr_size - text_width) / 2 # 文字居中对齐
text_y = margin + 20 # 文字位置靠近底部
c.drawString(text_x, text_y, text)
+
+ if buttom_text:
+ # 在下方中间添加文字
+ text = button_text
+ text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 10) # 准确计算文字宽度
+ text_x = (page_width - text_width) / 2 # 文字居中对齐
+ text_y = margin + 20 # 文字位置靠近底部
+ c.drawString(text_x, text_y, text)
c.save()
@@ -196,11 +212,12 @@ class PrintingUtils(models.AbstractModel):
# 合并原始页面和二维码页面
page.mergePage(qr_page)
- output.addPage(page)
# 添加剩余的页面
- for i in range(1, existing_pdf.getNumPages()):
+ for i in range(0, last_page):
output.addPage(existing_pdf.getPage(i))
+
+ output.addPage(page)
# 保存最终的PDF到一个临时文件
final_temp_path = pdf_path + '.tmp'
diff --git a/sf_base/controllers/controllers.py b/sf_base/controllers/controllers.py
index c8447d4e..8694170e 100644
--- a/sf_base/controllers/controllers.py
+++ b/sf_base/controllers/controllers.py
@@ -4,6 +4,7 @@ import json
import logging
from odoo import http
from odoo.http import request
+from odoo.addons.sf_base.decorators.api_log import api_log
_logger = logging.getLogger(__name__)
@@ -11,6 +12,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/MachineToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('机床刀具组', requester='中控系统')
def get_maintenance_tool_groups_Info(self, **kw):
"""
机床刀具组接口
diff --git a/sf_base/decorators/api_log.py b/sf_base/decorators/api_log.py
index 2838ec55..07a7bd00 100644
--- a/sf_base/decorators/api_log.py
+++ b/sf_base/decorators/api_log.py
@@ -27,6 +27,9 @@ def api_log(name=None, requester=None):
# 执行原始函数
result = func(*args, **kwargs)
+ origin_result = result
+ if isinstance(result, str):
+ result = json.loads(result)
# 计算响应时间
end_time = datetime.now()
@@ -41,7 +44,7 @@ def api_log(name=None, requester=None):
'response_data': json.dumps(result, ensure_ascii=False),
'remote_addr': remote_addr,
'response_time': response_time,
- 'status': result.get('code', 500),
+ 'status': result.get('code') or result.get('ErrorCode') or 500,
'requester': requester,
'responser': '智能工厂'
}
@@ -49,7 +52,7 @@ def api_log(name=None, requester=None):
# 异步创建日志记录
request.env['api.request.log'].sudo().with_context(tracking_disable=True).create(log_vals)
- return result
+ return origin_result
except Exception as e:
_logger.error(f"API日志记录失败: {str(e)}")
diff --git a/sf_base/models/api_log.py b/sf_base/models/api_log.py
index 4b630b88..7d54ac80 100644
--- a/sf_base/models/api_log.py
+++ b/sf_base/models/api_log.py
@@ -1,4 +1,9 @@
from odoo import models, fields, api
+import json, ast
+import logging
+import requests
+
+_logger = logging.getLogger(__name__)
class ApiRequestLog(models.Model):
@@ -16,3 +21,52 @@ class ApiRequestLog(models.Model):
status = fields.Integer('状态码')
requester = fields.Char('请求方')
responser = fields.Char('响应方')
+
+ @api.model
+ def log_request(self, method, url, name=None, responser=None, **kwargs):
+ # Log the request
+ request_headers = kwargs.get('headers', {})
+ request_body = kwargs.get('json') or kwargs.get('params') or {}
+
+ _logger.info(f"Request: {method} {url} Headers: {request_headers} Body: {request_body}")
+
+ # Make the actual request
+ response = requests.request(method, url, **kwargs)
+
+ # Log the response
+ response_status = response.status_code
+ response_headers = response.headers
+ response_body = response.text
+ response_time = response.elapsed.total_seconds()
+
+ _logger.info(f"Response: Status: {response_status} Headers: {response_headers} Body: {response_body}")
+
+ try:
+ # 如果是字符串,先尝试用 ast.literal_eval 安全地转换成 Python 对象
+ if isinstance(response_body, str):
+
+ response_body_obj = json.loads(response_body)
+ else:
+ response_body_obj = response_body
+
+ # 再使用 json.dumps 转换成标准的 JSON 字符串
+ response_body = json.dumps(response_body_obj, ensure_ascii=False)
+ except Exception as e:
+ _logger.warning(f"转换 response_body 到标准 JSON 失败: {str(e)}")
+ # 如果转换失败,保持原样
+
+ # Save to database
+ self.sudo().create({
+ 'name': name,
+ 'path': url,
+ 'method': method,
+ 'request_data': request_body,
+ 'response_data': response_body,
+ 'remote_addr': None,
+ 'response_time': response_time,
+ 'status': response_status,
+ 'requester': '智能工厂',
+ 'responser': responser
+ })
+
+ return response
\ No newline at end of file
diff --git a/sf_dlm_management/models/mrp_routing_workcenter.py b/sf_dlm_management/models/mrp_routing_workcenter.py
index dd9e59e3..f104cd13 100644
--- a/sf_dlm_management/models/mrp_routing_workcenter.py
+++ b/sf_dlm_management/models/mrp_routing_workcenter.py
@@ -2,8 +2,8 @@
# from odoo import fields, models, api
# from odoo.exceptions import UserError
# from odoo.tools import str2bool
-#
-#
+
+
# class ResMrpRoutingWorkcenter(models.Model):
# _inherit = 'mrp.routing.workcenter'
# def init(self):
diff --git a/sf_dlm_management/models/sf_production_common.py b/sf_dlm_management/models/sf_production_common.py
index 42cb9ff2..f3141892 100644
--- a/sf_dlm_management/models/sf_production_common.py
+++ b/sf_dlm_management/models/sf_production_common.py
@@ -3,12 +3,12 @@
# from odoo import fields, models, api
# from odoo.exceptions import UserError
# from odoo.tools import str2bool
-#
-#
+
+
# class SfProductionProcessParameter(models.Model):
# _inherit = 'sf.production.process.parameter'
-#
-#
+
+
# @api.model
# def create(self, vals):
# # if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
@@ -26,7 +26,7 @@
# def create_service_product(self):
# service_categ = self.env.ref(
# 'sf_dlm.product_category_surface_technics_sf').sudo()
-#
+
# product_name = f"{self.process_id.name}_{self.name}"
# product_id = self.env['product.template'].search(
# [("name", '=', product_name)])
@@ -48,7 +48,7 @@
# 'partner_id': res_partner.id,
# 'price': 1, })],
# })
-#
+
# def create_work_center(self):
# production_process_parameter = self
# if not production_process_parameter.process_id:
@@ -70,7 +70,7 @@
# production_process_parameter.routing_id = routing_id.id
# else:
# production_process_parameter.routing_id = workcenter_id.id
-#
+
# def init(self):
# super(SfProductionProcessParameter, self).init()
# # 在模块初始化时触发计算字段的更新
diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py
index 24c8a4b0..f30c1300 100644
--- a/sf_manufacturing/__manifest__.py
+++ b/sf_manufacturing/__manifest__.py
@@ -48,6 +48,7 @@
'views/mrp_workorder_batch_replan.xml',
'views/purchase_order_view.xml',
'views/product_template_views.xml',
+ # 'views/stock_warehouse_orderpoint.xml',
],
'assets': {
diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py
index 64a981ad..70b0b87b 100644
--- a/sf_manufacturing/controllers/controllers.py
+++ b/sf_manufacturing/controllers/controllers.py
@@ -6,12 +6,14 @@ from datetime import datetime
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
from odoo import http
from odoo.http import request
+from odoo.addons.sf_base.decorators.api_log import api_log
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('获取工单', requester='中控系统')
def get_Work_Info(self, **kw):
"""
自动化传递工单号获取工单信息
@@ -54,6 +56,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('获取日计划', requester='中控系统')
def get_ShiftPlan(self, **kw):
"""
自动化每天获取机台日计划
@@ -107,6 +110,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/QcCheck', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('工件预调(前置三元检测)', requester='中控系统')
def get_qcCheck(self, **kw):
"""
工件预调(前置三元检测)
@@ -149,6 +153,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('工单开始', requester='中控系统')
def button_Work_START(self, **kw):
"""
工单任务开始
@@ -198,6 +203,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('工单结束', requester='中控系统')
def button_Work_End(self, **kw):
"""
工单任务结束
@@ -249,6 +255,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('零件检测(后置三元检测)', requester='中控系统')
def PartQualityInspect(self, **kw):
"""
零件质检
@@ -295,6 +302,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('CMM测量程序下载', requester='中控系统')
def CMMProgDolod(self, **kw):
"""
中控系统传递RFID编号给MES,获取测量程序文件。Ftp下载文件
@@ -335,6 +343,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('CAM加工程序下载', requester='中控系统')
def NCProgDolod(self, **kw):
"""
中控系统传递RFID编号给MES,获取程序单及程序文件。Ftp下载文件
@@ -376,6 +385,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/LocationChange', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('库位变更', requester='中控系统')
def LocationChange(self, **kw):
"""
库位变更
@@ -480,6 +490,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('AGV运送上产线', requester='中控系统')
def AGVToProduct(self, **kw):
"""
AGV运送上产线(完成)
@@ -552,6 +563,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
+ @api_log('AGV运送下产线', requester='中控系统')
def AGVDownProduct(self, **kw):
"""
MES调度AGV,搬运零件AGV托盘到产线接驳站。
diff --git a/sf_manufacturing/models/__init__.py b/sf_manufacturing/models/__init__.py
index bccaf634..8af783e7 100644
--- a/sf_manufacturing/models/__init__.py
+++ b/sf_manufacturing/models/__init__.py
@@ -18,3 +18,4 @@ from . import quick_easy_order
from . import purchase_order
from . import quality_check
from . import purchase_request_line
+# from . import stock_warehouse_orderpoint
\ No newline at end of file
diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py
index 25a016e3..f52cf05d 100644
--- a/sf_manufacturing/models/mrp_production.py
+++ b/sf_manufacturing/models/mrp_production.py
@@ -928,12 +928,13 @@ class MrpProduction(models.Model):
# 'sf_stock.stock_route_process_outsourcing').id)]
# for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items():
# cur_request_line = request_line_list[0]
- # cur_request_line['product_qty'] = len(request_line_list)
+ # # cur_request_line['product_qty'] = cur_request_line['product_qty']
# cur_request_line['request_id'] = pr.id
# cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')})
# cur_request_line.pop('group_id', None)
# cur_request_line.pop('production_name', None)
# self.env["purchase.request.line"].create(cur_request_line)
+ # pr.button_approved()
# 外协出入库单处理
def get_subcontract_pick_purchase(self):
diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py
index 2c32ed9c..cd449c69 100644
--- a/sf_manufacturing/models/mrp_workorder.py
+++ b/sf_manufacturing/models/mrp_workorder.py
@@ -71,7 +71,7 @@ class ResMrpWorkOrder(models.Model):
tracking=True)
back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True)
# pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True)
- #
+
# @api.depends('state')
# def _compute_pr_mp_count(self):
# for item in self:
@@ -85,6 +85,7 @@ class ResMrpWorkOrder(models.Model):
# item.pr_mp_count = len(pr_ids)
# else:
# item.pr_mp_count = 0
+
@api.depends('state')
def _compute_back_button_display(self):
for record in self:
@@ -443,9 +444,8 @@ class ResMrpWorkOrder(models.Model):
def _compute_surface_technics_purchase_ids(self):
for order in self:
if order.routing_type == '表面工艺' and order.state not in ['cancel']:
- domain = [('purchase_type', '=', 'consignment'),
- ('origin', 'like', '%' + self.production_id.name + '%'),
- ('state', '!=', 'cancel')]
+ domain = [('group_id', '=', self.production_id.procurement_group_id.id),
+ ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')]
# domain = [('purchase_type', '=', 'consignment'),
# ('origin', 'like', '%' + self.production_id.name + '%'),
# ('state', '!=', 'cancel')]
@@ -485,6 +485,7 @@ class ResMrpWorkOrder(models.Model):
# 'view_mode': 'tree,form',
# })
# return action
+
def action_view_surface_technics_purchase(self):
self.ensure_one()
# if self.routing_type == '表面工艺':
@@ -513,7 +514,8 @@ class ResMrpWorkOrder(models.Model):
return result
def _get_surface_technics_purchase_ids(self):
- domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')]
+ domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'),
+ ('state', '!=', 'cancel')]
# domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment')]
# domain = [('group_id', '=', self.production_id.procurement_group_id.id), ('purchase_type', '=', 'consignment')]
purchase_orders = self.env['purchase.order'].search(domain, order='id desc')
@@ -1243,6 +1245,13 @@ class ResMrpWorkOrder(models.Model):
}]
return workorders_values_str
+ # def check_lot_exists(self, picking_id, lot_id):
+ # return bool(
+ # picking_id.move_ids.move_line_ids.filtered(
+ # lambda line: line.lot_id.id == lot_id
+ # )
+ # )
+
def _process_compute_state(self):
sorted_workorders = sorted(self, key=lambda x: x.sequence)
for workorder in sorted_workorders:
@@ -1264,10 +1273,17 @@ class ResMrpWorkOrder(models.Model):
workorder.state = 'pending'
continue
# ================= 如果制造订单制造类型为【人工线下加工】==========================
+ # lot_id = workorder.production_id.move_raw_ids.move_line_ids.lot_id
+ # picking_ids = workorder.production_id.picking_ids.filtered(
+ # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
+ # exists = any(
+ # move_line.lot_id == lot_id
+ # for picking in picking_ids
+ # for move in picking.move_ids
+ # for move_line in move.move_line_ids
+ # )
if (workorder.production_id.production_type == '人工线下加工'
- and workorder.production_id.schedule_state == '已排'
- and len(workorder.production_id.picking_ids.filtered(
- lambda w: w.state not in ['done', 'cancel'])) == 0):
+ and workorder.production_id.schedule_state == '已排'):
# and workorder.production_id.programming_state == '已编程'
if workorder.is_subcontract is True:
if workorder.production_id.state == 'rework':
@@ -1276,6 +1292,9 @@ class ResMrpWorkOrder(models.Model):
purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id.state == 'purchase':
workorder.state = 'ready'
+ # picking_id = workorder.production_id.picking_ids.filtered(
+ # lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
+ # move_out = picking_id.move_ids
move_out = workorder.move_subcontract_workorder_ids[1]
for mo in move_out:
if mo.state != 'done':
@@ -1316,6 +1335,10 @@ class ResMrpWorkOrder(models.Model):
if purchase_orders_id.state == 'purchase':
workorder.state = 'ready'
move_out = workorder.move_subcontract_workorder_ids[1]
+ # picking_id = workorder.production_id.picking_ids.filtered(
+ # lambda
+ # wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
+ # move_out = picking_id.move_ids
for mo in move_out:
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
@@ -1325,7 +1348,6 @@ 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')
@@ -1357,7 +1379,8 @@ class ResMrpWorkOrder(models.Model):
# 判断是否有坯料的序列号信息
boolean = False
if self.production_id.move_raw_ids:
- if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料' and self.production_id.move_raw_ids[0].product_id.tracking == 'serial':
+ if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料' and \
+ self.production_id.move_raw_ids[0].product_id.tracking == 'serial':
if self.production_id.move_raw_ids[0].move_line_ids:
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
boolean = True
@@ -1390,6 +1413,10 @@ class ResMrpWorkOrder(models.Model):
if self.routing_type == '表面工艺':
if self.is_subcontract is True:
move_out = self.move_subcontract_workorder_ids[1]
+ # picking_id = self.production_id.picking_ids.filtered(
+ # lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
+ # move_out = picking_id.move_ids
+ # move_out = self.move_subcontract_workorder_ids[1]
# move_out = self.env['stock.move'].search(
# [('location_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
@@ -1808,7 +1835,7 @@ class ResMrpWorkOrder(models.Model):
orderby=orderby,
lazy=lazy
)
-
+
model_id = fields.Char('模型ID', related='production_id.model_id')
diff --git a/sf_manufacturing/models/purchase_order.py b/sf_manufacturing/models/purchase_order.py
index 111c09e3..e967215d 100644
--- a/sf_manufacturing/models/purchase_order.py
+++ b/sf_manufacturing/models/purchase_order.py
@@ -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', '=', productions.bom_id.bom_line_ids.product_id.id),
+ # ('location_id', '=', location_id.id)
+ # ])
+ # total_qty = sum(quants.mapped('quantity')) # 计算该位置的总库存量
+ # is_available = total_qty > 0
+ # if not is_available:
+ # raise UserError('请先完成坯料入库')
+ # for production_id in productions:
+ # work_ids = production_id.workorder_ids.filtered(
+ # lambda wk: wk.state not in ['done', 'rework', 'cancel'])
+ # if not work_ids:
+ # continue
+ # min_sequence_wk = min(work_ids, key=lambda wk: wk.sequence)
+ # if min_sequence_wk.is_subcontract:
+ # picking_id = production_id.picking_ids.filtered(
+ # lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
+ # move_out = picking_id.move_ids
+ # for mo in move_out:
+ # if 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,10 @@ 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()
+
res = super(PurchaseOrder, self).button_confirm()
+
for line in self.order_line:
# 将产品不追踪序列号的行项目设置qty_done
if line.move_ids and line.move_ids[0].product_id.tracking == 'none':
diff --git a/sf_manufacturing/models/purchase_request_line.py b/sf_manufacturing/models/purchase_request_line.py
index eca52d3b..487683a0 100644
--- a/sf_manufacturing/models/purchase_request_line.py
+++ b/sf_manufacturing/models/purchase_request_line.py
@@ -1,4 +1,4 @@
-# # -*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
# import base64
# import datetime
# import logging
@@ -7,24 +7,24 @@
# import re
# import traceback
# from operator import itemgetter
-#
+
# import requests
# from itertools import groupby
# from collections import defaultdict, namedtuple
-#
+
# from odoo import api, fields, models, SUPERUSER_ID, _
# from odoo.exceptions import UserError, ValidationError
# from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
-#
-#
+
+
# class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request'
# is_subcontract = fields.Boolean(string='是否外协',default=False)
# class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request.line'
# is_subcontract = fields.Boolean(string='是否外协')
-#
-#
+
+
# class PurchaseRequest(models.Model):
# _inherit = 'purchase.request'
# bom_id = fields.Many2one('mrp.bom')
diff --git a/sf_manufacturing/models/sale_order.py b/sf_manufacturing/models/sale_order.py
index 4ce8750c..8251a78d 100644
--- a/sf_manufacturing/models/sale_order.py
+++ b/sf_manufacturing/models/sale_order.py
@@ -56,10 +56,10 @@ class SaleOrder(models.Model):
'jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo()
# 复制成品模板上的属性
- line.product_id.product_tmpl_id.copy_template(product_template_id)
+ line.product_id.copy_template(product_template_id)
# 将模板上的single_manufacturing属性复制到成品上
- line.product_id.single_manufacturing = product_template_id.single_manufacturing
- line.product_id.tracking = product_template_id.tracking
+ # line.product_id.single_manufacturing = product_template_id.single_manufacturing
+ # line.product_id.tracking = product_template_id.tracking
order_id = self
product = line.product_id
@@ -76,7 +76,7 @@ class SaleOrder(models.Model):
'embryo_redundancy_id': line.embryo_redundancy_id,
}
product_name = ''
- match = re.search(r'(S\d{5}-\d)', product.name)
+ match = re.search(r'(S\d{5}-\d*)', product.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
diff --git a/sf_manufacturing/models/sf_production_common.py b/sf_manufacturing/models/sf_production_common.py
index 2cdfd750..203dd221 100644
--- a/sf_manufacturing/models/sf_production_common.py
+++ b/sf_manufacturing/models/sf_production_common.py
@@ -20,13 +20,13 @@ class SfProductionProcessParameter(models.Model):
# is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False)
# is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False)
# routing_id = fields.Many2one('mrp.routing.workcenter', string="工序")
- #
+
# @api.depends('outsourced_service_products')
# def _compute_service_products(self):
# for record in self:
# # 假设取第一条作为主明细
- # record.service_products = record.outsourced_service_products.id if record.outsourced_service_products else False
- #
+ # record.service_products = record.outsourced_service_products.ids if record.outsourced_service_products else False
+
# def _inverse_service_products(self):
# for record in self:
# if record.service_products:
@@ -45,7 +45,7 @@ class SfProductionProcessParameter(models.Model):
# for record in self:
# if len(record.outsourced_service_products) > 1:
# raise ValidationError("工艺参数不能与多个产品关联")
- #
+
# @api.onchange('outsourced_service_products')
# def _onchange_validate_partner_limit(self):
# for record in self:
@@ -58,7 +58,7 @@ class SfProductionProcessParameter(models.Model):
# record.is_product_button = True
# else:
# record.is_product_button = False
- #
+
# def has_wksp_prefix(self):
# """
# 判断字符串是否以WKSP开头(不区分大小写)
diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py
index 8572b620..d8caae88 100644
--- a/sf_manufacturing/models/stock.py
+++ b/sf_manufacturing/models/stock.py
@@ -631,6 +631,62 @@ class StockPicking(models.Model):
move.action_clear_lines_show_details()
move.action_show_details()
res = super().button_validate()
+ # lot_ids = None
+ # product_ids = self.move_ids.mapped('product_id')
+ # if not self.move_ids[0].product_id.single_manufacturing and self.move_ids[0].product_id.tracking == 'none':
+ # lot_ids = self.move_ids.move_line_ids.mapped('lot_id')
+ # production_ids = self.sale_order_id.mrp_production_ids if self.sale_order_id else self.env['mrp.production']
+ # if res and self.location_id.name == '外协收料区' and self.location_dest_id.name == '制造前':
+ # # 如果是最后一张外协入库单,则设置库存位置的预留数量
+ # for production_id in production_ids:
+ # if lot_ids:
+ # lot_id = production_id.move_raw_ids.move_line_ids.lot_id
+ # # picking_ids = production_id.picking_ids.filtered(
+ # # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
+ # if lot_id in lot_ids:
+ # workorder_id = production_id.workorder_ids.filtered(
+ # lambda a: a.state == 'progress' and a.is_subcontract)
+ # if not workorder_id:
+ # continue
+ # workorder_id.button_finish()
+ # else:
+ # workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'progress' and a.is_subcontract)
+ # if not workorder_id:
+ # continue
+ # workorder_id.button_finish()
+ # # lot_id = workorder.production_id.move_raw_ids.move_line_ids.lot_id
+ # # picking_ids = workorder.production_id.picking_ids.filtered(
+ # # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
+
+ # # if move_in:
+ # # workorder = move_in.subcontract_workorder_id
+ # # workorders = workorder.production_id.workorder_ids
+ # # subcontract_workorders = workorders.filtered(
+ # # lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
+ # # # if workorder == subcontract_workorders[-1]:
+ # # # self.env['stock.quant']._update_reserved_quantity(
+ # # # move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
+ # # # lot_id=move_in.move_line_ids.lot_id,
+ # # # package_id=False, owner_id=False, strict=False
+ # # # )
+ # # workorder.button_finish()
+ # if res and self.location_id.name == '制造前' and self.location_dest_id.name == '外协加工区':
+ # for production_id in production_ids:
+ # if lot_ids:
+ # lot_id = production_id.move_raw_ids.move_line_ids.lot_id
+ # # picking_ids = production_id.picking_ids.filtered(
+ # # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
+ # if lot_id in lot_ids:
+ # workorder_id = production_id.workorder_ids.filtered(
+ # lambda a: a.state == 'progress' and a.is_subcontract)
+ # if not workorder_id:
+ # continue
+ # workorder_id.button_finish()
+ # else:
+ # workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'ready' and a.is_subcontract)
+ # if not workorder_id:
+ # continue
+ # workorder_id.button_start()
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
if res is True and self.picking_type_id.id == picking_type_in:
# 如果是最后一张外协入库单,则设置库存位置的预留数量
diff --git a/sf_manufacturing/models/stock_warehouse_orderpoint.py b/sf_manufacturing/models/stock_warehouse_orderpoint.py
new file mode 100644
index 00000000..09289f52
--- /dev/null
+++ b/sf_manufacturing/models/stock_warehouse_orderpoint.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Part of SmartGo. See LICENSE file for full copyright and licensing details.
+import base64
+from io import BytesIO
+from odoo import api, fields, models, SUPERUSER_ID, _
+
+
+class StockWarehouseOrderpoint(models.Model):
+ _inherit = 'stock.warehouse.orderpoint'
+ origin = fields.Char(string='来源')
+ _order = 'create_date DESC'
\ No newline at end of file
diff --git a/sf_manufacturing/views/mrp_routing_workcenter_view.xml b/sf_manufacturing/views/mrp_routing_workcenter_view.xml
index d23e1616..98968058 100644
--- a/sf_manufacturing/views/mrp_routing_workcenter_view.xml
+++ b/sf_manufacturing/views/mrp_routing_workcenter_view.xml
@@ -22,26 +22,26 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml
index c519ff70..f388ace0 100644
--- a/sf_manufacturing/views/mrp_workorder_view.xml
+++ b/sf_manufacturing/views/mrp_workorder_view.xml
@@ -144,17 +144,17 @@
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
-
-
-
-
-
-
-
-
-
-
-
+