Compare commits

..

59 Commits

Author SHA1 Message Date
guanhuan
000cd9a13c 修复坯料发料提醒重复发送 2025-02-27 09:53:42 +08:00
guanhuan
a628826b52 采购协议 2025-02-19 11:34:58 +08:00
guanhuan
bdccce07d4 采购协议 2025-02-19 11:31:46 +08:00
guanhuan
4dc19cab81 采购协议 2025-02-19 10:42:48 +08:00
马广威
f25d06e08f Accept Merge Request #1799: (feature/制造功能优化 -> develop)
Merge Request: 调整工单处人工编程可见

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1799?initial=true
2025-02-10 13:35:06 +08:00
mgw
62314d38b3 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 13:34:06 +08:00
mgw
027be2b021 调整工单处人工编程可见 2025-02-10 13:33:46 +08:00
马广威
372dfd99c4 Accept Merge Request #1798: (feature/制造功能优化 -> develop)
Merge Request: 人工线下加工工单标识

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1798?initial=true
2025-02-10 13:15:01 +08:00
mgw
c702d1f3be Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 13:14:25 +08:00
mgw
7e33ca44d0 人工线下加工工单标识 2025-02-10 13:13:57 +08:00
马广威
c7d9b15624 Accept Merge Request #1797: (feature/制造功能优化 -> develop)
Merge Request: 增加人工线下加工工单的查找

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1797
2025-02-10 11:30:52 +08:00
mgw
a4d640acb5 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 11:29:56 +08:00
mgw
523cc4ac5e 增加人工线下加工工单的查找 2025-02-10 11:29:37 +08:00
马广威
9aa2bd5d54 Accept Merge Request #1796: (feature/制造功能优化 -> develop)
Merge Request: 人工线下加工编程方式设置

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1796
2025-02-10 11:00:17 +08:00
mgw
ae23edb9d5 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 10:58:19 +08:00
mgw
9695997542 人工线下加工编程方式设置 2025-02-10 10:58:01 +08:00
管欢
90243a7d2f Accept Merge Request #1795: (feature/取消加工订单 -> develop)
Merge Request: 月结取消订单

Created By: @管欢
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @管欢
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1795
2025-02-10 09:53:46 +08:00
guanhuan
5dc5602e3d 月结取消订单 2025-02-10 09:47:42 +08:00
胡尧
cbf58fd3fb Accept Merge Request #1794: (feature/mrp_bug_fixed -> develop)
Merge Request: 解决采购总监选择销售员报错的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1794?initial=true
2025-02-10 09:30:49 +08:00
马广威
0ada4933ba Accept Merge Request #1793: (feature/制造功能优化 -> develop)
Merge Request: 人工线下加工默认加工面ZM

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1793?initial=true
2025-02-10 09:14:33 +08:00
mgw
b1261bb91b Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 09:13:16 +08:00
mgw
ffde591b3c 人工线下加工默认加工面ZM 2025-02-10 09:12:58 +08:00
胡尧
356e1e55e4 解决采购总监选择销售员报错的问题 2025-02-10 09:03:13 +08:00
马广威
b6ce462171 Accept Merge Request #1792: (feature/制造功能优化 -> develop)
Merge Request: 增加人工线下加工程序写入

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1792?initial=true
2025-02-08 16:49:34 +08:00
mgw
e8b5ff0b75 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-08 16:48:59 +08:00
mgw
ac3b50c534 增加人工线下加工程序写入 2025-02-08 16:48:36 +08:00
马广威
e7bc434d60 Accept Merge Request #1791: (feature/制造功能优化 -> develop)
Merge Request: 调整程序写入逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1791?initial=true
2025-02-08 16:01:11 +08:00
mgw
2de501b61d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-08 16:00:25 +08:00
mgw
976d75973d 调整程序写入逻辑 2025-02-08 16:00:07 +08:00
马广威
e20bf3816a Accept Merge Request #1790: (feature/制造功能优化 -> develop)
Merge Request: 人工线下加工工单也需要走cloud编程流程的优化需求

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1790?initial=true
2025-02-08 14:10:19 +08:00
mgw
44f29d001e 调整人工线下加工页面内容 2025-02-08 14:08:53 +08:00
mgw
aff81f0b3a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-08 13:32:59 +08:00
mgw
88840abf9f 制造订单编程状态搜索的优化需求 2025-02-08 13:32:40 +08:00
胡尧
332276752a Accept Merge Request #1789: (feature/mrp_bug_fixed -> develop)
Merge Request: 修复bug

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1789?initial=true
2025-02-07 10:54:56 +08:00
胡尧
d852cf54df 修复bug 2025-02-07 10:49:17 +08:00
管欢
6756cc201c Accept Merge Request #1788: (feature/工单返工优化 -> develop)
Merge Request: 人工线下加工工单下发通知修改

Created By: @管欢
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @管欢
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1788
2025-02-06 11:14:52 +08:00
guanhuan
06dd9c12e0 人工线下加工工单下发通知修改 2025-02-06 10:43:11 +08:00
mgw
978e427734 调整制造订单集合 2025-02-05 17:44:32 +08:00
mgw
dd35a6ae5f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-05 17:42:10 +08:00
mgw
0a8cc050b8 人工线下加工工单也需要走cloud编程流程的优化需求 2025-02-05 17:41:52 +08:00
管欢
eb47e4c963 Accept Merge Request #1787: (feature/工单返工优化 -> develop)
Merge Request: CNC工单返工

Created By: @管欢
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @管欢
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1787
2025-01-23 16:48:24 +08:00
guanhuan
5636681ee6 CNC工单返工 2025-01-23 16:42:16 +08:00
guanhuan
9b49edb290 装夹工单返工 2025-01-23 13:55:35 +08:00
胡尧
39b29960e3 Accept Merge Request #1786: (feature/验证合并的逻辑 -> develop)
Merge Request: Merge branch 'develop' into feature/验证合并的逻辑

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1786?initial=true
2025-01-21 10:40:27 +08:00
胡尧
6abb237491 Merge branch 'develop' into feature/验证合并的逻辑 2025-01-21 10:39:41 +08:00
胡尧
b0c043676c Accept Merge Request #1785: (release/release_2.8 -> develop)
Merge Request: 优化工件下产线,修改CNC加工工单状态为待检测时,同步修改对应质检单状态为待处理

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1785?initial=true
2025-01-21 10:39:19 +08:00
yuxianghui
8f9b7b2fb0 优化工件下产线,修改CNC加工工单状态为待检测时,同步修改对应质检单状态为待处理 2025-01-20 15:39:08 +08:00
mgw
32255af3a3 申请编程状态 2025-01-20 14:53:26 +08:00
yuxianghui
955b6a6213 工单优化 2025-01-20 14:47:09 +08:00
yuxianghui
8e788d3745 处理CNC加工工单通过接口上产线下产线变更为待检测时,对应质检单还是等待状态问题 2025-01-20 14:18:02 +08:00
guanhuan
dc843588e9 制造列表新增编程状态显示 2025-01-20 14:09:23 +08:00
马广威
8bfe5eca03 Accept Merge Request #1783: (feature/制造功能优化 -> develop)
Merge Request: 申请编程状态控制

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1783?initial=true
2025-01-20 14:04:27 +08:00
mgw
dbcf8b1089 申请编程状态控制 2025-01-20 14:02:54 +08:00
yuxianghui
0bf701e743 质检单状态优化 2025-01-20 12:55:12 +08:00
yuxianghui
a96a9f5b75 1、修改工单检测结果只读条件;2、修改质检单完成时同步完成工单判断条件;3、去除只有配置后置三元检测的工单才能看到质检单对应字段、页签的判断条件 2025-01-20 12:47:11 +08:00
yuxianghui
9e2704f726 新增工单送检按钮显示的时候隐藏完成按钮 2025-01-20 10:34:32 +08:00
胡尧
c436bbea46 Merge branch 'release/release_2.8' into feature/验证合并的逻辑 2025-01-17 16:12:27 +08:00
胡尧
47c73ae66e 处理订单行参数说明的精度问题 2025-01-17 15:20:50 +08:00
胡尧
19e1b16122 屏蔽将制造订单的补货组修改为不同的代码 2025-01-17 09:27:38 +08:00
29 changed files with 357 additions and 629 deletions

View File

@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from . import controllers
from . import models from . import models
from . import wizards from . import wizards

View File

@@ -24,9 +24,6 @@
# always loaded # always loaded
'data': [ 'data': [
'security/ir.model.access.csv',
'data/documents_data.xml',
'wizards/upload_file_wizard_view.xml',
'views/views.xml', 'views/views.xml',
], ],
# only loaded in demonstration mode # only loaded in demonstration mode

View File

@@ -1,3 +0,0 @@
# -*- coding: utf-8 -*-
from . import controllers

View File

@@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# from odoo import http
# class JikimoPurchaseTierValidation(http.Controller):
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation', auth='public')
# def index(self, **kw):
# return "Hello, world"
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation/objects', auth='public')
# def list(self, **kw):
# return http.request.render('jikimo_purchase_tier_validation.listing', {
# 'root': '/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation',
# 'objects': http.request.env['jikimo_purchase_tier_validation.jikimo_purchase_tier_validation'].search([]),
# })
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation/objects/<model("jikimo_purchase_tier_validation.jikimo_purchase_tier_validation"):obj>', auth='public')
# def object(self, obj, **kw):
# return http.request.render('jikimo_purchase_tier_validation.object', {
# 'object': obj
# })

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- 创建采购合同文件夹 -->
<record id="documents_purchase_contracts_folder" model="documents.folder">
<field name="name">采购合同</field>
<field name="description">存放采购合同相关文件</field>
<field name="sequence">10</field>
</record>
</data>
</odoo>

View File

@@ -1,30 +0,0 @@
<odoo>
<data>
<!--
<record id="object0" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
<field name="name">Object 0</field>
<field name="value">0</field>
</record>
<record id="object1" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
<field name="name">Object 1</field>
<field name="value">10</field>
</record>
<record id="object2" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
<field name="name">Object 2</field>
<field name="value">20</field>
</record>
<record id="object3" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
<field name="name">Object 3</field>
<field name="value">30</field>
</record>
<record id="object4" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
<field name="name">Object 4</field>
<field name="value">40</field>
</record>
-->
</data>
</odoo>

View File

@@ -21,12 +21,8 @@ class jikimo_purchase_tier_validation(models.Model):
def button_confirm(self): def button_confirm(self):
for record in self: for record in self:
# if record.need_validation and record.validation_status != 'validated':
# raise ValidationError(_('此操作需要至少对一条记录进行审批。\n请发起审批申请。'))
if record.state in ['to approve']: if record.state in ['to approve']:
raise ValidationError(_('请先完成审批。')) raise ValidationError(_('请先完成审批。'))
# if record.state == 'approved':
# record.state = 'purchase'
res = super(jikimo_purchase_tier_validation, self).button_confirm() res = super(jikimo_purchase_tier_validation, self).button_confirm()
for record in self: for record in self:
if record.state == 'approved': if record.state == 'approved':
@@ -39,45 +35,8 @@ class jikimo_purchase_tier_validation(models.Model):
record.message_subscribe([record.partner_id.id]) record.message_subscribe([record.partner_id.id])
return res return res
# def button_confirm(self):
# self = self.with_context(skip_validation=True)
# return super().button_confirm()
#
# def _check_state_conditions(self, vals):
# self.ensure_one()
# if self._context.get('skip_validation'):
# return False
# return (
# self._check_state_from_condition()
# and vals.get(self._state_field) in self._state_to
# )
def request_validation(self): def request_validation(self):
for record in self: for record in self:
error_messages = []
# 检查必填字段
required_fields = {
'partner_ref': '合同名称',
'contract_number': '合同编号'
}
missing_fields = [
name for field, name in required_fields.items()
if not record[field]
]
if missing_fields:
error_messages.append('* 如下字段要求必须填写:%s' % ''.join(missing_fields))
# 检查合同文件
if not record.contract_document_id:
error_messages.append('* 必须点击上传合同文件')
# 如果有任何错误,一次性显示所有错误信息
if error_messages:
raise ValidationError('\n'.join(error_messages))
# 添加通知消息 # 添加通知消息
if hasattr(record, 'message_post'): if hasattr(record, 'message_post'):
current_user = self.env.user.name current_user = self.env.user.name

View File

@@ -1,2 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ir_attachment_wizard,ir.attachment.wizard,model_ir_attachment_wizard,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ir_attachment_wizard ir.attachment.wizard model_ir_attachment_wizard base.group_user 1 1 1 1

View File

@@ -1,24 +0,0 @@
<odoo>
<data>
<!--
<template id="listing">
<ul>
<li t-foreach="objects" t-as="object">
<a t-attf-href="#{ root }/objects/#{ object.id }">
<t t-esc="object.display_name"/>
</a>
</li>
</ul>
</template>
<template id="object">
<h1><t t-esc="object.display_name"/></h1>
<dl>
<t t-foreach="object._fields" t-as="field">
<dt><t t-esc="field"/></dt>
<dd><t t-esc="object[field]"/></dd>
</t>
</dl>
</template>
-->
</data>
</odoo>

View File

@@ -23,76 +23,10 @@
<xpath expr="//header/field[@name='state']" position="replace"> <xpath expr="//header/field[@name='state']" position="replace">
<field name="state" widget="statusbar" statusbar_visible="draft,sent,to approve, approved, purchase" readonly="1"/> <field name="state" widget="statusbar" statusbar_visible="draft,sent,to approve, approved, purchase" readonly="1"/>
</xpath> </xpath>
<xpath expr="//header/button[last()]" position="after"> <xpath expr="//header/button[last()]" position="after">
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" /> <button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" />
</xpath> </xpath>
<xpath expr="//header/button[@name='action_rfq_send'][1]" position="before">
<field name="validation_status" invisible="1"/>
<field name="is_upload_contract_file" invisible="1"/>
<button name="upload_contract_file" string="上传合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', '|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', True), ('state', 'not in', ['draft', 'sent'])]}"/>]}"/>
<button name="delete_contract_file" string="删除合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', False)]}"/>
</xpath>
<xpath expr="//notebook/page[1]" position="before">
<page string="合同" name="contract_documents"
attrs="{'invisible': [('contract_document_id', '=', False)]}"
autofocus="autofocus">
<group>
<group>
<field name="contract_document_id" invisible="1"/>
<field name="contract_file_name" invisible="1"/>
<field name="contract_file"
widget="adaptive_viewer"
filename="contract_file_name"/>
</group>
</group>
</page>
</xpath>
</field> </field>
</record> </record>
<!-- actions opening views on models -->
<!--
<record model="ir.actions.act_window" id="jikimo_purchase_tier_validation.action_window">
<field name="name">jikimo_purchase_tier_validation window</field>
<field name="res_model">jikimo_purchase_tier_validation.jikimo_purchase_tier_validation</field>
<field name="view_mode">tree,form</field>
</record>
-->
<!-- server action to the one above -->
<!--
<record model="ir.actions.server" id="jikimo_purchase_tier_validation.action_server">
<field name="name">jikimo_purchase_tier_validation server</field>
<field name="model_id" ref="model_jikimo_purchase_tier_validation_jikimo_purchase_tier_validation"/>
<field name="state">code</field>
<field name="code">
action = {
"type": "ir.actions.act_window",
"view_mode": "tree,form",
"res_model": model._name,
}
</field>
</record>
-->
<!-- Top menu item -->
<!--
<menuitem name="jikimo_purchase_tier_validation" id="jikimo_purchase_tier_validation.menu_root"/>
-->
<!-- menu categories -->
<!--
<menuitem name="Menu 1" id="jikimo_purchase_tier_validation.menu_1" parent="jikimo_purchase_tier_validation.menu_root"/>
<menuitem name="Menu 2" id="jikimo_purchase_tier_validation.menu_2" parent="jikimo_purchase_tier_validation.menu_root"/>
-->
<!-- actions -->
<!--
<menuitem name="List" id="jikimo_purchase_tier_validation.menu_1_list" parent="jikimo_purchase_tier_validation.menu_1"
action="jikimo_purchase_tier_validation.action_window"/>
<menuitem name="Server to list" id="jikimo_purchase_tier_validation" parent="jikimo_purchase_tier_validation.menu_2"
action="jikimo_purchase_tier_validation.action_server"/>
-->
</data> </data>
</odoo> </odoo>

View File

@@ -1,2 +1 @@
from . import upload_file_wizard from . import comment_wizard
from . import comment_wizard

View File

@@ -1,114 +0,0 @@
from odoo import models, fields, api, _
class IrAttachmentWizard(models.TransientModel):
_name = 'ir.attachment.wizard'
_description = '文件上传向导'
attachment = fields.Binary(string='选择文件', required=True)
filename = fields.Char(string='文件名')
res_model = fields.Char()
res_id = fields.Integer()
# def action_upload_file(self):
# self.ensure_one()
# # 首先创建 ir.attachment
# attachment = self.env['ir.attachment'].create({
# 'name': self.filename,
# 'type': 'binary',
# 'datas': self.attachment,
# 'res_model': self.res_model,
# 'res_id': self.res_id,
# })
#
# # 获取默认的文档文件夹
# workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
#
# # 创建 documents.document 记录
# document = self.env['documents.document'].create({
# 'name': self.filename,
# 'attachment_id': attachment.id,
# 'folder_id': workspace.id,
# 'res_model': self.res_model,
# 'res_id': self.res_id,
# })
#
# return {
# 'type': 'ir.actions.client',
# 'tag': 'display_notification',
# 'params': {
# 'title': _('成功'),
# 'message': _('文件上传成功'),
# 'type': 'success',
# }
# }
def action_upload_file(self):
self.ensure_one()
# 获取当前用户的 partner_id
current_partner = self.env.user.partner_id
# 首先创建 ir.attachment
attachment = self.env['ir.attachment'].create({
'name': self.filename,
'type': 'binary',
'datas': self.attachment,
'res_model': self.res_model,
'res_id': self.res_id,
})
# 获取默认的文档文件夹
workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
# 创建 documents.document 记录
document = self.env['documents.document'].create({
'name': self.filename,
'attachment_id': attachment.id,
'folder_id': workspace.id,
'res_model': self.res_model,
'res_id': self.res_id,
'partner_id': current_partner.id,
})
# 更新采购订单的合同文档字段
purchase_order = self.env['purchase.order'].browse(self.res_id)
purchase_order.write({
'contract_document_id': document.id,
'is_upload_contract_file': True
})
# 显示成功消息并关闭向导
message = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('成功'),
'message': _('文件上传成功'),
'type': 'success',
'sticky': False, # 自动消失
'next': {
'type': 'ir.actions.act_window_close'
}
}
}
return message
# def action_upload_file(self):
# self.ensure_one()
# attachment = self.env['ir.attachment'].create({
# 'name': self.filename,
# 'type': 'binary',
# 'datas': self.attachment,
# 'res_model': self.res_model,
# 'res_id': self.res_id,
# })
# return {
# 'type': 'ir.actions.client',
# 'tag': 'display_notification',
# 'params': {
# 'title': _('成功'),
# 'message': _('文件上传成功'),
# 'type': 'success',
# }
# }

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_upload_file_wizard_form" model="ir.ui.view">
<field name="name">ir.attachment.wizard.form</field>
<field name="model">ir.attachment.wizard</field>
<field name="arch" type="xml">
<form string="上传文件">
<group>
<field name="attachment" widget="binary" filename="filename" options="{'accepted_file_extensions': '.pdf,.doc,.docx,.jpg,.jpeg,.png'}"/>
<field name="filename" invisible="1"/>
<field name="res_model" invisible="1"/>
<field name="res_id" invisible="1"/>
</group>
<footer>
<button name="action_upload_file" string="确认上传" type="object" class="btn-primary"/>
<button string="取消" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>

View File

@@ -4,6 +4,7 @@ import json
import logging import logging
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect 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_manufacturing.controllers.controllers import Manufacturing_Connect
from datetime import datetime
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -30,6 +31,7 @@ class WorkorderExceptionConroller(http.Controller):
workorder = request.env['mrp.workorder'].sudo().search([ workorder = request.env['mrp.workorder'].sudo().search([
('rfid_code', '=', ret['RfidCode']), ('rfid_code', '=', ret['RfidCode']),
('routing_type', '=', 'CNC加工'), ('routing_type', '=', 'CNC加工'),
('state', '!=', 'rework')
]) ])
if not workorder: if not workorder:
res = {'Succeed': False, 'ErrorCode': 401, 'Error': '无效的工单'} res = {'Succeed': False, 'ErrorCode': 401, 'Error': '无效的工单'}
@@ -41,7 +43,10 @@ class WorkorderExceptionConroller(http.Controller):
'exception_code': ret.get('coding'), 'exception_code': ret.get('coding'),
'exception_content': ret.get('Error', '') 'exception_content': ret.get('Error', '')
}) })
# 申请重新编程
workorder.production_id.update_programming_state(trigger_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
reprogramming_reason=ret.get('Error', ''))
workorder.production_id.write({'programming_state': '编程中', 'work_state': '编程中', 'is_rework': False})
except Exception as e: except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
_logger.info('workder_exception error:%s' % e) _logger.info('workder_exception error:%s' % e)

View File

@@ -132,6 +132,26 @@ class Sf_Bf_Connect(http.Controller):
request.cr.rollback() request.cr.rollback()
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/api/bfm_cancel_order', type='http', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_bfm_cancel_order(self, **kw):
"""
业务平台取消销售订单
:param kw:
:return:
"""
res = {'status': 1, 'message': '工厂取消销售订单成功'}
logging.info('get_bfm_cancel_order:%s' % kw['order_number'])
try:
sale_order_info = request.env['sale.order'].sudo().search([('name', '=', kw['order_number'])])
sale_order_info._action_cancel()
return json.JSONEncoder().encode(res)
except Exception as e:
logging.error('get_bfm_cancel_order error: %s' % e)
res['status'] = -1
res['message'] = '工厂取消销售订单失败,请联系管理员'
return json.JSONEncoder().encode(res)
class jdElcp(http.Controller): class jdElcp(http.Controller):

View File

@@ -596,6 +596,9 @@ class Manufacturing_Connect(http.Controller):
if panel_workorder: if panel_workorder:
panel_workorder.write({'production_line_state': '已下产线'}) panel_workorder.write({'production_line_state': '已下产线'})
workorder.write({'state': 'to be detected'}) workorder.write({'state': 'to be detected'})
workorder.check_ids.filtered(
lambda ch: ch.quality_state == 'waiting').write(
{'quality_state': 'none'})
else: else:
res = {'Succeed': False, 'ErrorCode': 204, res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']} 'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}

View File

@@ -1,27 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import asyncio
import base64 import base64
import cProfile
import concurrent
import datetime import datetime
import io
import logging import logging
import json import json
import os import os
import pstats
import re import re
import threading
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
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, _, tools from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
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 MrpProduction(models.Model): class MrpProduction(models.Model):
_inherit = 'mrp.production' _inherit = 'mrp.production'
_description = "制造订单" _description = "制造订单"
@@ -584,16 +577,19 @@ class MrpProduction(models.Model):
# 编程单更新 # 编程单更新
# 增加触发时间参数 # 增加触发时间参数
def update_programming_state(self, trigger_time=None): def update_programming_state(self, trigger_time=None, reprogramming_reason=None):
try: try:
manufacturing_type = 'rework' manufacturing_type = None
if self.is_scrap: if self.is_scrap:
manufacturing_type = 'scrap' manufacturing_type = 'scrap'
elif self.tool_state == '2': elif self.tool_state == '2':
manufacturing_type = 'invalid_tool_rework' manufacturing_type = 'invalid_tool_rework'
elif self.is_rework:
manufacturing_type = 'rework'
res = {'programming_no': self.programming_no, res = {'programming_no': self.programming_no,
'manufacturing_type': manufacturing_type, 'manufacturing_type': manufacturing_type,
'trigger_time': trigger_time} 'trigger_time': trigger_time,
'reprogramming_reason': reprogramming_reason}
logging.info('res=%s:' % res) logging.info('res=%s:' % res)
configsettings = self.env['res.config.settings'].get_values() configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
@@ -662,6 +658,8 @@ class MrpProduction(models.Model):
programme_way = 'manual operation' programme_way = 'manual operation'
else: else:
programme_way = 'auto' programme_way = 'auto'
if cnc.production_type == '人工线下加工':
programme_way = 'manual operation'
if quick_order: if quick_order:
programme_way = 'manual operation' programme_way = 'manual operation'
try: try:
@@ -766,105 +764,64 @@ class MrpProduction(models.Model):
# if self.product_id.tracking == 'serial': # if self.product_id.tracking == 'serial':
# self._set_qty_producing() # self._set_qty_producing()
# 重载根据工序生成工单的程序如果产品BOM中没有工序时 # 重载根据工序生成工单的程序如果产品BOM中没有工序时
# 根据产品对应的模板类型中工序,去生成工单; # 根据产品对应的模板类型中工序,去生成工单;
# CNC加工工序的选取规则 # CNC加工工序的选取规则
# 如果自动报价有带过来预分配的机床, # 如果自动报价有带过来预分配的机床,
# 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制; # 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制;
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心; # 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
def process_production(self):
workorders_values = []
# production = self.env['mrp.production'].browse(production_id)
product_qty = self.product_uom_id._compute_quantity(self.product_qty,
self.bom_id.product_uom_id)
exploded_boms, dummy = self.bom_id.explode(self.product_id,
product_qty / self.bom_id.product_qty,
picking_type=self.bom_id.picking_type_id)
for bom, bom_data in exploded_boms:
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
continue
for operation in bom.operation_ids:
if operation._skip_operation_line(bom_data['product']):
continue
workorders_values += [{
'name': operation.name,
'production_id': self.id,
'workcenter_id': operation.workcenter_id.id,
'product_uom_id': self.product_uom_id.id,
'operation_id': operation.id,
'state': 'pending',
}]
if self.product_id.categ_id.type in ['成品', '坯料']:
# # 根据工序设计生成工单
technology_design_ids = sorted(self.technology_design_ids, key=lambda x: x.sequence)
for route in technology_design_ids:
workorder_has = self.env['mrp.workorder'].search(
[('technology_design_id', '=', route.id), ('production_id', '=', self.id)])
if not workorder_has:
if route.route_id.routing_type not in ['表面工艺']:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(self, route))
else:
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', route.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(
self, route, product_production_process.seller_ids[0].partner_id.id))
return workorders_values
def _set_workorder_duration_expected(self):
try:
# 在每个线程中创建独立的 ORM 环境
with api.Environment.manage():
new_cr = self.pool.cursor()
self = self.with_env(self.env(cr=new_cr))
# program_ids = self.sudo().env['loyalty.program'].browse(program_ids.ids)
# 使用独立的环境来处理数据库事务
# 在独立的环境中对 workorder 进行操作
production = self.sudo().env['mrp.production'].browse(self.id)
workorders_values = production.process_production()
production.write({'workorder_ids': workorders_values})
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
# 可以进行其他与工作单相关的操作
# workorder_env.write({'...'})
return production
except Exception as e:
logging.error(f"Error processing workorder {workorder.id}: {e}")
# workorders_values = self.process_production(self)
# print('_set_workorder_duration_expected wqio ', self)
# self.write({'workorder_ids': workorders_values})
# for workorder in self.workorder_ids:
# workorder.duration_expected = workorder._get_duration_expected()
def _create_workorder3(self, item): def _create_workorder3(self, item):
with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: for production in self:
futures = [] if not production.bom_id or not production.product_id:
for production in self: continue
if not production.bom_id or not production.product_id: workorders_values = []
product_qty = production.product_uom_id._compute_quantity(production.product_qty,
production.bom_id.product_uom_id)
exploded_boms, dummy = production.bom_id.explode(production.product_id,
product_qty / production.bom_id.product_qty,
picking_type=production.bom_id.picking_type_id)
for bom, bom_data in exploded_boms:
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
continue continue
# 提交每个生产任务到线程池 for operation in bom.operation_ids:
futures.append(executor.submit(production._set_workorder_duration_expected)) if operation._skip_operation_line(bom_data['product']):
continue
# 等待所有线程完成任务 workorders_values += [{
results = [] 'name': operation.name,
for future in futures: 'production_id': production.id,
try: 'workcenter_id': operation.workcenter_id.id,
result = future.result() 'product_uom_id': production.product_uom_id.id,
if result: 'operation_id': operation.id,
results.append(result) 'state': 'pending',
}]
except Exception as e: if production.product_id.categ_id.type in ['成品', '坯料']:
logging.error(f"Error processing production: {e}") # # 根据工序设计生成工单
return results technology_design_ids = sorted(production.technology_design_ids, key=lambda x: x.sequence)
for route in technology_design_ids:
workorder_has = self.env['mrp.workorder'].search(
[('technology_design_id', '=', route.id), ('production_id', '=', production.id)])
if not workorder_has:
if route.route_id.routing_type not in ['表面工艺']:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(production, route))
else:
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', route.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(
production, route, product_production_process.seller_ids[0].partner_id.id))
production.workorder_ids = workorders_values
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
# 外协出入库单处理 # 外协出入库单处理
def get_subcontract_pick_purchase(self,productions): def get_subcontract_pick_purchase(self):
production_all = productions.sorted(lambda x: x.id) production_all = self.sorted(lambda x: x.id)
product_id_to_production_names = {} product_id_to_production_names = {}
grouped_product_ids = {k: list(g) for k, g in grouped_product_ids = {k: list(g) for k, g in
groupby(production_all, key=lambda x: x.product_id.id)} groupby(production_all, key=lambda x: x.product_id.id)}
@@ -873,10 +830,9 @@ class MrpProduction(models.Model):
sorted_workorders = None sorted_workorders = None
for production in production_all: for production in production_all:
proc_workorders = [] proc_workorders = []
process_parameter_workorder=production.workorder_ids.filtered(lambda w: w.surface_technics_parameters_id and w.is_subcontract and w.state!='cancel') process_parameter_workorder = self.env['mrp.workorder'].search(
# process_parameter_workorder = self.env['mrp.workorder'].search( [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
# [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id), ('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
# ('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
if process_parameter_workorder: if process_parameter_workorder:
# 将这些特殊表面工艺工单的采购单与调拨单置为失效 # 将这些特殊表面工艺工单的采购单与调拨单置为失效
for workorder in process_parameter_workorder: for workorder in process_parameter_workorder:
@@ -991,49 +947,48 @@ class MrpProduction(models.Model):
if purchase_order_line: if purchase_order_line:
line.unlink() line.unlink()
def _process_reset_work_order_sequence(self,rec): def _reset_work_order_sequence(self):
workorder_ids = rec.workorder_ids
technology_design_ids = rec.technology_design_ids
if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')):
# 获取返工后新生成的工单
work_ids = workorder_ids.filtered(lambda item: item.sequence == 0)
# 对工单进行逐个插入
for work_id in work_ids:
order_rework_ids = rec.workorder_ids.filtered(
lambda item: (item.sequence > 0 and work_id.name == item.name
and work_id.processing_panel == item.processing_panel))
order_rework_ids = sorted(order_rework_ids, key=lambda item: item.sequence, reverse=True)
work_id.sequence = order_rework_ids[0].sequence + 1
# 对该工单之后的工单工序进行加一
work_order_ids = rec.workorder_ids.filtered(
lambda item: item.sequence >= work_id.sequence and item.id != work_id.id)
for work in work_order_ids:
work.sequence = work.sequence + 1
else:
# 将工艺设计生成的工单序号赋值给工单的序号
for work in workorder_ids:
td_ids = technology_design_ids.filtered(
lambda item: (item.route_id.name in work.name and item.process_parameters_id
and item.process_parameters_id == work.surface_technics_parameters_id) or
(item.route_id.name == work.name and item.panel
and item.panel == work.processing_panel))
if work.name == '人工线下加工':
td_ids = technology_design_ids.filtered(lambda item: (item.route_id.name in work.name))
if td_ids:
work.sequence = td_ids[0].sequence
cancel_work_ids = workorder_ids.filtered(lambda item: item.state in ('已取消', 'cancel'))
if cancel_work_ids:
sequence = max(workorder_ids.filtered(lambda item: item.state not in ('已取消', 'cancel')),
key=lambda w: w.sequence).sequence
for cw in cancel_work_ids:
cw.sequence = sequence + 1
def _reset_work_order_sequence(self,productions):
""" """
工单工序排序方法(新) 工单工序排序方法(新)
""" """
for rec in productions: for rec in self:
self._process_reset_work_order_sequence(rec) workorder_ids = rec.workorder_ids
technology_design_ids = rec.technology_design_ids
if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')):
# 获取返工后新生成的工单
work_ids = workorder_ids.filtered(lambda item: item.sequence == 0)
# 对工单进行逐个插入
for work_id in work_ids:
order_rework_ids = rec.workorder_ids.filtered(
lambda item: (item.sequence > 0 and work_id.name == item.name
and work_id.processing_panel == item.processing_panel))
order_rework_ids = sorted(order_rework_ids, key=lambda item: item.sequence, reverse=True)
work_id.sequence = order_rework_ids[0].sequence + 1
# 对该工单之后的工单工序进行加一
work_order_ids = rec.workorder_ids.filtered(
lambda item: item.sequence >= work_id.sequence and item.id != work_id.id)
for work in work_order_ids:
work.sequence = work.sequence + 1
else:
# 将工艺设计生成的工单序号赋值给工单的序号
for work in workorder_ids:
td_ids = technology_design_ids.filtered(
lambda item: (item.route_id.name in work.name and item.process_parameters_id
and item.process_parameters_id == work.surface_technics_parameters_id) or
(item.route_id.name == work.name and item.panel
and item.panel == work.processing_panel))
if work.name == '人工线下加工':
td_ids = technology_design_ids.filtered(lambda item: (item.route_id.name in work.name))
if td_ids:
work.sequence = td_ids[0].sequence
cancel_work_ids = workorder_ids.filtered(lambda item: item.state in ('已取消', 'cancel'))
if cancel_work_ids:
sequence = max(workorder_ids.filtered(lambda item: item.state not in ('已取消', 'cancel')),
key=lambda w: w.sequence).sequence
for cw in cancel_work_ids:
cw.sequence = sequence + 1
def _reset_work_order_sequence_1(self): def _reset_work_order_sequence_1(self):
""" """
工单工序排序方法(旧) 工单工序排序方法(旧)
@@ -1129,9 +1084,9 @@ class MrpProduction(models.Model):
# 创建工单并进行排序 # 创建工单并进行排序
def _create_workorder(self, item): def _create_workorder(self, item):
productions = self._create_workorder3(item) self._create_workorder3(item)
self._reset_work_order_sequence(productions) self._reset_work_order_sequence()
return productions return True
def production_process(self, pro_plan): def production_process(self, pro_plan):
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False} type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
@@ -1342,7 +1297,7 @@ class MrpProduction(models.Model):
for rework_item in rework_workorder: for rework_item in rework_workorder:
pending_workorder = production.workorder_ids.filtered( pending_workorder = production.workorder_ids.filtered(
lambda m1: m1.state in [ lambda m1: m1.state in [
'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type == 'CNC加工') 'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type in ['CNC加工', '人工线下加工'])
if not pending_workorder.cnc_ids: if not pending_workorder.cnc_ids:
production.get_new_program(rework_item.processing_panel) production.get_new_program(rework_item.processing_panel)
# production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False}) # production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
@@ -1352,6 +1307,7 @@ class MrpProduction(models.Model):
# 对制造订单所以面的cnc工单的程序用刀进行校验 # 对制造订单所以面的cnc工单的程序用刀进行校验
try: try:
logging.info(f'已更新制造订单:{productions_not_delivered}') logging.info(f'已更新制造订单:{productions_not_delivered}')
productions = productions.filtered(lambda p: p.production_type == '自动化产线加工')
productions.production_cnc_tool_checkout() productions.production_cnc_tool_checkout()
except Exception as e: except Exception as e:
logging.info(f'对cnc工单的程序用刀进行校验报错{e}') logging.info(f'对cnc工单的程序用刀进行校验报错{e}')
@@ -1384,7 +1340,7 @@ class MrpProduction(models.Model):
if productions: if productions:
for production in productions: for production in productions:
panel_workorder = production.workorder_ids.filtered(lambda panel_workorder = production.workorder_ids.filtered(lambda
pw: pw.processing_panel == processing_panel and pw.routing_type == 'CNC加工' and pw.state not in ( pw: pw.processing_panel == processing_panel and pw.routing_type in ['CNC加工', '人工线下加工'] and pw.state not in (
'rework', 'done')) 'rework', 'done'))
if panel_workorder: if panel_workorder:
if panel_workorder.cmm_ids: if panel_workorder.cmm_ids:
@@ -1410,7 +1366,7 @@ class MrpProduction(models.Model):
'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())}) 'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
logging.info('len(cnc_worksheet):%s' % len(panel_workorder.cnc_worksheet)) logging.info('len(cnc_worksheet):%s' % len(panel_workorder.cnc_worksheet))
pre_workorder = production.workorder_ids.filtered(lambda pre_workorder = production.workorder_ids.filtered(lambda
ap: ap.routing_type == '装夹预调' and ap.processing_panel == processing_panel and ap.state not in ( ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.processing_panel == processing_panel and ap.state not in (
'rework', 'done')) 'rework', 'done'))
if pre_workorder: if pre_workorder:
pre_workorder.write( pre_workorder.write(
@@ -1724,13 +1680,13 @@ class MrpProduction(models.Model):
url = '/api/intelligent_programming/reset_state_again' url = '/api/intelligent_programming/reset_state_again'
config_url = configsettings['sf_url'] + url config_url = configsettings['sf_url'] + url
ret = requests.post(config_url, json=res, data=None, headers=config_header) ret = requests.post(config_url, json=res, data=None, headers=config_header)
ret = ret.json() # ret = ret.json()
result = json.loads(ret['result']) # result = json.loads(ret['result'])
logging.info('update_programming_state-ret:%s' % result) # logging.info('update_programming_state-ret:%s' % result)
if result['status'] == 1: # if result['status'] == 1:
self.write({'is_rework': True}) # self.write({'is_rework': True})
else: # else:
raise UserError(ret['message']) # raise UserError(ret['message'])
except Exception as e: except Exception as e:
logging.info('update_programming_state error:%s' % e) logging.info('update_programming_state error:%s' % e)
raise UserError("更新编程单状态失败,请联系管理员") raise UserError("更新编程单状态失败,请联系管理员")
@@ -1806,5 +1762,3 @@ class sf_processing_panel(models.Model):
name = fields.Char('加工面') name = fields.Char('加工面')
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)

View File

@@ -455,6 +455,32 @@ class ResMrpWorkOrder(models.Model):
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True) ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
detailed_reason = fields.Text('详细原因') detailed_reason = fields.Text('详细原因')
is_rework = fields.Boolean(string='是否返工', default=False) is_rework = fields.Boolean(string='是否返工', default=False)
# rework_flag = fields.Boolean(string='返工标志', compute='_compute_rework_flag')
#
# @api.depends('state', 'production_line_state')
# def _compute_rework_flag(self):
# for record in self:
# if record.state == 'done' and record.routing_type == '装夹预调':
# next_workorder = record.production_id.workorder_ids.filtered(
# lambda w: w.sequence == record.sequence + 1)
# if next_workorder and next_workorder.routing_type == 'CNC加工' and next_workorder.state in ['ready',
# 'waiting',
# 'pending'] and next_workorder.production_line_state == '待上产线':
# record.rework_flag = False
# elif next_workorder and next_workorder.routing_type == '表面工艺' and next_workorder.state in ['ready',
# 'waiting',
# 'pending']:
# record.rework_flag = False
# else:
# record.rework_flag = True
# else:
# record.rework_flag = True
#
# def button_rework(self):
# for item in self:
# item.state = 'progress'
# for time_id in item.time_ids:
# time_id.write({'date_end': None})
def button_change_env(self): def button_change_env(self):
self.is_test_env = not self.is_test_env self.is_test_env = not self.is_test_env
@@ -1543,7 +1569,8 @@ class ResMrpWorkOrder(models.Model):
# 修改工单状态 # 修改工单状态
self.write({'state': 'to be detected'}) self.write({'state': 'to be detected'})
# 若关联的【质量检查_需送检】=true则质量检查单的状态从“等待”更新为“待处理” # 若关联的【质量检查_需送检】=true则质量检查单的状态从“等待”更新为“待处理”
self.check_ids.filtered(lambda ch: ch.is_inspect is True).write({'quality_state': 'none'}) self.check_ids.filtered(lambda ch: ch.is_inspect is True and ch.quality_state == 'waiting').write(
{'quality_state': 'none'})
class CNCprocessing(models.Model): class CNCprocessing(models.Model):

View File

@@ -180,14 +180,14 @@ class StockRule(models.Model):
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create( productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
productions_values) productions_values)
# 将这一批制造订单的采购组根据成品设置为不同的采购组 # 将这一批制造订单的采购组根据成品设置为不同的采购组
product_group_id = {} # product_group_id = {}
for index, production in enumerate(productions): # for index, production in enumerate(productions):
if production.product_id.id not in product_group_id.keys(): # if production.product_id.id not in product_group_id.keys():
product_group_id[production.product_id.id] = production.procurement_group_id.id # product_group_id[production.product_id.id] = production.procurement_group_id.id
else: # else:
productions_values[index].update({'name': production.name}) # productions_values[index].update({'name': production.name})
procurement_group_vals = production._prepare_procurement_group_vals(productions_values[index]) # procurement_group_vals = production._prepare_procurement_group_vals(productions_values[index])
production.procurement_group_id = self.env["procurement.group"].create(procurement_group_vals).id # production.procurement_group_id = self.env["procurement.group"].create(procurement_group_vals).id
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values()) # self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# self.env['stock.move'].sudo().create(productions._get_moves_finished_values()) # self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
@@ -197,7 +197,7 @@ class StockRule(models.Model):
''' '''
# productions._create_workorder() # productions._create_workorder()
# #
self.env['stock.move'].sudo().create(productions._get_moves_finished_values()) # self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \ productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
( (
p.move_dest_ids.procure_method != 'make_to_order' and not p.move_dest_ids.procure_method != 'make_to_order' and not
@@ -289,7 +289,7 @@ class StockRule(models.Model):
if production_item.product_id.id in product_id_to_production_names: if production_item.product_id.id in product_id_to_production_names:
# 同一个产品多个制造订单对应一个编程单和模型库 # 同一个产品多个制造订单对应一个编程单和模型库
# 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递 # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production_item.programming_no and production_item.production_type == '自动化产线加工': if not production_item.programming_no and production_item.production_type in ['自动化产线加工', '人工线下加工']:
if not production_programming.programming_no: if not production_programming.programming_no:
production_item.fetchCNC( production_item.fetchCNC(
', '.join(product_id_to_production_names[production_item.product_id.id])) ', '.join(product_id_to_production_names[production_item.product_id.id]))
@@ -314,6 +314,11 @@ class StockRule(models.Model):
i += 1 i += 1
technology_design_values.append( technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str(k, route, i, False)) self.env['sf.technology.design'].json_technology_design_str(k, route, i, False))
elif production_item.production_type == '人工线下加工':
for route in product_routing_workcenter:
i += 1
technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str('ZM', route, i, False))
else: else:
for route in product_routing_workcenter: for route in product_routing_workcenter:
i += 1 i += 1

View File

@@ -74,7 +74,9 @@
<xpath expr="//field[@name='production_real_duration']" position="attributes"> <xpath expr="//field[@name='production_real_duration']" position="attributes">
<attribute name="invisible">1</attribute> <attribute name="invisible">1</attribute>
</xpath> </xpath>
<xpath expr="//field[@name='state']" position="after">
<field name="programming_state" optional="hide"/>
</xpath>
</field> </field>
</record> </record>
@@ -115,11 +117,11 @@
<xpath expr="//sheet//group//group//div[3]" position="after"> <xpath expr="//sheet//group//group//div[3]" position="after">
<field name="production_type" readonly="1"/> <field name="production_type" readonly="1"/>
<field name="manual_quotation" readonly="1" <field name="manual_quotation" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/> attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工', '人工线下加工'])]}"/>
<field name="programming_no" readonly="1" <field name="programming_no" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/> attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工', '人工线下加工'])]}"/>
<field name="programming_state" readonly="1" <field name="programming_state" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}" attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工', '人工线下加工'])]}"
decoration-success="programming_state == '已编程'" decoration-success="programming_state == '已编程'"
decoration-warning="programming_state =='编程中'" decoration-warning="programming_state =='编程中'"
decoration-danger="programming_state =='已编程未下发'"/> decoration-danger="programming_state =='已编程未下发'"/>
@@ -630,6 +632,8 @@
<field name="state" icon="fa-filter" enable_counters="1"/> <field name="state" icon="fa-filter" enable_counters="1"/>
<field name="delivery_status" icon="fa-filter" enable_counters="1"/> <field name="delivery_status" icon="fa-filter" enable_counters="1"/>
<field name="production_type" icon="fa-filter" enable_counters="1"/> <field name="production_type" icon="fa-filter" enable_counters="1"/>
<field name="programming_state" icon="fa-filter" enable_counters="1"/>
</searchpanel> </searchpanel>
</xpath> </xpath>
<filter name='todo' position="replace"/> <filter name='todo' position="replace"/>

View File

@@ -163,6 +163,7 @@
<field name='is_delivery' invisible="1"/> <field name='is_delivery' invisible="1"/>
<field name="is_trayed" invisible="1"/> <field name="is_trayed" invisible="1"/>
<field name="is_inspect" invisible="1"/> <field name="is_inspect" invisible="1"/>
<!-- <field name="rework_flag" invisible="1"/>-->
<!-- <field name='is_send_program_again' invisible="1"/>--> <!-- <field name='is_send_program_again' invisible="1"/>-->
<!-- 工单form页面的开始停工按钮等 --> <!-- 工单form页面的开始停工按钮等 -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" --> <!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
@@ -182,7 +183,7 @@
<button name="button_pending" type="object" string="暂停" class="btn-warning" <button name="button_pending" type="object" string="暂停" class="btn-warning"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/> attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工" <button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/> attrs="{'invisible': ['|', '|', '|',('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False),'&amp;','&amp;',('state', 'in', ('progress')), ('is_inspect', '=', True), ('routing_type','!=','CNC加工')]}"/>
<button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="阻塞" <button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="阻塞"
context="{'default_workcenter_id': workcenter_id}" class="btn-danger" context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
@@ -211,6 +212,9 @@
attrs="{'invisible': ['|', '|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False), ('state', 'in', ('done'))]}"/> attrs="{'invisible': ['|', '|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False), ('state', 'in', ('done'))]}"/>
<button name="print_method" type="object" string="打印二维码" class="btn-primary" <button name="print_method" type="object" string="打印二维码" class="btn-primary"
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/> attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
<!-- <button type="object" class="oe_highlight jikimo_button_confirm" name="button_rework"-->
<!-- string="返工"-->
<!-- attrs='{"invisible": [("rework_flag","=",True)]}' confirm="是否返工"/>-->
</xpath> </xpath>
<xpath expr="//page[1]" position="before"> <xpath expr="//page[1]" position="before">
<page string="开料要求" attrs='{"invisible": [("routing_type","not in",("切割", "线切割", "人工线下加工"))]}'> <page string="开料要求" attrs='{"invisible": [("routing_type","not in",("切割", "线切割", "人工线下加工"))]}'>
@@ -239,7 +243,7 @@
invisible="1" sum="real duration"/> invisible="1" sum="real duration"/>
<field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型"/> <field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型"/>
<field name="manual_quotation" readonly="1" <field name="manual_quotation" readonly="1"
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/> attrs="{'invisible': [('routing_type', 'not in', ['CNC加工', '人工线下加工'])]}"/>
<field name="processing_panel" readonly="1" <field name="processing_panel" readonly="1"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/> attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="equipment_id" readonly="1" <field name="equipment_id" readonly="1"
@@ -489,7 +493,7 @@
</group> </group>
</page> </page>
<page string="2D加工图纸" attrs="{'invisible': [('routing_type','!=','装夹预调')]}"> <page string="2D加工图纸" attrs='{"invisible": [("routing_type","not in",["装夹预调", "人工线下加工"])]}'>
<field name="machining_drawings" widget="adaptive_viewer"/> <field name="machining_drawings" widget="adaptive_viewer"/>
</page> </page>
@@ -533,7 +537,7 @@
<page string="后置三元检测" attrs='{"invisible": [("individuation_page_PTD", "=", False)]}'> <page string="后置三元检测" attrs='{"invisible": [("individuation_page_PTD", "=", False)]}'>
<group> <group>
<field name="test_results" <field name="test_results"
attrs='{"readonly":[("state","!=","to be detected"), "|",("routing_type","=","CNC加工"),("is_inspect", "=", True)], attrs='{"readonly":["&amp;","|",("state","!=","to be detected"), "|",("routing_type","=","CNC加工"),("is_inspect", "=", True),("state","in",["done","rework"])],
"invisible":[("results","!=",False)]}'/> "invisible":[("results","!=",False)]}'/>
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>--> <!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
<!-- <field name="is_fetchcnc"--> <!-- <field name="is_fetchcnc"-->
@@ -561,7 +565,7 @@
</page> </page>
</xpath> </xpath>
<xpath expr="//page[1]" position="before"> <xpath expr="//page[1]" position="before">
<page string="CNC程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'> <page string="CNC程序" attrs='{"invisible": [("routing_type","not in",["CNC加工", "人工线下加工"])]}'>
<field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id" <field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id"
readonly="0"> readonly="0">
<tree> <tree>
@@ -585,7 +589,7 @@
</field> </field>
</page> </page>
<page string="CMM程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'> <page string="CMM程序" attrs='{"invisible": [("routing_type","not in",["CNC加工", "人工线下加工"])]}'>
<field name="cmm_ids" widget="one2many" string="CMM程序" readonly="1"> <field name="cmm_ids" widget="one2many" string="CMM程序" readonly="1">
<tree> <tree>
<field name="sequence_number"/> <field name="sequence_number"/>
@@ -638,7 +642,7 @@
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/> <field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//form//sheet//group//group[2]" position="replace"> <xpath expr="//form//sheet//group//group[2]" position="replace">
<group string="装夹图纸" attrs="{'invisible': [('routing_type', '!=', '装夹预调')]}"> <group string="装夹图纸" attrs="{'invisible': [('routing_type', 'not in', ['装夹预调', '人工线下加工'])]}">
<!-- 隐藏加工图纸字段名 --> <!-- 隐藏加工图纸字段名 -->
<field name="processing_drawing" widget="pdf_viewer" string="" readonly="1"/> <field name="processing_drawing" widget="pdf_viewer" string="" readonly="1"/>
<!-- <field name="production_id" invisible="0"/>--> <!-- <field name="production_id" invisible="0"/>-->

View File

@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details. # Part of YiZuo. See LICENSE file for full copyright and licensing details.
import cProfile import logging
import io from itertools import groupby
import pstats
from concurrent.futures import ThreadPoolExecutor
from odoo import models, api, fields, _ from odoo import models, api, fields, _
@@ -15,71 +13,6 @@ class ProductionTechnologyWizard(models.TransientModel):
origin = fields.Char(string='源单据') origin = fields.Char(string='源单据')
is_technology_confirm = fields.Boolean(default=True) is_technology_confirm = fields.Boolean(default=True)
def _process_production(self,productions,technology_designs):
for production in productions:
# self._process_production_special_design(production,technology_designs)
with ThreadPoolExecutor(max_workers=4) as executor:
executor.submit(self._process_production_special_design, production,technology_designs)
def _process_production_special_design(self,production,technology_designs):
if production != self.production_id:
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
production)
# 特殊表面工艺
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
for special in special_design:
workorders_values = []
if special.active is False:
# is_cancel = False
# 工单采购单外协出入库单皆需取消
domain = [('production_id', '=', special.production_id.id)]
if special.process_parameters_id:
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id),
('state', '!=', 'cancel')]
else:
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
workorder = self.env['mrp.workorder'].search(domain)
# previous_workorder = self.env['mrp.workorder'].search(
# [('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
# ('production_id', '=', workorder.production_id.id)])
# if previous_workorder:
# if previous_workorder.supplier_id != workorder.supplier_id:
# is_cancel = True
# if workorder.state != 'cancel' and is_cancel is True:
workorder.write({'state': 'cancel'})
workorder.picking_ids.write({'state': 'cancel'})
workorder.picking_ids.move_ids.write({'state': 'cancel'})
purchase_order = self.env['purchase.order'].search(
[('origin', '=', workorder.production_id.name), ('purchase_type', '=', 'consignment')])
for line in purchase_order.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id:
purchase_order.write({'state': 'cancel'})
else:
if special.production_id.workorder_ids:
workorder = self.env['mrp.workorder'].search(
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id),
('state', '!=', 'cancel')])
if not workorder:
if special.route_id.routing_type == '表面工艺':
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special,
product_production_process.seller_ids[
0].partner_id.id))
else:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
special.production_id.write({'workorder_ids': workorders_values})
else:
if len(workorder.blocked_by_workorder_ids) > 1:
if workorder.sequence == 1:
workorder.blocked_by_workorder_ids = None
else:
if workorder.blocked_by_workorder_ids:
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
def confirm(self): def confirm(self):
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']: if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']:
domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'), domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'),
@@ -90,24 +23,72 @@ class ProductionTechnologyWizard(models.TransientModel):
[('production_id', '=', self.production_id.id), ('active', 'in', [True, False])]) [('production_id', '=', self.production_id.id), ('active', 'in', [True, False])])
# technology_designs = self.production_id.technology_design_ids # technology_designs = self.production_id.technology_design_ids
productions = self.env['mrp.production'].search(domain) productions = self.env['mrp.production'].search(domain)
pr = cProfile.Profile() for production in productions:
pr.enable() if production != self.production_id:
self._process_production(productions,technology_designs) self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
productions = productions._create_workorder(False) production)
# 特殊表面工艺
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
for special in special_design:
workorders_values = []
if special.active is False:
# is_cancel = False
# 工单采购单外协出入库单皆需取消
domain = [('production_id', '=', special.production_id.id)]
if special.process_parameters_id:
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')]
else:
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
workorder = self.env['mrp.workorder'].search(domain)
# previous_workorder = self.env['mrp.workorder'].search(
# [('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
# ('production_id', '=', workorder.production_id.id)])
# if previous_workorder:
# if previous_workorder.supplier_id != workorder.supplier_id:
# is_cancel = True
# if workorder.state != 'cancel' and is_cancel is True:
workorder.write({'state': 'cancel'})
workorder.picking_ids.write({'state': 'cancel'})
workorder.picking_ids.move_ids.write({'state': 'cancel'})
purchase_order = self.env['purchase.order'].search(
[('origin', '=', workorder.production_id.name), ('purchase_type', '=', 'consignment')])
for line in purchase_order.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id:
purchase_order.write({'state': 'cancel'})
else:
if special.production_id.workorder_ids:
workorder = self.env['mrp.workorder'].search(
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')])
if not workorder:
if special.route_id.routing_type == '表面工艺':
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special,
product_production_process.seller_ids[
0].partner_id.id))
else:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
special.production_id.write({'workorder_ids': workorders_values})
else:
if len(workorder.blocked_by_workorder_ids) > 1:
if workorder.sequence == 1:
workorder.blocked_by_workorder_ids = None
else:
if workorder.blocked_by_workorder_ids:
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
productions._create_workorder(False)
if self.production_id.product_id.categ_id.type == '成品': if self.production_id.product_id.categ_id.type == '成品':
self.production_id.get_subcontract_pick_purchase(productions) productions.get_subcontract_pick_purchase()
productions.is_adjust = False
for item in productions: for item in productions:
item.is_adjust = False
workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted( workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
key=lambda a: a.sequence) key=lambda a: a.sequence)
if workorder[0].state in ['pending']: if workorder[0].state in ['pending']:
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程': if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorder[0].state = 'waiting' workorder[0].state = 'waiting'
pr.disable() # 停止性能分析
# 将结果输出到 StringIO
s = io.StringIO()
ps = pstats.Stats(pr, stream=s)
ps.strip_dirs().sort_stats('cumulative').print_stats()
analysis_output = s.getvalue()
return productions return productions

View File

@@ -16,6 +16,7 @@ class SFMessageProduct(models.Model):
mrp_production_list = self.env['mrp.production'].sudo().search( mrp_production_list = self.env['mrp.production'].sudo().search(
[('product_id', '=', product_product.id)]) [('product_id', '=', product_product.id)])
production_num = 0 production_num = 0
routing_type = None
for mrp_production_info in mrp_production_list: for mrp_production_info in mrp_production_list:
routing_type = '人工线下加工' if mrp_production_info.production_type == '人工线下加工' else '装夹预调' routing_type = '人工线下加工' if mrp_production_info.production_type == '人工线下加工' else '装夹预调'
mrp_production_ready = mrp_production_info.workorder_ids.filtered( mrp_production_ready = mrp_production_info.workorder_ids.filtered(
@@ -23,7 +24,7 @@ class SFMessageProduct(models.Model):
if mrp_production_ready: if mrp_production_ready:
production_num += 1 production_num += 1
if production_num >= 1: if production_num >= 1:
url = self.get_request_url() url = self.get_request_url(routing_type)
content = content.replace('{{product_id}}', product_product.name).replace( content = content.replace('{{product_id}}', product_product.name).replace(
'{{number}}', str(production_num)).replace( '{{number}}', str(production_num)).replace(
'{{request_url}}', url) '{{request_url}}', url)
@@ -42,11 +43,15 @@ class SFMessageProduct(models.Model):
contents.append(content) contents.append(content)
return contents, message_queue_ids return contents, message_queue_ids
def get_request_url(self): def get_request_url(self, routing_type):
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
action_id = self.env.ref('sf_message.mrp_workorder_issued_action').id action_id = self.env.ref('sf_message.mrp_workorder_issued_action').id
menu_id = self.env.ref('mrp.menu_mrp_root').id menu_id = self.env.ref('mrp.menu_mrp_root').id
active_id = self.env['mrp.workcenter'].sudo().search([('name', '=', '工件装夹中心')]).id if routing_type == '人工线下加工':
routing_name = '线下工作中心'
else:
routing_name = '工件装夹中心'
active_id = self.env['mrp.workcenter'].sudo().search([('name', '=', routing_name)]).id
# 查询参数 # 查询参数
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder', params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder',
'view_type': 'list', 'active_id': active_id} 'view_type': 'list', 'active_id': active_id}

View File

@@ -27,7 +27,9 @@ class SFMessageStockPicking(models.Model):
for record in self: for record in self:
if (record.state == 'assigned' and record.picking_type_id.sequence_code == 'PC' if (record.state == 'assigned' and record.picking_type_id.sequence_code == 'PC'
and record.product_id.categ_id.type == '坯料'): and record.product_id.categ_id.type == '坯料'):
record.add_queue('坯料发料提醒') jikimo_message_queue = record.get_message_queue(record.id)
if not jikimo_message_queue:
record.add_queue('坯料发料提醒')
if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done': if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done':
stock_picking_sfp = record.env['stock.picking'].search( stock_picking_sfp = record.env['stock.picking'].search(
@@ -107,3 +109,14 @@ class SFMessageStockPicking(models.Model):
# 拼接URL # 拼接URL
full_url = url + "/web#" + query_string full_url = url + "/web#" + query_string
return full_url return full_url
def get_message_queue(self, res_id):
business_node_id = self.env.ref('sf_message.bussiness_material_picking_remind').id
message_template = self.env["jikimo.message.template"].sudo().search([
("model", "=", self._name),
("bussiness_node_id", "=", business_node_id)
], limit=1)
jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search(
[('res_id', '=', res_id), ("message_status", "in", ("pending", "sent")),
('message_template_id', '=', message_template.id)])
return jikimo_message_queue

View File

@@ -61,7 +61,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
for panel in ret['processing_panel'].split(','): for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单 # 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder_has = production.workorder_ids.filtered( cnc_workorder_has = production.workorder_ids.filtered(
lambda ach: ach.routing_type == 'CNC加工' and ach.state not in ['progress', 'done', lambda ach: ach.routing_type in ['CNC加工', '人工线下加工'] and ach.state not in ['progress', 'done',
'rework', 'rework',
'cancel'] and ach.processing_panel == panel) 'cancel'] and ach.processing_panel == panel)
if cnc_workorder_has: if cnc_workorder_has:
@@ -76,7 +76,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
for panel in ret['processing_panel'].split(','): for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单 # 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder = productions.workorder_ids.filtered( cnc_workorder = productions.workorder_ids.filtered(
lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done', 'rework' lambda ac: ac.routing_type in ['CNC加工', '人工线下加工'] and ac.state not in ['progress', 'done', 'rework'
'cancel'] and ac.processing_panel == panel) 'cancel'] and ac.processing_panel == panel)
if cnc_workorder: if cnc_workorder:
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
@@ -91,19 +91,21 @@ 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)
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 == '装夹预调' and ap.state not in ['done', 'rework' lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
'cancel'] and ap.processing_panel == panel) 'cancel'] and ap.processing_panel == panel)
if pre_workorder: if pre_workorder:
pre_workorder.write( pre_workorder.write(
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())}) {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
productions.write({'programming_state': '已编程', 'work_state': '已编程'}) productions.write({'programming_state': '已编程', 'work_state': '已编程'})
productions.filtered(lambda p: p.production_type == '人工线下加工').write({'manual_quotation': True})
logging.info('已更新制造订单编程状态:%s' % productions.ids) logging.info('已更新制造订单编程状态:%s' % productions.ids)
# 对制造订单所有面的cnc工单的程序用刀进行校验 # 对制造订单所有面的cnc工单的程序用刀进行校验
try: try:
logging.info(f'已更新制造订单:{productions}') logging.info(f'已更新制造订单:{productions}')
re_tool_chekout = False re_tool_chekout = False
re_tool_chekout = productions.production_cnc_tool_checkout() productions_temp = productions.filtered(lambda p: p.production_type == '自动化产线加工')
re_tool_chekout = productions_temp.production_cnc_tool_checkout()
if re_tool_chekout: if re_tool_chekout:
return json.JSONEncoder().encode({'status': -3, 'message': '对cnc工单的程序用刀进行校验失败'}) return json.JSONEncoder().encode({'status': -3, 'message': '对cnc工单的程序用刀进行校验失败'})
except Exception as e: except Exception as e:
@@ -200,6 +202,17 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
'send_time': ret['send_time'], 'send_time': ret['send_time'],
}) })
logging.info('已创建无效功能刀具的编程记录:%s' % production.name) logging.info('已创建无效功能刀具的编程记录:%s' % production.name)
elif ret['reprogramming_reason']:
production.programming_record_ids.create({
'number': len(production.programming_record_ids) + 1,
'production_id': production.id,
'reason': ret['reprogramming_reason'],
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': ret['trigger_time'],
'send_time': ret['send_time'],
})
else: else:
logging.info('无对应状态,不需更新编程记录') logging.info('无对应状态,不需更新编程记录')

View File

@@ -48,7 +48,7 @@ class QualityCheck(models.Model):
@api.depends('point_id.is_inspect') @api.depends('point_id.is_inspect')
def _compute_quality_state(self): def _compute_quality_state(self):
for qc in self: for qc in self:
if qc.point_id.is_inspect and qc.quality_state == 'none': if qc.point_id.is_inspect and qc.quality_state == 'none' and qc.workorder_id.state != 'to be detected':
qc.quality_state = 'waiting' qc.quality_state = 'waiting'
elif not qc.point_id.is_inspect and qc.quality_state == 'waiting': elif not qc.point_id.is_inspect and qc.quality_state == 'waiting':
qc.quality_state = 'none' qc.quality_state = 'none'
@@ -62,7 +62,9 @@ class QualityCheck(models.Model):
def do_pass(self): def do_pass(self):
self.ensure_one() self.ensure_one()
super().do_pass() super().do_pass()
if self.workorder_id and self.individuation_page_PTD is True: if self.workorder_id:
if self.workorder_id.state in ('pending', 'waiting'):
raise ValidationError('工单未就绪!')
# 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】 # 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】
if self.test_results in ['返工', '报废']: if self.test_results in ['返工', '报废']:
raise ValidationError('请重新选择【判定结果】-【检测结果】') raise ValidationError('请重新选择【判定结果】-【检测结果】')
@@ -74,7 +76,9 @@ class QualityCheck(models.Model):
def do_fail(self): def do_fail(self):
self.ensure_one() self.ensure_one()
super().do_fail() super().do_fail()
if self.workorder_id and self.individuation_page_PTD is True: if self.workorder_id:
if self.workorder_id.state in ('pending', 'waiting'):
raise ValidationError('工单未就绪!')
# 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】 # 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】
if not self.test_results: if not self.test_results:
raise ValidationError('请填写【判定结果】里的信息') raise ValidationError('请填写【判定结果】里的信息')

View File

@@ -9,22 +9,24 @@
<field name="production_id" invisible="1"/> <field name="production_id" invisible="1"/>
<field name="work_state" invisible="1"/> <field name="work_state" invisible="1"/>
<field name="individuation_page_PTD" invisible="1"/> <field name="individuation_page_PTD" invisible="1"/>
<field name="production_line_id" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> <field name="production_line_id" attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="equipment_id" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> <field name="equipment_id" attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1" <field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': ['|', '|',('model_file', '=', False), ('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> attrs="{'invisible': ['|',('model_file', '=', False), ('production_id', '=', False)]}"/>
</xpath> </xpath>
<xpath expr="//field[@name='partner_id']" position="after"> <xpath expr="//field[@name='partner_id']" position="after">
<field name="processing_panel" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> <field name="processing_panel" attrs="{'invisible': [('production_id', '=', False)]}"/>
<!-- <field name="production_id" string="制造订单" readonly="1"-->
<!-- attrs="{'invisible': [('production_id', '=', False)]}"/>-->
<field name="workorder_id" string="工单号" readonly="1" <field name="workorder_id" string="工单号" readonly="1"
attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="is_inspect" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> <field name="is_inspect" attrs="{'invisible': [('production_id', '=', False)]}"/>
</xpath> </xpath>
<xpath expr="//page[@name='notes']" position="before"> <xpath expr="//page[@name='notes']" position="before">
<page string="检测报告" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"> <page string="检测报告" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="detection_report" string="" widget="pdf_viewer"/> <field name="detection_report" string="" widget="pdf_viewer"/>
</page> </page>
<page string="判定结果" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"> <page string="判定结果" attrs="{'invisible': [('production_id', '=', False)]}">
<group> <group>
<group> <group>
<field name="test_results" attrs="{'readonly': [('quality_state','in', ['pass', 'fail'])]}"/> <field name="test_results" attrs="{'readonly': [('quality_state','in', ['pass', 'fail'])]}"/>
@@ -35,25 +37,33 @@
</group> </group>
</group> </group>
</page> </page>
<page string="2D图纸" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"> <page string="2D图纸" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="machining_drawings" string="" widget="adaptive_viewer"/> <field name="machining_drawings" string="" widget="adaptive_viewer"/>
</page> </page>
<page string="客户质量标准" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"> <page string="客户质量标准" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="quality_standard" string="" widget="adaptive_viewer"/> <field name="quality_standard" string="" widget="adaptive_viewer"/>
</page> </page>
<page string="其他" <page string="其他"
attrs="{'invisible': ['|','|', ('quality_state', 'not in', ['pass', 'fail']), ('production_id', '=', False),('individuation_page_PTD', '=', False)]}"> attrs="{'invisible': ['|',('quality_state', 'not in', ['pass', 'fail']), ('production_id', '=', False)]}">
<group> <group>
<field name="write_uid" widget='many2one_avatar_user' string="判定人" readonly="1"/> <field name="write_uid" widget='many2one_avatar_user' string="判定人" readonly="1"/>
<field name="write_date" string="判定时间" readonly="1"/> <field name="write_date" string="判定时间" readonly="1"/>
</group> </group>
</page> </page>
</xpath> </xpath>
<xpath expr="//header//button[@name='do_pass'][1]" position="attributes">
<attribute name="string">合格</attribute>
</xpath>
<xpath expr="//header//button[@name='do_pass'][2]" position="attributes"> <xpath expr="//header//button[@name='do_pass'][2]" position="attributes">
<attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'fail'),('work_state','in', ('done', 'rework'))]}</attribute> <attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'fail'),('work_state','in', ('done', 'rework'))]}</attribute>
<attribute name="string">合格</attribute>
</xpath>
<xpath expr="//header//button[@name='do_fail'][1]" position="attributes">
<attribute name="string">不合格</attribute>
</xpath> </xpath>
<xpath expr="//header//button[@name='do_fail'][2]" position="attributes"> <xpath expr="//header//button[@name='do_fail'][2]" position="attributes">
<attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'pass'),('work_state','in', ('done', 'rework'))]}</attribute> <attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'pass'),('work_state','in', ('done', 'rework'))]}</attribute>
<attribute name="string">不合格</attribute>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -130,7 +130,10 @@ class ReSaleOrder(models.Model):
'order_id': self.id, 'order_id': self.id,
'product_id': product.id, 'product_id': product.id,
'name': '%s/%s/%s/%s/%s/%s' % ( 'name': '%s/%s/%s/%s/%s/%s' % (
product.model_long, product.model_width, product.model_height, product.model_volume, self.format_float(product.model_long),
self.format_float(product.model_width),
self.format_float(product.model_height),
self.format_float(product.model_volume),
machining_accuracy_name, machining_accuracy_name,
product.materials_id.name), product.materials_id.name),
'price_unit': product.list_price, 'price_unit': product.list_price,
@@ -143,6 +146,20 @@ class ReSaleOrder(models.Model):
} }
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals) return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
def format_float(self, value):
# 将浮点数转换为字符串
value_str = str(value)
# 检查小数点的位置
if '.' in value_str:
# 获取小数部分
decimal_part = value_str.split('.')[1]
# 判断小数位数是否超过2位
if len(decimal_part) > 2:
# 超过2位则保留2位小数
return "{:.2f}".format(value)
# 否则保持原来的位数
return float(value_str)
@api.constrains('order_line') @api.constrains('order_line')
def check_order_line(self): def check_order_line(self):
for item in self: for item in self:
@@ -500,9 +517,10 @@ class ResUserToSale(models.Model):
@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):
domain = []
if self._context.get('is_sale'): if self._context.get('is_sale'):
if self.env.user.has_group('sf_base.group_sale_director'): if self.env.user.has_group('sf_base.group_sale_director'):
domain = [] pass
elif self.env.user.has_group('sf_base.group_sale_salemanager'): elif self.env.user.has_group('sf_base.group_sale_salemanager'):
if self.id != self.env.user.id: if self.id != self.env.user.id:
domain = [('id', '=', self.id)] domain = [('id', '=', self.id)]
@@ -511,7 +529,7 @@ class ResUserToSale(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)
elif self._context.get('supplier_rank'): elif self._context.get('supplier_rank'):
if self.env.user.has_group('sf_base.group_purchase_director'): if self.env.user.has_group('sf_base.group_purchase_director'):
domain = [] pass
elif self.env.user.has_group('sf_base.group_purchase'): elif self.env.user.has_group('sf_base.group_purchase'):
if self.id != self.env.user.id: if self.id != self.env.user.id:
domain = [('id', '=', self.id)] domain = [('id', '=', self.id)]

View File

@@ -139,7 +139,7 @@
</field> </field>
<xpath expr="//field[@name='date_order']" position="after"> <xpath expr="//field[@name='date_order']" position="after">
<field name="payment_term_id" attrs="{'readonly': ['|', ('invoice_status','=', 'invoiced'), ('state', '=', 'done')]}" options="{'no_create': True}"/> <field name="payment_term_id" attrs="{'readonly': ['|', ('invoice_status','=', 'invoiced'), ('state', '=', 'done')]}" options="{'no_create': True}"/>
<field name="contract_summary"/> <!-- <field name="contract_summary"/>-->
</xpath> </xpath>
<field name="partner_ref" position="attributes"> <field name="partner_ref" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]} <attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}