Merge branch 'refs/heads/develop' into feature/tool_standard_library_process
# Conflicts: # sf_manufacturing/models/product_template.py
This commit is contained in:
@@ -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',
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
39
jikimo_purchase_request/models/mrp_production.py
Normal file
39
jikimo_purchase_request/models/mrp_production.py
Normal file
@@ -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
|
||||
@@ -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'
|
||||
|
||||
21
jikimo_purchase_request/views/mrp_production.xml
Normal file
21
jikimo_purchase_request/views/mrp_production.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="mrp_production_inherited_form_purchase_request" model="ir.ui.view">
|
||||
<field name="name">mrp.production.inherited.form.purchase.request</field>
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='action_view_mo_delivery']" position="before">
|
||||
<button class="oe_stat_button" name="action_view_pr_mp" type="object" icon="fa-credit-card"
|
||||
attrs="{'invisible': [('pr_mp_count', '=', 0)]}">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field name="pr_mp_count"/>
|
||||
</span>
|
||||
<span class="o_stat_text">采购申请</span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
3
jikimo_purchase_request_tier_validation/__init__.py
Normal file
3
jikimo_purchase_request_tier_validation/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
28
jikimo_purchase_request_tier_validation/__manifest__.py
Normal file
28
jikimo_purchase_request_tier_validation/__manifest__.py
Normal file
@@ -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': [
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
24
jikimo_purchase_request_tier_validation/models/models.py
Normal file
24
jikimo_purchase_request_tier_validation/models/models.py
Normal file
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -91,24 +91,30 @@ class QualityController(http.Controller):
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class QualityReportController(http.Controller):
|
||||
|
||||
@http.route('/quality/report/<int:attachment_id>', type='http', auth='public')
|
||||
def get_public_report(self, attachment_id, **kw):
|
||||
@http.route('/quality/report/<int:document_id>', type='http', auth='public')
|
||||
def get_public_report(self, document_id, **kw):
|
||||
"""提供公开访问PDF报告的控制器"""
|
||||
attachment = request.env['ir.attachment'].sudo().browse(int(attachment_id))
|
||||
document = request.env['documents.document'].sudo().browse(int(document_id))
|
||||
|
||||
# 安全检查:确保只有质检报告附件可以被访问
|
||||
if attachment.exists() and 'QC-' in attachment.name:
|
||||
# 解码Base64数据为二进制数据
|
||||
pdf_content = base64.b64decode(attachment.datas)
|
||||
# 安全检查:确保只有质检报告文档可以被访问
|
||||
if document.exists() and document.res_model == 'quality.check':
|
||||
# 获取PDF内容
|
||||
pdf_content = document.raw
|
||||
|
||||
# 返回解码后的PDF内容
|
||||
# 返回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()
|
||||
|
||||
@http.route('/quality/report/not_published', type='http', auth='public')
|
||||
def get_not_published_report(self, **kw):
|
||||
"""提供未发布报告的控制器"""
|
||||
return "报告尚未发布"
|
||||
|
||||
|
||||
@@ -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,7 +334,7 @@ class QualityCheck(models.Model):
|
||||
'sequence': len(self.report_history_ids) + 1
|
||||
})
|
||||
|
||||
# 更新流水号
|
||||
# 7. 更新其他信息
|
||||
self.serial_number += 1
|
||||
self.quality_manager = self.env.user.id
|
||||
|
||||
@@ -350,7 +345,6 @@ class QualityCheck(models.Model):
|
||||
'publish_status': 'published',
|
||||
})
|
||||
|
||||
# 返回成功消息
|
||||
return True
|
||||
|
||||
# 发布前检验零件图号、操机员、质检员
|
||||
@@ -461,17 +455,14 @@ 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):
|
||||
"""
|
||||
@@ -496,7 +487,8 @@ class QualityCheck(models.Model):
|
||||
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字符串
|
||||
@@ -601,7 +593,6 @@ class QualityCheck(models.Model):
|
||||
except Exception as e:
|
||||
raise UserError(_('删除过程中发生错误: %s') % str(e))
|
||||
|
||||
|
||||
@depends('product_id')
|
||||
def _compute_material_name(self):
|
||||
for record in self:
|
||||
|
||||
@@ -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('描述')
|
||||
|
||||
@@ -645,6 +645,7 @@
|
||||
<field name="long"/>
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="remark"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -174,7 +174,6 @@ class ResProductMo(models.Model):
|
||||
|
||||
name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False)
|
||||
|
||||
|
||||
@api.constrains('seller_ids')
|
||||
def _check_seller_ids(self):
|
||||
if self.categ_type == '表面工艺':
|
||||
@@ -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'],
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -68,14 +68,8 @@
|
||||
context="{'group_by': 'retrospect_ref'}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='picking_type_id']" position="after">
|
||||
<field name="product_id"
|
||||
string="零件图号"
|
||||
filter_domain="[('product_id.part_number', 'ilike', self)]"
|
||||
/>
|
||||
<field name="product_id"
|
||||
string="零件名称"
|
||||
filter_domain="[('product_id.part_name', 'ilike', self)]"
|
||||
/>
|
||||
<field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', 'ilike', self)]"/>
|
||||
<field name="part_names" string="零件名称" filter_domain="[('part_names', 'ilike', self)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -97,7 +91,8 @@
|
||||
<attribute name="invisible">True</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//form//button[@name='action_assign_serial_show_details']" position="after">
|
||||
<button name="button_update_the_sequence_number" type="object" class="btn-link" data-hotkey="k" title="Assign Serial Numbers">
|
||||
<button name="button_update_the_sequence_number" type="object" class="btn-link" data-hotkey="k"
|
||||
title="Assign Serial Numbers">
|
||||
<span>更新序列号</span>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
@@ -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'],
|
||||
})
|
||||
@@ -16,13 +16,24 @@
|
||||
|
||||
<!-- 二维码和报告编号 -->
|
||||
<div style="position: absolute; top: 0; right: 0; text-align: right;">
|
||||
<t t-if="o.report_number_id">
|
||||
<img t-att-src="'/report/barcode/QR/%s' % o.get_report_url()" style="width:100px;height:100px"/>
|
||||
<div style="font-size: 14px; margin-top: 5px;">
|
||||
报告编号:<span t-if="o.report_number_id" t-field="o.report_number_name"/><span t-else="">ceshi</span>
|
||||
报告编号:<span t-field="o.report_number_id"/>
|
||||
</div>
|
||||
<div style="font-size: 12px; margin-top: 5px;">
|
||||
扫描二维码查看PDF报告
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<img t-att-src="'/report/barcode/QR/%s' % o.get_report_url()" style="width:100px;height:100px"/>
|
||||
<div style="font-size: 14px; margin-top: 5px;">
|
||||
报告编号:<span>ceshi</span>
|
||||
</div>
|
||||
<div style="font-size: 12px; margin-top: 5px;">
|
||||
扫描二维码查看PDF报告
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user