diff --git a/jikimo_purchase_request/__manifest__.py b/jikimo_purchase_request/__manifest__.py
index d919b25d..dad34ddc 100644
--- a/jikimo_purchase_request/__manifest__.py
+++ b/jikimo_purchase_request/__manifest__.py
@@ -9,6 +9,7 @@
'depends': ['sf_manufacturing', 'purchase_request'],
'data': [
'views/sale_order_view.xml',
+ 'views/mrp_production.xml',
'views/purchase_request_view.xml',
'wizard/purchase_request_line_make_purchase_order_view.xml',
],
diff --git a/jikimo_purchase_request/models/__init__.py b/jikimo_purchase_request/models/__init__.py
index d27a3cd0..433b063d 100644
--- a/jikimo_purchase_request/models/__init__.py
+++ b/jikimo_purchase_request/models/__init__.py
@@ -2,5 +2,6 @@
from . import product_template
from . import purchase_request
from . import sale_order
+from . import mrp_production
from . import purchase_order
from . import stock_rule
diff --git a/jikimo_purchase_request/models/mrp_production.py b/jikimo_purchase_request/models/mrp_production.py
new file mode 100644
index 00000000..c1c0feb9
--- /dev/null
+++ b/jikimo_purchase_request/models/mrp_production.py
@@ -0,0 +1,39 @@
+from odoo import fields, models, api, _
+
+
+class MrpProduction(models.Model):
+ _inherit = 'mrp.production'
+
+ 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:
+ pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name)])
+ if pr_ids:
+ item.pr_mp_count = len(pr_ids)
+ else:
+ item.pr_mp_count = 0
+
+ def action_view_pr_mp(self):
+ """
+ 采购请求
+ """
+ self.ensure_one()
+ pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)])
+ action = {
+ 'res_model': 'purchase.request',
+ 'type': 'ir.actions.act_window',
+ }
+ if len(pr_ids) == 1:
+ action.update({
+ 'view_mode': 'form',
+ 'res_id': pr_ids[0].id,
+ })
+ else:
+ action.update({
+ 'name': _("从 %s生成采购请求单", self.name),
+ 'domain': [('id', 'in', pr_ids)],
+ 'view_mode': 'tree,form',
+ })
+ return action
diff --git a/jikimo_purchase_request/models/purchase_request.py b/jikimo_purchase_request/models/purchase_request.py
index 4b5f86ba..9e610612 100644
--- a/jikimo_purchase_request/models/purchase_request.py
+++ b/jikimo_purchase_request/models/purchase_request.py
@@ -1,4 +1,5 @@
import re
+import ast
from odoo import models, fields, api
@@ -20,6 +21,13 @@ class PurchaseRequest(models.Model):
if pr.state != 'draft' and pr.rule_new_add:
pr.rule_new_add = False
+ def action_view_purchase_order(self):
+ action = super(PurchaseRequest, self).action_view_purchase_order()
+ origin_context = ast.literal_eval(action['context'])
+ if 'search_default_draft' in origin_context:
+ origin_context.pop('search_default_draft')
+ action['context'] = origin_context
+ return action
class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request.line'
diff --git a/jikimo_purchase_request/views/mrp_production.xml b/jikimo_purchase_request/views/mrp_production.xml
new file mode 100644
index 00000000..0f59a396
--- /dev/null
+++ b/jikimo_purchase_request/views/mrp_production.xml
@@ -0,0 +1,21 @@
+
+
+
+ mrp.production.inherited.form.purchase.request
+ mrp.production
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jikimo_purchase_request_tier_validation/__init__.py b/jikimo_purchase_request_tier_validation/__init__.py
new file mode 100644
index 00000000..cde864ba
--- /dev/null
+++ b/jikimo_purchase_request_tier_validation/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import models
diff --git a/jikimo_purchase_request_tier_validation/__manifest__.py b/jikimo_purchase_request_tier_validation/__manifest__.py
new file mode 100644
index 00000000..403297ab
--- /dev/null
+++ b/jikimo_purchase_request_tier_validation/__manifest__.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+{
+ 'name': "机企猫 采购审批流程",
+
+ 'summary': """
+ Short (1 phrase/line) summary of the module's purpose, used as
+ subtitle on modules listing or apps.openerp.com""",
+
+ 'description': """
+ Long description of module's purpose
+ """,
+
+ 'author': "My Company",
+ 'website': "https://www.yourcompany.com",
+
+ # Categories can be used to filter modules in modules listing
+ # Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
+ # for the full list
+ 'category': 'Uncategorized',
+ 'version': '0.1',
+
+ # any module necessary for this one to work correctly
+ 'depends': ['purchase_request_tier_validation'],
+
+ # always loaded
+ 'data': [
+ ],
+}
diff --git a/jikimo_purchase_request_tier_validation/models/__init__.py b/jikimo_purchase_request_tier_validation/models/__init__.py
new file mode 100644
index 00000000..5305644d
--- /dev/null
+++ b/jikimo_purchase_request_tier_validation/models/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import models
\ No newline at end of file
diff --git a/jikimo_purchase_request_tier_validation/models/models.py b/jikimo_purchase_request_tier_validation/models/models.py
new file mode 100644
index 00000000..0070a4c7
--- /dev/null
+++ b/jikimo_purchase_request_tier_validation/models/models.py
@@ -0,0 +1,24 @@
+from odoo import models, fields, api, _
+from odoo.exceptions import ValidationError
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+class PurchaseRequest(models.Model):
+ _inherit = 'purchase.request'
+
+
+ def _validate_tier(self, tiers=False):
+ res = super(PurchaseRequest, self)._validate_tier(tiers)
+
+ # 检查是否所有审批都已通过
+ all_approved = all(
+ tier_review.status == 'approved'
+ for tier_review in self.review_ids
+ )
+
+ if self.review_ids and all_approved: # 确保有审批记录
+ self.state = 'approved'
+
+ return res
diff --git a/jikimo_purchase_tier_validation/__manifest__.py b/jikimo_purchase_tier_validation/__manifest__.py
index 4d676d8f..ed0cecb7 100644
--- a/jikimo_purchase_tier_validation/__manifest__.py
+++ b/jikimo_purchase_tier_validation/__manifest__.py
@@ -1,14 +1,12 @@
# -*- coding: utf-8 -*-
{
- 'name': "机企猫 采购审批流程",
+ 'name': "机企猫 采购申请审批流程",
'summary': """
- Short (1 phrase/line) summary of the module's purpose, used as
- subtitle on modules listing or apps.openerp.com""",
+ 采购申请审批流程""",
'description': """
- Long description of module's purpose
- """,
+ 采购申请审批流程""",
'author': "My Company",
'website': "https://www.yourcompany.com",
diff --git a/quality_control/controllers/main.py b/quality_control/controllers/main.py
index 5bbe71a9..95b11f5c 100644
--- a/quality_control/controllers/main.py
+++ b/quality_control/controllers/main.py
@@ -90,25 +90,31 @@ class QualityController(http.Controller):
('Access-Control-Allow-Headers', 'Content-Type, Authorization')
]
)
-
+
+
class QualityReportController(http.Controller):
-
- @http.route('/quality/report/', type='http', auth='public')
- def get_public_report(self, attachment_id, **kw):
+ @http.route('/quality/report/', type='http', auth='public')
+ def get_public_report(self, document_id, **kw):
"""提供公开访问PDF报告的控制器"""
- attachment = request.env['ir.attachment'].sudo().browse(int(attachment_id))
-
- # 安全检查:确保只有质检报告附件可以被访问
- if attachment.exists() and 'QC-' in attachment.name:
- # 解码Base64数据为二进制数据
- pdf_content = base64.b64decode(attachment.datas)
-
- # 返回解码后的PDF内容
+ document = request.env['documents.document'].sudo().browse(int(document_id))
+
+ # 安全检查:确保只有质检报告文档可以被访问
+ if document.exists() and document.res_model == 'quality.check':
+ # 获取PDF内容
+ pdf_content = document.raw
+
+ # 返回PDF内容
return request.make_response(
pdf_content,
headers=[
('Content-Type', 'application/pdf'),
- ('Content-Disposition', f'inline; filename={attachment.name}')
+ ('Content-Disposition', f'inline; filename={document.name}.pdf')
]
)
- return request.not_found()
\ No newline at end of file
+ return request.not_found()
+
+ @http.route('/quality/report/not_published', type='http', auth='public')
+ def get_not_published_report(self, **kw):
+ """提供未发布报告的控制器"""
+ return "报告尚未发布"
+
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index 022dc6b7..412305b2 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -183,10 +183,12 @@ class QualityCheck(models.Model):
report_number_name = fields.Char('出厂检验报告编号名称', compute='_compute_report_number_name')
old_report_name = fields.Char('旧出厂检验报告编号', default='')
+
@api.depends('serial_number', 'part_number')
def _compute_report_number_name(self):
for record in self:
- str_serial_number = '0' + str(record.serial_number) if record.serial_number < 10 else str(record.serial_number)
+ str_serial_number = '0' + str(record.serial_number) if record.serial_number < 10 else str(
+ record.serial_number)
str_part_number = record.part_number if record.part_number else ''
record.report_number_name = f'FQC{str_part_number}{str_serial_number}'
@@ -284,25 +286,7 @@ class QualityCheck(models.Model):
"""实际执行发布操作的方法"""
self.ensure_one()
- # 1. 获取报告动作
- report_action = self.env.ref('sf_quality.action_report_quality_inspection')
-
- # 2. 生成PDF报告 - 修改这里的调用方式
- pdf_content, _ = report_action._render_qweb_pdf(
- report_ref=report_action.report_name, # 添加report_ref参数
- res_ids=self.ids
- )
-
- # attachment = self.env['ir.attachment'].create({
- # 'name': f'{self.name}.pdf',
- # 'type': 'binary',
- # 'datas': b64encode(pdf_content),
- # 'res_model': self._name,
- # 'res_id': self.id,
- # 'mimetype': 'application/pdf',
- # })
-
- # 获取已发布的文档文件夹
+ # 1. 获取已发布的文档文件夹
workspace = self.env['documents.folder'].search(
[('parent_folder_id', '=', self.env.ref('sf_quality.documents_purchase_contracts_folder').id),
('name', '=', '已发布')], limit=1)
@@ -310,11 +294,9 @@ class QualityCheck(models.Model):
if self.serial_number > 99:
raise UserError(_('流水号不能大于99'))
- # 3. 创建文档记录
+ # 2. 先创建空文档记录
doc_vals = {
'name': self.report_number_name,
- 'raw': pdf_content,
- # 'attachment_id': attachment.id,
'mimetype': 'application/pdf',
'res_id': self.id,
'folder_id': workspace.id,
@@ -322,13 +304,26 @@ class QualityCheck(models.Model):
}
doc = self.env['documents.document'].create(doc_vals)
- # 关联到当前质检记录
+
+ # 3. 关联文档到质检记录
self.write({
'report_number_id': doc.id,
'quality_state': 'pass'
})
- # 记录发布历史
+ # 4. 获取报告动作并生成PDF(此时二维码将包含正确的文档ID)
+ report_action = self.env.ref('sf_quality.action_report_quality_inspection')
+ pdf_content, _ = report_action._render_qweb_pdf(
+ report_ref=report_action.report_name,
+ res_ids=self.ids
+ )
+
+ # 5. 更新文档内容
+ doc.write({
+ 'raw': pdf_content
+ })
+
+ # 6. 记录发布历史
self.env['quality.check.report.history'].create({
'check_id': self.id,
'report_number_id': doc.id,
@@ -339,18 +334,17 @@ class QualityCheck(models.Model):
'sequence': len(self.report_history_ids) + 1
})
- # 更新流水号
+ # 7. 更新其他信息
self.serial_number += 1
self.quality_manager = self.env.user.id
if self.publish_status == 'canceled' and self.picking_id.state == 'done':
self.upload_factory_report()
-
+
self.write({
'publish_status': 'published',
})
- # 返回成功消息
return True
# 发布前检验零件图号、操机员、质检员
@@ -397,7 +391,7 @@ class QualityCheck(models.Model):
self.report_number_id.write({
'folder_id': self.env.ref('sf_quality.documents_purchase_contracts_folder_canceled').id,
})
-
+
# 3. 记录发布历史
self.env['quality.check.report.history'].create({
'check_id': self.id,
@@ -408,7 +402,7 @@ class QualityCheck(models.Model):
'document_status': 'canceled',
'sequence': len(self.report_history_ids) + 1
})
-
+
self.write({
'old_report_name': self.report_number_id.name
})
@@ -442,7 +436,7 @@ class QualityCheck(models.Model):
width=140, height=140)
)
)
-
+
def get_latest_report_attachment(self, check_id):
"""获取指定质检记录的最新报告附件,并删除旧的报告附件"""
# 查找特定质检记录的所有附件
@@ -459,20 +453,17 @@ class QualityCheck(models.Model):
# 返回最新的附件(如果存在)
return attachments and attachments[0] or False
-
+
def get_report_url(self):
- """生成报告访问URL,确保获取最新版本"""
+ """生成报告访问URL"""
+ self.ensure_one()
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
- report_url = f"{base_url}/web/content/ir.attachment"
-
- # 获取最新附件的ID
- latest_attachment = self.get_latest_report_attachment(self.id)
- if latest_attachment:
- # 生成包含附件ID的URL
- print(f"{base_url}/quality/report/{latest_attachment.id}")
- return f"{base_url}/quality/report/{latest_attachment.id}"
- return False
-
+ if self.report_number_id:
+ print(f"{base_url}/quality/report/{self.report_number_id.id}")
+ return f"{base_url}/quality/report/{self.report_number_id.id}"
+ else:
+ return f"{base_url}/quality/report/not_published"
+
def upload_factory_report(self):
"""
上传出厂检验报告到加工订单明细中
@@ -481,39 +472,40 @@ class QualityCheck(models.Model):
self.ensure_one()
if not self.report_content:
raise UserError(_('当前质检单没有出厂检验报告,请先发布报告'))
-
+
if not self.product_id.model_name:
raise UserError(_('产品模型名称为空'))
-
+
if not self.picking_id or not self.picking_id.origin:
raise UserError(_('无法找到相关的调拨单或来源单据'))
-
+
# 获取订单号(从调拨单的来源字段获取)
order_ref = self.picking_id.retrospect_ref
-
+
try:
# 准备请求数据
payload = {
"order_ref": order_ref,
"model_name": self.product_id.model_name,
- "report_file": self.report_content.decode('utf-8') if isinstance(self.report_content, bytes) else self.report_content
+ "report_file": self.report_content.decode('utf-8') if isinstance(self.report_content,
+ bytes) else self.report_content
}
-
+
# 将Python字典转换为JSON字符串
json_data = json.dumps(payload)
-
+
# 获取服务器URL
base_url = self.env['ir.config_parameter'].sudo().get_param('bfm_url_new')
api_url = f"{base_url}/api/report/create"
-
+
# 设置请求头
headers = {
'Content-Type': 'application/json',
}
-
+
# 发送POST请求
response = requests.post(api_url, data=json_data, headers=headers)
-
+
# 处理响应
if response.status_code == 200:
result = response.json()
@@ -536,10 +528,10 @@ class QualityCheck(models.Model):
else:
# HTTP请求失败
raise UserError(_('请求失败,状态码: %s') % response.status_code)
-
+
except Exception as e:
raise UserError(_('上传过程中发生错误: %s') % str(e))
-
+
def delete_factory_report(self):
"""
删除加工订单明细中的出厂检验报告
@@ -549,7 +541,7 @@ class QualityCheck(models.Model):
if not order_ref:
raise UserError(_('无法找到相关的调拨单或来源单据'))
-
+
if not self.product_id.model_name:
raise UserError(_('产品模型名称为空'))
@@ -558,11 +550,11 @@ class QualityCheck(models.Model):
payload = {
"order_ref": order_ref,
"model_name": self.product_id.model_name
- }
-
+ }
+
# 将Python字典转换为JSON字符串
json_data = json.dumps(payload)
-
+
# 获取服务器URL
base_url = self.env['ir.config_parameter'].sudo().get_param('bfm_url_new')
api_url = f"{base_url}/api/report/delete"
@@ -597,10 +589,9 @@ class QualityCheck(models.Model):
else:
# HTTP请求失败
raise UserError(_('请求失败,状态码: %s') % response.status_code)
-
+
except Exception as e:
- raise UserError(_('删除过程中发生错误: %s') % str(e))
-
+ raise UserError(_('删除过程中发生错误: %s') % str(e))
@depends('product_id')
def _compute_material_name(self):
diff --git a/sf_base/models/base.py b/sf_base/models/base.py
index d7a71e1e..54a1fe1f 100644
--- a/sf_base/models/base.py
+++ b/sf_base/models/base.py
@@ -421,3 +421,4 @@ class EmbryoRedundancy(models.Model):
width = fields.Float('宽度(mm)', required=True)
height = fields.Float('高度(mm)', required=True)
active = fields.Boolean('有效', default=True)
+ remark = fields.Char('描述')
diff --git a/sf_base/views/base_view.xml b/sf_base/views/base_view.xml
index bd6f103b..ccac31c9 100644
--- a/sf_base/views/base_view.xml
+++ b/sf_base/views/base_view.xml
@@ -645,6 +645,7 @@
+
diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py
index 0528d65d..3554967c 100644
--- a/sf_manufacturing/models/product_template.py
+++ b/sf_manufacturing/models/product_template.py
@@ -173,7 +173,6 @@ class ResProductMo(models.Model):
'压紧方式', domain=[('type', '=', '压紧方式')])
name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False)
-
@api.constrains('seller_ids')
def _check_seller_ids(self):
@@ -896,8 +895,9 @@ class ResProductMo(models.Model):
'model_long': self.format_float(item['model_long'] + embryo_redundancy_id.long),
'model_width': self.format_float(item['model_width'] + embryo_redundancy_id.width),
'model_height': self.format_float(item['model_height'] + embryo_redundancy_id.height),
- 'model_volume': self.format_float(item['blank_volume']),
- 'model_area': self.format_float(item['blank_area']),
+ 'model_volume': self.format_float((item['model_long'] + embryo_redundancy_id.long) * (
+ item['model_width'] + embryo_redundancy_id.width) * (
+ item['model_height'] + embryo_redundancy_id.height)),
'product_model_type_id': model_type.id,
'model_processing_panel': item['processing_panel_detail'],
'model_machining_precision': item['model_machining_precision'],
@@ -1114,9 +1114,9 @@ class ResProductMo(models.Model):
image_data = fileObj.read()
base64_data = base64.b64encode(image_data)
return base64_data
-
+
# 增加产品表面积
-
+
class ResProductFixture(models.Model):
diff --git a/sf_manufacturing/models/purchase_order.py b/sf_manufacturing/models/purchase_order.py
index 3d71b6c7..044b68e1 100644
--- a/sf_manufacturing/models/purchase_order.py
+++ b/sf_manufacturing/models/purchase_order.py
@@ -59,18 +59,15 @@ class PurchaseOrder(models.Model):
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
purchase.production_count = len(production_id)
- # def button_confirm(self):
- # super().button_confirm()
- # workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id), ('state', '!=', 'cancel')])
- # for workorder in workorders:
- # if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True:
- # move_out = workorder.move_subcontract_workorder_ids[1]
- # 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(workorder.production_id, workorder))
- # return True
+ def button_confirm(self):
+ for record in self:
+ for line in record.order_line:
+ if line.product_qty <= 0:
+ raise UserError('请对【产品】中的【数量】进行输入')
+ if line.price_unit <= 0:
+ raise UserError('请对【产品】中的【单价】进行输入')
+ return super(PurchaseOrder, self).button_confirm()
+
origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id')
origin_sale_ids = fields.Many2many('sale.order', string='销售订单号(多个)', store=True,
diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py
index 245c1b11..051f0507 100644
--- a/sf_manufacturing/models/stock.py
+++ b/sf_manufacturing/models/stock.py
@@ -564,6 +564,18 @@ class StockPicking(models.Model):
sale_order_id = fields.Many2one('sale.order', '销售单号', compute='_compute_move_ids', store=True)
picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code')
+ part_numbers = fields.Char(string="零件图号", compute='_compute_part_info', store=True, index=True)
+ part_names = fields.Char(string="零件名称", compute='_compute_part_info', store=True, index=True)
+
+ @api.depends('move_ids_without_package.part_number', 'move_ids_without_package.part_name')
+ def _compute_part_info(self):
+ for picking in self:
+ # 聚合所有关联行的 part_number 和 part_name
+ part_numbers = picking.move_ids_without_package.mapped('part_number')
+ part_names = picking.move_ids_without_package.mapped('part_name')
+ picking.part_numbers = ','.join(filter(None, part_numbers))
+ picking.part_names = ','.join(filter(None, part_names))
+
@api.depends('move_ids', 'move_ids.product_id')
def _compute_move_ids(self):
for item in self:
diff --git a/sf_manufacturing/views/stock_picking_view.xml b/sf_manufacturing/views/stock_picking_view.xml
index 3cb877a9..f5b1941b 100644
--- a/sf_manufacturing/views/stock_picking_view.xml
+++ b/sf_manufacturing/views/stock_picking_view.xml
@@ -68,14 +68,8 @@
context="{'group_by': 'retrospect_ref'}"/>
-
-
+
+
@@ -97,7 +91,8 @@
True
-
diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py
index cd0d8405..652ec3eb 100644
--- a/sf_mrs_connect/models/sync_common.py
+++ b/sf_mrs_connect/models/sync_common.py
@@ -3214,6 +3214,7 @@ class EmbryoRedundancySync(models.Model):
embryo_redundancy.width = item['width']
embryo_redundancy.height = item['height']
embryo_redundancy.active = item['active']
+ embryo_redundancy.remark = item['remark']
else:
self.env['sf.embryo.redundancy'].sudo().create({
"name": item['name'],
@@ -3222,4 +3223,5 @@ class EmbryoRedundancySync(models.Model):
"width": item['width'],
"height": item['height'],
"active": item['active'],
+ "remark": item['remark'],
})
\ No newline at end of file
diff --git a/sf_quality/data/insepection_report_template.xml b/sf_quality/data/insepection_report_template.xml
index f605b3e4..8362a589 100644
--- a/sf_quality/data/insepection_report_template.xml
+++ b/sf_quality/data/insepection_report_template.xml
@@ -16,13 +16,24 @@
-
![]()
-
- 报告编号:ceshi
-
-
- 扫描二维码查看PDF报告
-
+
+
+
+ 报告编号:
+
+
+ 扫描二维码查看PDF报告
+
+
+
+
+
+ 报告编号:ceshi
+
+
+ 扫描二维码查看PDF报告
+
+