Compare commits

..

2 Commits

Author SHA1 Message Date
胡尧
c5dfb50da5 修改模块名字 2024-10-31 14:42:28 +08:00
胡尧
1f938292f5 解决异常报错的问题 2024-10-31 14:34:53 +08:00
168 changed files with 1476 additions and 6952 deletions

View File

@@ -536,7 +536,3 @@ div:has(.o_required_modifier) > label::before {
position: unset;
}
// 修复搜索面板checkbox样式
.o_search_panel .form-check .form-check-label span {
position: relative;
}

View File

@@ -190,7 +190,7 @@ def _create(self, data_list):
# 如果该用户组被限制创建或更新操作
if rec['is_create_or_update']:
raise UserError(
_("您没有执行此操作的权限。请联系管理员"))
_("You are restricted from performing this operation. Please contact the administrator."))
else:
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
# 例如,记录日志、发送通知或者简单地跳过这部分逻辑

View File

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

View File

@@ -1,36 +0,0 @@
# -*- 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', 'base_tier_validation', 'documents'],
# always loaded
'data': [
'security/ir.model.access.csv',
'data/documents_data.xml',
'wizards/upload_file_wizard_view.xml',
'views/views.xml',
],
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
}

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

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

View File

@@ -1,154 +0,0 @@
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import logging
_logger = logging.getLogger(__name__)
class jikimo_purchase_tier_validation(models.Model):
_name = 'purchase.order'
_inherit = ['purchase.order', 'tier.validation']
_tier_validation_buttons_xpath = "/form/header/button[@id='draft_confirm'][1]"
contract_document_id = fields.Many2one('documents.document', string='合同文件')
contract_file = fields.Binary(related='contract_document_id.datas', string='合同文件内容')
contract_file_name = fields.Char(related='contract_document_id.attachment_id.name', string='文件名')
# 是否已上传合同文件
is_upload_contract_file = fields.Boolean(string='是否已上传合同文件', default=False)
def button_confirm(self):
for record in self:
if record.need_validation and record.validation_status != 'validated':
raise ValidationError(_('请先完成审批!'))
return super().button_confirm()
# 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):
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))
return super(jikimo_purchase_tier_validation, self).request_validation()
# 上传合同文件
def upload_contract_file(self):
print('upload_contract_file===========================')
# self.ensure_one()
# return {
# 'name': _('上传合同文件'),
# 'type': 'ir.actions.act_window',
# 'res_model': 'ir.attachment',
# 'view_mode': 'form',
# 'view_type': 'form',
# 'target': 'new',
# 'context': {
# 'default_res_model': self._name,
# 'default_res_id': self.id,
# 'default_type': 'binary',
# 'default_mimetype': 'application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/jpeg,image/png',
# }
# }
self.ensure_one()
action = {
'type': 'ir.actions.act_window',
'name': _('上传合同文件'),
'res_model': 'ir.attachment.wizard', # 我们需要创建一个新的向导模型
'view_mode': 'form',
'target': 'new',
'context': {
'default_res_model': self._name,
'default_res_id': self.id,
}
}
return action
# 删除合同文件
def delete_contract_file(self):
self.ensure_one()
if self.contract_document_id:
try:
document = self.contract_document_id
# 清空关联
self.write({
'contract_document_id': False,
'contract_file': False,
'contract_file_name': False
})
# 删除文档
if document:
document.with_context(no_attachment=True).sudo().unlink()
self.is_upload_contract_file = False
# 返回视图动作来刷新当前表单
return {
'type': 'ir.actions.act_window',
'res_model': 'purchase.order',
'res_id': self.id,
'view_mode': 'form',
'view_type': 'form',
'target': 'current',
'flags': {'mode': 'readonly'},
}
except Exception as e:
_logger.error('删除合同文件时出错: %s', str(e))
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('错误'),
'message': _('删除文件时出现错误'),
'type': 'danger',
'sticky': True,
}
}
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('提示'),
'message': _('没有需要删除的合同文件'),
'type': 'warning',
'sticky': False,
}
}

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

@@ -1,82 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record model="ir.ui.view" id="tier_validation_view_purchase_order_form_inherit">
<field name="name">tier_validation_view_purchase_order_form_inherit</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//header/button[@name='button_cancel']" position="replace">
</xpath>
<xpath expr="//header/button[last()]" position="after">
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" />
</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)]}"/>
<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>
</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>
</odoo>

View File

@@ -1 +0,0 @@
from . import upload_file_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

@@ -1,139 +0,0 @@
# -*- coding: utf-8 -*-
from . import models
from . import controllers
from odoo import api, SUPERUSER_ID
def _data_install(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
# 获取所有需要设置的产品模板
env.ref('jikimo_sale_multiple_supply_methods.product_template_purchase').product_variant_id.write({'active': False, 'is_bfm': True})
env.ref('jikimo_sale_multiple_supply_methods.product_template_manual_processing').product_variant_id.write({'active': False, 'single_manufacturing': True, 'is_bfm': True})
env.ref('jikimo_sale_multiple_supply_methods.product_template_default').product_variant_id.write({'active': False, 'is_bfm': True})
env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').product_variant_id.write({'active': False})
env.ref('jikimo_sale_multiple_supply_methods.product_template_outsourcing').product_variant_id.write({'active': False, 'is_bfm': True})
# 为三步制造增加规则
warehouse = env['stock.warehouse'].search([('company_id', '=', env.company.id)], limit=1)
product_route_id = warehouse.pbm_route_id
# 创建规则:原料存货区 -> 制造前, 坯料存货区 -> 制造前, 制造后 -> 坯料存货区, 制造后 -> 成品存货区
raw_material_location_id = env['stock.location'].search([('name', '=', '坯料存货区')], limit=1)
picking_type_production = warehouse.pbm_type_id
picking_type_stock = warehouse.sam_type_id
material_location_id = env['stock.location'].search([('name', '=', '原料存货区')], limit=1)
# 为mto增加规则
mto_route_id = env.ref('stock.route_warehouse0_mto')
# 创建规则:原料存货区 -> 外包位置, 坯料存货区 -> 外包位置
subcontracting_location_id = env.company.subcontracting_location_id
picking_type_subcontracting = warehouse.subcontracting_resupply_type_id
# 为补给外包商增加规则
resupply_route_id = warehouse.subcontracting_route_id
rules_data = [
{
'name': 'WH: 原料存货区 → 制造前',
'location_src_id': material_location_id.id,
'location_dest_id': warehouse.pbm_loc_id.id,
'route_id': product_route_id.id,
'picking_type_id': picking_type_production.id,
'action': 'pull',
'sequence': 20,
'warehouse_id': warehouse.id,
'procure_method': 'mts_else_mto',
},
{
'name': 'WH: 坯料存货区 → 制造前',
'location_src_id': raw_material_location_id.id,
'location_dest_id': warehouse.pbm_loc_id.id,
'route_id': product_route_id.id,
'picking_type_id': picking_type_production.id,
'action': 'pull',
'sequence': 21,
'warehouse_id': warehouse.id,
'procure_method': 'mts_else_mto',
},
{
'name': 'WH: 制造后 → 坯料存货区',
'location_src_id': warehouse.sam_loc_id.id,
'location_dest_id': raw_material_location_id.id,
'route_id': product_route_id.id,
'picking_type_id': picking_type_stock.id,
'action': 'push',
'sequence': 23,
},
{
'name': 'WH: 制造后 → 成品存货区',
'location_src_id': warehouse.sam_loc_id.id,
'location_dest_id': env['stock.location'].search([('name', '=', '成品存货区')], limit=1).id,
'route_id': product_route_id.id,
'picking_type_id': picking_type_stock.id,
'action': 'push',
'sequence': 24,
},
{
'name': 'WH: 原料存货区 → 外包位置 (MTO)',
'location_src_id': material_location_id.id,
'location_dest_id': subcontracting_location_id.id,
'route_id': mto_route_id.id,
'picking_type_id': picking_type_subcontracting.id,
'action': 'pull',
'sequence': 24,
'warehouse_id': warehouse.id,
'procure_method': 'mts_else_mto',
},
{
'name': 'WH: 坯料存货区 → 外包位置 (MTO)',
'location_src_id': raw_material_location_id.id,
'location_dest_id': subcontracting_location_id.id,
'route_id': mto_route_id.id,
'picking_type_id': picking_type_subcontracting.id,
'action': 'pull',
'sequence': 25,
'warehouse_id': warehouse.id,
'procure_method': 'mts_else_mto',
},
{
'name': 'WH: 坯料存货区 → 外包位置',
'location_src_id': raw_material_location_id.id,
'location_dest_id': subcontracting_location_id.id,
'route_id': resupply_route_id.id,
'picking_type_id': picking_type_subcontracting.id,
'action': 'pull',
'sequence': 26,
'warehouse_id': warehouse.id,
'procure_method': 'make_to_stock',
}
]
# 遍历每个规则数据,执行创建或更新操作
for rule_data in rules_data:
_create_or_update_stock_rule(env, rule_data)
def _create_or_update_stock_rule(env, rule_data):
# 尝试查找现有的 stock.rule
existing_rule = env['stock.rule'].search([
('name', '=', rule_data['name']),
('route_id', '=', rule_data.get('route_id'))
], limit=1)
if existing_rule:
# 如果存在,更新现有记录
existing_rule.write(rule_data)
else:
# 如果不存在,创建新记录
env['stock.rule'].create(rule_data)
def _data_uninstall(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
warehouse = env['stock.warehouse'].search([('company_id', '=', env.company.id)], limit=1)
product_route_id = warehouse.pbm_route_id
resupply_route_id = warehouse.subcontracting_route_id
mto_route_id = env.ref('stock.route_warehouse0_mto')
# Fail unlink means that the route is used somewhere (e.g. route_id on stock.rule). In this case
# we don't try to do anything.
try:
with env.cr.savepoint():
env['stock.rule'].search([('name', 'in', ('WH: 原料存货区 → 制造前', 'WH: 坯料存货区 → 制造前', 'WH: 制造后 → 坯料存货区', 'WH: 制造后 → 成品存货区')), ('route_id', '=', product_route_id.id)]).unlink()
env['stock.rule'].search([('name', 'in', ('WH: 原料存货区 → 外包位置 (MTO)', 'WH: 坯料存货区 → 外包位置 (MTO)')), ('route_id', '=', mto_route_id.id)]).unlink()
env['stock.rule'].search([('name', '=', 'WH: 坯料存货区 → 外包位置'), ('route_id', '=', resupply_route_id.id)]).unlink()
except:
pass

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
{
'name': '机企猫 多供货方式',
'version': '16.0.1.0.0',
'summary': """ 报价单提供(自动化产线加工/人工线下加工/外购/委外加工)多种供货方式选择 """,
'author': 'fox',
'website': '',
'category': '',
'depends': ['sf_dlm', 'sale_stock', 'sf_sale', 'sale'],
"data": [
'security/ir.model.access.csv',
'data/stock_routes.xml',
'data/product_data.xml',
# 'views/product_product_views.xml',
],'assets': {
# 'web.assets_backend': [
# 'jikimo_sale_multiple_supply_methods/static/src/**/*'
# ],
},
'post_init_hook': '_data_install',
'uninstall_hook': '_data_uninstall',
'application': True,
'installable': True,
'auto_install': False,
'license': 'LGPL-3',
}

View File

@@ -1 +0,0 @@
# -*- coding: utf-8 -*-

View File

@@ -1,98 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data noupdate="1">
<record id="product_template_manual_processing" model="product.template">
<field name="name">人工线下加工模板</field>
<field name="active" eval="False"/>
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
<field name="route_ids"
eval="[ref('stock.route_warehouse0_mto'), ref('mrp.route_warehouse0_manufacture')]"/>
<field name="invoice_policy">delivery</field>
<field name="detailed_type">product</field>
<field name="purchase_ok">false</field>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="company_id" ref="base.main_company"/>
<field name="single_manufacturing">true</field>
<field name="tracking">serial</field>
<!-- <field name="categ_type">成品</field> -->
<field name="is_manual_processing">true</field>
</record>
<record id="product_template_purchase" model="product.template">
<field name="name">成品外购模板</field>
<field name="active" eval="False"/>
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
<field name="route_ids"
eval="[ref('stock.route_warehouse0_mto'), ref('purchase_stock.route_warehouse0_buy')]"/>
<field name="tracking">serial</field>
<field name="detailed_type">product</field>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="company_id" ref="base.main_company"/>
<!-- <field name="categ_type">成品</field> -->
</record>
<record id="product_template_outsourcing" model="product.template">
<field name="name">成品委外加工模板</field>
<field name="active" eval="False"/>
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
<field name="route_ids"
eval="[ref('stock.route_warehouse0_mto'), ref('purchase_stock.route_warehouse0_buy'), ref('mrp_subcontracting.route_resupply_subcontractor_mto')]"/>
<field name="tracking">serial</field>
<field name="detailed_type">product</field>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="company_id" ref="base.main_company"/>
<!-- <field name="categ_type">成品</field> -->
</record>
<record id="product_template_default" model="product.template">
<field name="name">成品初始化模板</field>
<field name="active" eval="False"/>
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
<field name="route_ids" eval="[]"/>
<field name="tracking">serial</field>
<field name="detailed_type">product</field>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="company_id" ref="base.main_company"/>
<!-- <field name="categ_type">成品</field> -->
</record>
<!-- 供应商信息业务平台由于数据是python创建只能指定ID -->
<record id="product_supplierinfo_bfm" model="product.supplierinfo">
<field name="partner_id" eval="91"/>
</record>
<record id="product_template_embryo_customer_provided" model="product.template">
<field name="name">坯料客供料模板</field>
<field name="active" eval="False"/>
<field name="categ_id" ref="sf_dlm.product_category_embryo_sf"/>
<field name="route_ids" eval="[
ref('stock.route_warehouse0_mto'),
ref('mrp_subcontracting.route_resupply_subcontractor_mto'),
ref('jikimo_sale_multiple_supply_methods.route_material_processing')]"/>
<field name="sale_ok">false</field>
<field name="tracking">serial</field>
<field name="detailed_type">product</field>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="company_id" ref="base.main_company"/>
<!-- <field name="categ_type">坯料</field> -->
<field name="seller_ids" eval="[ref('jikimo_sale_multiple_supply_methods.product_supplierinfo_bfm')]"/>
</record>
<record id="sf_dlm.product_embryo_sf_self_machining" model="product.product">
<field name="is_manual_processing">true</field>
</record>
<record id="sf_dlm.product_embryo_sf_self_machining" model="product.product">
<field name="route_ids" eval="[(4, ref('mrp_subcontracting.route_resupply_subcontractor_mto'))]"/>
</record>
<record id="sf_dlm.product_embryo_sf_purchase" model="product.product">
<field name="route_ids" eval="[(4, ref('mrp_subcontracting.route_resupply_subcontractor_mto'))]"/>
</record>
</data>
</odoo>

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data noupdate="1">
<record id="route_material_processing" model="stock.route">
<field name="name">带料加工</field>
<field name="product_selectable">true</field>
<field name="warehouse_selectable">true</field>
<field name="warehouse_ids" eval="[ref('stock.warehouse0')]"/>
<field name="sequence">16</field>
</record>
<record id="material_picking_in" model="stock.picking.type">
<field name="name">客供料入库</field>
<field name="code">incoming</field>
<field name="active">true</field>
<field name="company_id" ref="base.main_company"/>
<field name="sequence_code">DL</field>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="default_location_src_id" ref="stock.stock_location_customers"/>
<field name="default_location_dest_id" eval="25"/>
</record>
<record id="rule_material_receiving" model="stock.rule">
<field name="name">带料收货</field>
<field name="route_id" ref="route_material_processing"/>
<field name="location_dest_id" ref="stock.stock_location_company"/>
<field name="location_src_id" ref="stock.stock_location_customers"/>
<field name="picking_type_id" ref="material_picking_in"/>
<field name="action">pull</field>
</record>
</data>
</odoo>

View File

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

View File

@@ -1,13 +0,0 @@
from odoo import models, fields
class MrpBom(models.Model):
_inherit = 'mrp.bom'
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
def bom_create(self, product, bom_type, product_type):
bom_id = super(MrpBom, self).bom_create(product, bom_type, product_type)
# 成品的供应商从模板中获取
if product_type == 'product':
bom_id.subcontractor_id = product.product_tmpl_id.seller_ids.partner_id.id
return bom_id

View File

@@ -1,26 +0,0 @@
from odoo import models, fields, api
class ProductTemplate(models.Model):
_inherit = 'product.template'
is_manual_processing = fields.Boolean(string='人工线下加工')
is_customer_provided = fields.Boolean(string='客供料')
def copy_template(self, product_template_id):
if not isinstance(product_template_id, ProductTemplate):
raise ValueError('%s必须是ProductTemplate类型' % product_template_id)
self.route_ids = product_template_id.route_ids
self.categ_id = product_template_id.categ_id
self.invoice_policy = product_template_id.invoice_policy
self.detailed_type = product_template_id.detailed_type
self.purchase_ok = product_template_id.purchase_ok
self.uom_id = product_template_id.uom_id
self.uom_po_id = product_template_id.uom_po_id
self.company_id = product_template_id.company_id
self.single_manufacturing = product_template_id.single_manufacturing
self.tracking = product_template_id.tracking
self.is_bfm = product_template_id.is_bfm
self.is_manual_processing = product_template_id.is_manual_processing
# 复制 seller_ids
self.seller_ids = [(0, 0, {'partner_id': seller.partner_id.id, 'delay': 1.0}) for seller in product_template_id.seller_ids]

View File

@@ -1,8 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_order_group_production_engineer,sale.order_group_production_engineer,sale.model_sale_order,sf_base.group_production_engineer,1,1,0,0
access_sale_order_line_group_production_engineer,sale_order_line_group_production_engineer,sale.model_sale_order_line,sf_base.group_production_engineer,1,1,0,0
access_product_product_group_production_engineer,product_product_group_production_engineer,product.model_product_product,sf_base.group_production_engineer,1,0,0,0
access_product_template_group_production_engineer,product_template_group_production_engineer,product.model_product_template,sf_base.group_production_engineer,1,0,0,0
access_stock_picking_group_production_engineer,stock_picking_group_production_engineer,stock.model_stock_picking,sf_base.group_production_engineer,1,0,0,0
access_stock_move_group_production_engineer,stock_move_group_production_engineer,stock.model_stock_move,sf_base.group_production_engineer,1,0,0,0
access_mrp_bom_group_production_engineer,mrp_bom_group_production_engineer,mrp.model_mrp_bom,sf_base.group_production_engineer,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_order_group_production_engineer sale.order_group_production_engineer sale.model_sale_order sf_base.group_production_engineer 1 1 0 0
3 access_sale_order_line_group_production_engineer sale_order_line_group_production_engineer sale.model_sale_order_line sf_base.group_production_engineer 1 1 0 0
4 access_product_product_group_production_engineer product_product_group_production_engineer product.model_product_product sf_base.group_production_engineer 1 0 0 0
5 access_product_template_group_production_engineer product_template_group_production_engineer product.model_product_template sf_base.group_production_engineer 1 0 0 0
6 access_stock_picking_group_production_engineer stock_picking_group_production_engineer stock.model_stock_picking sf_base.group_production_engineer 1 0 0 0
7 access_stock_move_group_production_engineer stock_move_group_production_engineer stock.model_stock_move sf_base.group_production_engineer 1 0 0 0
8 access_mrp_bom_group_production_engineer mrp_bom_group_production_engineer mrp.model_mrp_bom sf_base.group_production_engineer 1 0 0 0

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- 由于该模块不能依赖sf_dlm_management, 该功能只能在sf_dlm_management中实现并且依赖该模块-->
<record id="view_product_product_form_inherit_sf" model="ir.ui.view">
<field name="name">view.product.template.form.inherit.sf</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="sf_dlm_management.view_sale_product_template_form_inherit_sf"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='manual_quotation']" position="after">
<field name="is_customer_provided" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -43,7 +43,7 @@ class WorkorderExceptionConroller(http.Controller):
})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
_logger.info('workder_exception error:%s' % e)
return json.JSONEncoder().encode(res)

View File

@@ -1,5 +1,2 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_jikimo_workorder_exception","access.jikimo.workorder.exception","model_jikimo_workorder_exception","mrp.group_mrp_user",1,1,1,0
"access_jikimo_workorder_exception_group_quality","access.jikimo.workorder.exception.group_quality","model_jikimo_workorder_exception","sf_base.group_quality",1,1,1,0
"access_jikimo_workorder_exception_group_quality_director","access.jikimo.workorder.exception.group_quality_director","model_jikimo_workorder_exception","sf_base.group_quality_director",1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_jikimo_workorder_exception access.jikimo.workorder.exception model_jikimo_workorder_exception mrp.group_mrp_user 1 1 1 0
access_jikimo_workorder_exception_group_quality access.jikimo.workorder.exception.group_quality model_jikimo_workorder_exception sf_base.group_quality 1 1 1 0
access_jikimo_workorder_exception_group_quality_director access.jikimo.workorder.exception.group_quality_director model_jikimo_workorder_exception sf_base.group_quality_director 1 1 1 0

View File

@@ -51,11 +51,11 @@ class JikimoWorkorderException(models.Model):
return res
def _get_message(self, message_queue_ids):
contents, _ = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
contents = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
url = self.env['ir.config_parameter'].get_param('web.base.url')
action_id = self.env.ref('mrp.mrp_production_action').id
for index, content in enumerate(contents):
exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id)
url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id)
contents[index] = content.replace('{{url}}', url)
return contents, message_queue_ids
return contents

View File

@@ -1273,18 +1273,3 @@ msgstr ""
#: model:product.template,description_sale:mrp_workorder.product_template_stool_top
msgid "wooden stool top"
msgstr ""
#. module: mrp_workorder
#: model:quality.point.test_type,name:mrp_workorder.test_type_register_consumed_materials
msgid "Register Consumed Materials"
msgstr "登记消耗材料"
#. module: mrp_workorder
#: model:quality.point.test_type,name:mrp_workorder.test_type_register_byproducts
msgid "Register By-products"
msgstr "按产品注册"
#. module: mrp_workorder
#: model:quality.point.test_type,name:mrp_workorder.test_type_print_label
msgid "Print label"
msgstr "打印标签"

View File

@@ -138,7 +138,7 @@ if env.user.has_group('mrp.group_mrp_workorder_dependencies'):
<button name="openMenuPopup" t-att-disabled="isBlocked" class="btn btn-secondary o_workorder_icon_btn fa fa-bars" type="workorder_event" title="menu"/>
<span groups="mrp_workorder.group_mrp_wo_tablet_timer">
<button name="button_pending" type="object" class="btn btn-secondary" attrs="{'invisible': ['|', ('is_user_working', '=', False), ('working_state', '=', 'blocked')]}" barcode_trigger="pause" string="PAUSE"/>
<button name="button_start" type="object" class="btn btn-warning" attrs="{'invisible': ['|', '|', ('is_user_working', '=', True), ('working_state', '=', 'blocked'), ('state', '=', ('done','rework', 'cancel'))]}" barcode_trigger="pause" string="CONTINUE"/>
<button name="button_start" type="object" class="btn btn-warning" attrs="{'invisible': ['|', '|', ('is_user_working', '=', True), ('working_state', '=', 'blocked'), ('state', '=', ('done', 'cancel'))]}" barcode_trigger="pause" string="CONTINUE"/>
<button name="button_unblock" type="object" class="btn btn-secondary btn-danger o_unblock" attrs="{'invisible': [('working_state', '!=', 'blocked')]}">Unblock</button>
<field name="duration" widget="mrp_timer" class="ms-1" readonly="1"/>
</span>

View File

@@ -1050,13 +1050,3 @@ msgstr "工作中心故障"
#: model:ir.model.fields,field_description:quality.field_quality_point_test_type__active
msgid "active"
msgstr "有效"
#. module: quality
#: model:quality.point.test_type,name:quality.test_type_instructions
msgid "Instructions"
msgstr "使用说明"
#. module: quality
#: model:quality.point.test_type,name:quality.test_type_picture
msgid "Take a Picture"
msgstr "照片"

View File

@@ -15,7 +15,7 @@ class TestType(models.Model):
_description = "Quality Control Test Type"
# Used instead of selection field in order to hide a choice depending on the view.
name = fields.Char('Name', required=True,translate=True)
name = fields.Char('Name', required=True)
technical_name = fields.Char('Technical name', required=True)
active = fields.Boolean('active', default=True)

View File

@@ -1185,14 +1185,3 @@ msgstr "请先进行质量检查!"
#: model_terms:ir.ui.view,arch_db:quality_control.quality_alert_team_view_form
msgid "e.g. The QA Masters"
msgstr "例如QA大师"
#. module: quality_control
#: model:quality.point.test_type,name:quality_control.test_type_passfail
msgid "Pass - Fail"
msgstr "通过-失败"
#. module: quality_control
#: model:quality.point.test_type,name:quality_control.test_type_measure
msgid "Measure"
msgstr "测量"

View File

@@ -6,4 +6,3 @@ from . import stock_move
from . import stock_move_line
from . import stock_picking
from . import stock_lot
from . import product_category

View File

@@ -1,32 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from math import sqrt
from dateutil.relativedelta import relativedelta
from datetime import datetime
import random
from odoo import api, models, fields, _
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
from odoo.osv.expression import OR
class ProductCategory(models.Model):
_inherit = 'product.category'
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
if args is None:
args = []
# 添加过滤条件,确保只返回名称为 'abc' 的记录
args += [('name', 'not in', ['Saleable', 'Expenses', 'Deliveries'])]
# 调用父类的 name_search 方法
return super(ProductCategory, self).name_search(name, args=args, operator=operator, limit=limit)
@api.model
def search(self, args, limit=100, offset=0, order=None, count=False):
# 添加过滤条件,确保只返回名称不在指定列表中的记录
args += [('name', 'not in', ['Saleable', 'Expenses', 'Deliveries'])]
# 调用父类的 search 方法
return super(ProductCategory, self).search(args, limit=limit, offset=offset, order=order, count=count)

View File

@@ -7,7 +7,6 @@ from datetime import datetime
import random
from odoo import api, models, fields, _
from odoo.api import depends
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
from odoo.osv.expression import OR
@@ -123,13 +122,7 @@ class QualityPoint(models.Model):
class QualityCheck(models.Model):
_inherit = "quality.check"
part_name = fields.Char('零件名称', compute='_compute_part_name_number', readonly=True)
part_number = fields.Char('零件图号', compute='_compute_part_name_number', readonly=True)
@depends('product_id')
def _compute_part_name_number(self):
for record in self:
record.part_number = record.product_id.part_number
record.part_name = record.product_id.part_name
failure_message = fields.Html(related='point_id.failure_message', readonly=True)
measure = fields.Float('Measure', default=0.0, digits='Quality Tests', tracking=True)
measure_success = fields.Selection([

View File

@@ -203,7 +203,7 @@
<record id="quality_alert_action_check" model="ir.actions.act_window">
<field name="name">Quality Alerts</field>
<field name="res_model">quality.alert</field>
<field name="view_mode">tree,kanban,form,pivot,graph,calendar</field>
<field name="view_mode">kanban,tree,form,pivot,graph,calendar</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new quality alert
@@ -389,8 +389,6 @@
<field name="name" decoration-bf="1"/>
<field name="measure_on" optional="show"/>
<field name='product_id' optional="show"/>
<field name="part_name" optional="hide"/>
<field name='part_number' optional="show"/>
<field name="lot_id" invisible="context.get('show_lots_text')"/>
<field name="lot_name" invisible="not context.get('show_lots_text')"/>
<field name="picking_id" optional="hide" string="Transfer"/>

View File

@@ -394,30 +394,3 @@ class MachineToolCategory(models.Model):
active = fields.Boolean('有效', default=True)
category = fields.Selection([('shukong', u'数控'), ('putong', u'普通')], string=u'机床类别',
default='shukong')
class MachiningAccuracy(models.Model):
_name = 'sf.machining.accuracy'
_description = '加工精度'
name = fields.Char('一般公差', index=True)
standard_tolerance = fields.Char(string="标准公差")
sync_id = fields.Char('同步ID')
class ReSaleOrder(models.Model):
_inherit = 'sale.order'
person_of_delivery = fields.Char('收货人')
telephone_of_delivery = fields.Char('电话号码')
address_of_delivery = fields.Char('联系地址')
class EmbryoRedundancy(models.Model):
_name = "sf.embryo.redundancy"
code = fields.Char('编码', required=True)
name = fields.Char('名称', required=True)
long = fields.Float('长度(mm)', required=True)
width = fields.Float('宽度(mm)', required=True)
height = fields.Float('高度(mm)', required=True)
active = fields.Boolean('有效', default=True)

View File

@@ -56,7 +56,7 @@ class MrsMaterialModel(models.Model):
finish_machining = fields.Float("精加工Vc(m/min)")
remark = fields.Text("备注")
gain_way = fields.Selection(
[("自加工", "自加工"), ("外协", "委外加工"), ("采购", "采购")],
[("自加工", "自加工"), ("外协", "外协"), ("采购", "采购")],
default="", string="获取方式")
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
active = fields.Boolean('有效', default=True)
@@ -100,7 +100,6 @@ class MrsProductionProcess(models.Model):
travel_day = fields.Float('路途天数/d')
sequence = fields.Integer('排序')
# class MrsProcessingTechnology(models.Model):
# _name = 'sf.processing.technology'
# _description = '加工工艺'
@@ -158,8 +157,6 @@ class MrsProductionProcessParameter(models.Model):
for parameter in self:
if parameter.process_id:
name = parameter.process_id.name + '-' + parameter.name
else:
name = parameter.name
result.append((parameter.id, name))
return result

View File

@@ -46,11 +46,6 @@
<field name="category_id" ref="base.module_category_manufacturing_manufacturing"/>
</record>
<record id="group_production_engineer" model="res.groups">
<field name="name">工艺工程师</field>
<field name="implied_ids" eval="[(4, ref('group_sf_mrp_user'))]"/>
<field name="category_id" ref="base.module_category_manufacturing_manufacturing"/>
</record>
<record model="ir.module.category" id="module_category_plan">
<field name="name">计划</field>
@@ -70,7 +65,7 @@
<record id="group_plan_dispatch" model="res.groups">
<field name="name">计划调度岗</field>
<field name="category_id" ref="module_category_plan"/>
<!-- <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> -->
<!-- <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> -->
</record>
<record id="group_plan_director" model="res.groups">

View File

@@ -247,10 +247,3 @@ access_sf_cutting_tool_type_group_sf_stock_manager,sf_cutting_tool_type_group_sf
access_sf_cutting_tool_material_group_plan_dispatch,sf_cutting_tool_material_group_plan_dispatch,model_sf_cutting_tool_material,sf_base.group_plan_dispatch,1,0,0,0
access_sf_functional_cutting_tool_model_group_plan_dispatch,sf_functional_cutting_tool_model_group_plan_dispatch,model_sf_functional_cutting_tool_model,sf_base.group_plan_dispatch,1,0,0,0
access_sf_cutting_tool_type_group_plan_dispatch,sf_cutting_tool_type_group_plan_dispatch,model_sf_cutting_tool_type,sf_base.group_plan_dispatch,1,0,0,0
access_sf_machining_accuracy,sf_machining_accuracy,model_sf_machining_accuracy,base.group_user,1,0,0,0
access_sf_machining_accuracy_admin,sf_machining_accuracy_admin,model_sf_machining_accuracy,base.group_system,1,0,0,0
access_sf_embryo_redundancy,sf_embryo_redundancy,model_sf_embryo_redundancy,base.group_user,1,0,0,0
access_sf_embryo_redundancy_admin,sf_embryo_redundancy_admin,model_sf_embryo_redundancy,base.group_system,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
247
248
249

View File

@@ -1,125 +0,0 @@
// 获取表格数据
function getDomData() {
const dom = $('div[name=cutting_speed_ids]')
if (!dom.length) return
const table = dom.find('.o_list_table')
const thead = table.children('thead')
const tbody = table.children('tbody')
const tbody_child = tbody.children()
const hideTheadDom = thead.find('[data-name=process_capability]')
hideTheadDom.hide().next().hide()
hideTheadDom.before('<th customTh>精加工</th><th customTh>粗加工</th>')
tbody_child.each(function () {
const dom = $(this).children('[name=process_capability]')
if(!dom.length) return
dom.css('cssText', 'display: none!important').next().css('cssText', 'display: none!important')
const isCu = dom.text() == '粗加工' // 是否粗加工
const v = dom.next().text() // 切削速度
dom.after(`<td customSpeed="1" name="process_capability" is="精加工" val="${ v }">${!isCu ? v : ''}</td><td customSpeed="1" name="process_capability" is="粗加工" val="${ v }">${isCu ? v : ''}</td>`)
setListenClick()
})
return;
handleTbody(tbody, newTableData, ΦList, table)
}
// 监听点击
function setListenClick() {
$(document).click(function (e) {
if ($(e.target).attr('customSpeed')) {
const orginV = $('[customInput=1]').children('input').val()
$('[customInput=1]').parent().html(orginV)
const v = $(e.target).attr('val')
const is = $(e.target).attr('is')
$(e.target).html('')
const input = $('<div customInput="1" is="' + is + '" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
input.children('input').val(v)
$(e.target).append(input)
input.children('input').focus()
input.children('input').select()
} else if ($(e.target).attr('customInput')) {
} else {
const orginV = $('[customInput=1]').children('input').val()
$('[customInput=1]').parent().html(orginV)
const v = $(e.target).attr('val')
}
})
$(document).off('change') // 防止重复绑定
$(document).on('change', '[customInput] input', async function () {
$(this).parents('td').attr('val', $(this).val())
$(this).parents('td').siblings('[customspeed]').attr('val', $(this).val())
var eve1 = new Event('change')
var eve2 = new Event('input')
var eve3 = new Event('click')
let patchSpeedDom = $(this).parents('td').siblings('[name=cutting_speed]')
let patchProcessDom = $(this).parents('td').siblings('[name=process_capability]')
$(this).parents('td').siblings('[customspeed]').text('') // 清空其他加工类型的数据
await timeOut(500)
patchProcessDom[0].dispatchEvent(eve3)
await timeOut(200)
const processVal = $(this).parent().attr('is')
patchProcessDom.find('select').val(`"${processVal}"`) // 设置源select的val为“加工类型 is”、
patchProcessDom.attr("data-tooltip", `${processVal}`)
patchProcessDom.find('select')[0].dispatchEvent(eve1)
patchSpeedDom[0].dispatchEvent(eve3)
await timeOut(200)
patchSpeedDom.find('input').val($(this).val())
await timeOut(50)
patchSpeedDom.find('input')[0].dispatchEvent(eve2)
patchSpeedDom.find('input')[0].dispatchEvent(eve1)
})
$(document).off('blur') // 防止重复绑定
$(document).on('blur', '[customInput] input', async function () {
if(!$(this).length) return
$(this).parents('td').siblings('[customspeed]').text('') // 清空其他加工类型的数据
let patchProcessDom = $(this).parents('td').siblings('[name=process_capability]')
try {
patchProcessDom[0].dispatchEvent(new Event('click'))
const processVal = $(this).parent().attr('is')
patchProcessDom.find('select').val(`"${processVal}"`) // 设置源select的val为“加工类型 is”、
patchProcessDom.attr("data-tooltip", `${processVal}`)
patchProcessDom.find('select')[0].dispatchEvent(new Event('change'))
} catch {
}
})
}
function timeOut(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, time)
})
}
function listenAdd() {
$('td.o_field_x2many_list_row_add a').click(async function () {
await timeOut(500)
const tr = $('.o_list_table').children('tbody').children('tr').eq(-2)
if(tr.children('td').eq(2).text() == '') {
const dom = tr.children('[name=process_capability]')
if(!dom.length) return
dom.css('cssText', 'display: none!important').next().css('cssText', 'display: none!important')
const isCu = dom.text() == '粗加工' // 是否粗加工
const v = dom.next().text() // 切削速度
dom.after(`<td customSpeed="1" name="process_capability" is="精加工" val="${ v }">${!isCu ? v : ''}</td><td customSpeed="1" name="process_capability" is="粗加工" val="${ v }">${isCu ? v : ''}</td>`)
}
})
}
function listenSave() {
$('.o_form_button_save').click( async function () {
await timeOut(1000)
if($(this).parent().next().length) return
$('th[customTh],td[cusomSpeed]').remove()
getDomData()
})
}
listenAdd()
listenSave()
getDomData()

View File

@@ -1,47 +0,0 @@
// 因为表格可以拖动设置宽度所以需要用js设置初始宽度
function setBasicParamTableWidth() {
// const _100px = 'th[data-name="cutting_blade_length"],th[data-name="cutting_blade_length"],th[data-name="name"],th[data-name="tip_handling_size"],th[data-name="cutting_depth_max"],th[data-name="diameter_inner_circle"],th[data-name="diameter_mounting_hole" ],th[data-name="radius_tip_re" ],th[data-name="is_chip_breaker"],th[data-name="chip_breaker_type_code"],th[data-name="blade_profile"]'
// const _65px = 'th[data-name="edge_angle"],th[data-name="relief_angle"],[data-name="total_length"],th[data-name="length"],th[data-name="thickness"],th[data-name="blade_number"]'
// const _80px = 'th[data-name="arbor_diameter"],th[data-name="head_height"],th[data-name="head_width"],th[data-name="head_length"],th[data-name="blade_diameter"],th[data-name="blade_length"] ,th[data-name="neck_length"] ,th[data-name="neck_diameter"] ,th[data-name="shank_diameter"],th[data-name="shank_length"],th[data-name="tip_diameter"],th[data-name="knife_tip_taper"],th[data-name="blade_helix_angle"] ,th[data-name="blade_width"],th[data-name="blade_depth"]'
// const _50px = 'th[data-name="pitch"],th[data-name="width"],th[data-name="height"]'
const basicParamDom = $('.fixTableCss')
// const basicParamDom_100px = basicParamDom.find(_100px) // 四字以上
// const basicParamDom_65px = basicParamDom.find(_65px) // 大概三个字加单位
// const basicParamDom_80px = basicParamDom.find(_80px) // 大概四个字加单位
// const basicParamDom_50px= basicParamDom.find(_50px) // 大概两个字加单位
//
// basicParamDom_100px.css({'width': '100px', 'max-width': 'auto', ',min-width': 'auto'})
// basicParamDom_65px.css({'width': '65px', 'max-width': 'auto', ',min-width': 'auto'})
// basicParamDom_80px.css({'width': '80px', 'max-width': 'auto', ',min-width': 'auto'})
// basicParamDom_50px.css({'width': '50px', 'max-width': 'auto', ',min-width': 'auto'})
let dom = []
try {
dom = basicParamDom.find('table').find('thead').children().children()
} catch {
dom = []
}
if (!dom) return
dom.each(function () {
if ($(this).hasClass('row_no') >= 0) { // 序号列
// 不设置 通过css设置
}
const text = $(this).text().split('(')
if ($(this).attr('data-name') == 'name' || text[0].length > 4) {
$(this).width('100px')
} else if(text[0].length == 4){
$(this).width('80px')
} else if(text[0].length == 3){
$(this).width('65px')
} else if(text[0].length == 2){
$(this).width('50px')
}
})
}
setBasicParamTableWidth()
$('.o_field_many2one_selection').on('click', $('#cutting_tool_material_id + ul'), function () {
setTimeout(setBasicParamTableWidth, 500)
})

View File

@@ -1,159 +0,0 @@
// 获取表格数据
function getDomData() {
const dom = $('#updateTable').prev()
if (!dom.length) return
const table = $('#updateTable').prev().find('.o_list_table')
const customTable = table.clone()
customTable.addClass('customTable')
table.parent().append(customTable)
table.hide()
const thead = customTable.children('thead')
const tbody = customTable.children('tbody')
const tableData = []
const tbody_child = tbody.children()
const tbody_child_len = tbody_child.length
for (let v = 0; v < tbody_child_len; v++) { // 将数据取出来到tableData里面
const data = tbody_child[v].innerText.split('\t')
// console.log('dom data',data)
const [index, deep, name, Φ, value] = data
tableData.push({index, deep, name, Φ, value})
}
const ΦList = [...new Set(tableData.map(_ => _.name))] // ΦList去重
const newTableData = {}
tableData.forEach(_ => {
const key = _.deep + '|' + _.Φ
!newTableData[key] ? newTableData[key] = {i: _.index} : '';
if (_.Φ) { // 去除没有Φ的脏数据
newTableData[key]['Φ' + _.Φ] = _.value
newTableData[key]['Φ' + _.Φ + 'i'] = _.index
}
})
// console.log('qwdh',tableData, ΦList, newTableData);
if (ΦList.filter(_ => _).length == 0) return;
handleThead(thead, ΦList)
handleTbody(tbody, newTableData, ΦList, table)
}
// 重新设置表头、
function handleThead(thead, ΦList) {
const dom = thead.children().eq(0).children()
const len = dom.length
dom.eq(0).attr('rowspan', 2)
dom.eq(1).attr('rowspan', 2)
len == 5 ? dom.eq(2).attr('rowspan', 2) : ''
dom.eq(-2).attr('colspan', ΦList.length)
dom.eq(-1).remove()
const tr = document.createElement('tr')
for (let v = 0; v < ΦList.length; v++) {
const th = document.createElement('th')
th.innerText = 'Φ' + ΦList[v]
tr.append(th)
}
thead.append(tr)
}
// 重新设置表格
function handleTbody(tbody, newTableData, ΦList, table) {
console.log(newTableData)
tbody.html('')
let i = 0
const data = Object.keys(newTableData)
// data.sort((a, b) => {
// a = a.split('=')[1].split('%')[0]
// b = b.split('=')[1].split('%')[0]
// return a - b
// })
// console.log('wqoqw ',ΦList)
data.forEach(_ => {
i++
const tr = $('<tr class="o_data_row"></tr>')
const td0 = $('<td></td>')
td0.text(i)
tr.append(td0)
const lit = _.split('|')
//
const td1 = $('<td></td>')
const td2 = $('<td></td>')
td1.text(lit[0])
td2.text(lit[1] || '')
tr.append(td1)
tr.append(td2)
ΦList.forEach(Φ => {
const td = $('<td class="o_data_cell cursor-pointer o_field_cell o_list_char"></td>')
td.text(newTableData[_]['Φ' + Φ])
td.attr('col', newTableData[_]['Φ' + Φ + 'i'])
td.attr('val', newTableData[_]['Φ' + Φ])
td.attr('coustomTd', 1)
tr.append(td)
})
// // for (let j = 0; j < ΦList.length; j++) {
// // const td = document.createElement('td')
// // td.innerText = newTableData[data[v]][_]
// // th.append(td)
// // }
tbody.append(tr)
})
// $(document).click(function (e) {
// if ($(e.target).attr('coustomTd')) {
// const orginV = $('[coustomInput=1]').children('input').val()
// $('[coustomInput=1]').parent().html(orginV)
// const v = $(e.target).attr('val')
// console.log($(e.target));
// $(e.target).html('')
// const input = $('<div coustomInput="1" name="feed_per_tooth" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
// input.children('input').val(v)
// $(e.target).append(input)
// input.children('input').focus()
// input.children('input').select()
// } else if ($(e.target).attr('coustomInput')) {
//
// } else {
// const orginV = $('[coustomInput=1]').children('input').val()
// $('[coustomInput=1]').parent().html(orginV)
// const v = $(e.target).attr('val')
// }
// })
// $(document).off('change') // 防止重复绑定
// $(document).on('change', '[coustomInput] input', function () {
// $(this).parents('td').attr('val', $(this).val());
// var eve1 = new Event('change');
// var eve2 = new Event('input');
// var eve3 = new Event('click');
// const i = $(this).parents('td').attr('col');
// let patchDom = table.find('tbody').children('tr').eq(i - 1);
//
// if (patchDom.length === 0) {
// console.error('No such row found');
// return;
// }
//
// patchDom = patchDom.children().eq(-1);
//
// setTimeout(() => {
// if (patchDom.length === 0) {
// console.error('No such cell found');
// return;
// }
// patchDom[0].dispatchEvent(eve3); // Simulate click event
//
// setTimeout(() => {
// patchDom = patchDom.find('input');
// if (patchDom.length === 0) {
// console.error('No input found in the target cell');
// return;
// }
// patchDom.val($(this).val());
// patchDom[0].dispatchEvent(eve2);
// patchDom[0].dispatchEvent(eve1);
// }, 200);
// }, 500);
// });
}
getDomData()

View File

@@ -1,52 +0,0 @@
.o_list_renderer .o_list_table tbody > tr > td:not(.o_list_record_selector):not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove) {
white-space: nowrap !important;
}
.text-truncate {
overflow: unset !important;
text-overflow: unset !important;
white-space: unset !important;
}
// 设置表格不超出页面宽度
.o_form_view .o_field_widget .o_list_renderer {
width: calc(100% - 64px) !important;
margin:0 auto;
overflow: auto;
}
// 表格针对处理
.fixTableCss {
text-align: center;
.o_list_number_th,.o_list_number {
text-align: center!important;
}
.ui-sortable {
tr > td:first-child {
padding: 0!important;
}
}
.row_no {
padding: 0!important;;
width: 35px!important;
}
th[data-name="total_length"],th[data-name="length"],th[data-name="thickness"] {
.flex-row-reverse {
span {
text-align: center;
}
}
}
}
// 其他不能用js处理的表格
.otherTableFix {
th[data-name="cutting_tool_material_id"] {
width: 100px!important;
}
th[data-name="ramping_angle_max"],th[data-name="ramping_angle_min"] {
width: 200px!important;
}
}

View File

@@ -14,7 +14,6 @@
.img-fluid {
max-width: unset !important;
width: 40px;
}
.o_inner_group .img-fluid {

View File

@@ -614,45 +614,4 @@
<field name="res_model">sf.machine.control_system</field>
<field name="view_mode">tree</field>
</record>
#------------------加工精度------------------
<record model="ir.ui.view" id="tree_sf_machining_accuracy_view">
<field name="name">tree.sf.machining.accuracy</field>
<field name="model">sf.machining.accuracy</field>
<field name="arch" type="xml">
<tree string="加工精度" create="0" edit="0" delete="0">
<field name="name"/>
<field name="standard_tolerance"/>
</tree>
</field>
</record>
<record id="action_sf_machining_accuracy" model="ir.actions.act_window">
<field name="name">加工精度</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.machining.accuracy</field>
<field name="view_mode">tree</field>
</record>
#------------------坯料冗余量------------------
<record model="ir.ui.view" id="tree_sf_embryo_redundancy_view">
<field name="name">tree.sf.embryo.redundancy</field>
<field name="model">sf.embryo.redundancy</field>
<field name="arch" type="xml">
<tree string="坯料冗余量" create="0" edit="0" delete="0">
<field name="name"/>
<field name="code"/>
<field name="long"/>
<field name="width"/>
<field name="height"/>
</tree>
</field>
</record>
<record id="action_sf_embryo_redundancy" model="ir.actions.act_window">
<field name="name">坯料冗余量</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.embryo.redundancy</field>
<field name="view_mode">tree</field>
</record>
</odoo>

View File

@@ -141,25 +141,12 @@
sequence="1"
action="action_sf_machine_brand"/>
<menuitem
id="menu_sf_embryo_redundancy"
parent="menu_sf_base"
name="坯料冗余"
sequence="2"
action="action_sf_embryo_redundancy"/>
<menuitem
id="menu_sf_machining_accuracy"
parent="menu_sf_base"
name="加工精度"
sequence="3"
action="action_sf_machining_accuracy"/>
<menuitem
id="menu_sf_machine_control_system"
parent="menu_sf_base"
name="数控系统"
sequence="4"
sequence="1"
action="action_sf_machine_control_system"/>

View File

@@ -360,7 +360,6 @@
<field name="cooling_jacket"/>
</tree>
</field>
<script src="/sf_base/static/js/setTableWidth.js?time=3"></script>
</page>
<page string="切削速度Vc"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
@@ -381,9 +380,6 @@
<field name="cutting_speed"/>
</tree>
</field>
<script src="/sf_base/static/js/customTable.js?time=3"></script>
<script src="/sf_base/static/js/setTableWidth.js?time=3"></script>
</page>
<page string="每齿走刀量fz"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
@@ -396,8 +392,6 @@
<field name="feed_per_tooth"/>
</tree>
</field>
<div id="updateTable"></div>
<script src="/sf_base/static/js/updateTable.js?time=3"></script>
<field name="feed_per_tooth_ids_3"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀片'))]}">
<tree editable="bottom" class="center" create="0" delete="0">

View File

@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
import json
import logging
import traceback
from odoo import http
from odoo.http import request
@@ -125,8 +123,7 @@ class Sf_Bf_Connect(http.Controller):
res['factory_order_no'] = order_id.name
return json.JSONEncoder().encode(res)
except Exception as e:
traceback_error = traceback.format_exc()
logging.error('get_bfm_process_order_list error: %s' % traceback_error)
logging.info('get_bfm_process_order_list error:%s' % e)
res['status'] = -1
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
request.cr.rollback()

View File

@@ -151,12 +151,6 @@ class JdEclp(models.Model):
_logger.info('准备调接口1')
url1 = config['bfm_url_new'] + '/api/create/jd/order'
requests.post(url1, json=json1, data=None)
# ===============修改销售订单状态为【物流中】===================
item = self.env['sale.order'].sudo().search([('name', '=', self.origin)])
if not item:
raise ValidationError('没有查询到订单号为【%s】的销售订单!' % self.origin)
else:
item.write({'state': 'physical_distribution'})
_logger.info('调用成功1')
_logger.info('准备调接口2')
json2 = {

View File

@@ -36,8 +36,6 @@ class StatusChange(models.Model):
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
try:
res = super(StatusChange, self).action_confirm()
# 修改销售订单状态为【加工中】
self.write({'state': 'processing'})
logging.info('原生方法返回结果:%s' % res)
# 原有方法执行后进行额外的操作如调用外部API
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

View File

@@ -58,7 +58,7 @@
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//form//sheet//notebook//page[@name='operations']" position="after">
<page string="发货信息" name="tracking" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}">
<page string="发货信息" name="tracking">
<group>
<group>
<field name="senderNickName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data noupdate="1">
<data>
<record id="product_category_embryo_sf" model="product.category">
<field name="name">坯料</field>
<field name="type">坯料</field>

View File

@@ -1,3 +1 @@
from . import product_supplierinfo
from . import stock_rule_inherit

View File

@@ -1,87 +0,0 @@
import logging
from itertools import groupby
from odoo import models, fields, api, _
class StockRuleInherit(models.Model):
_inherit = 'stock.rule'
@api.model
def _run_buy(self, procurements):
# 判断补货组的采购类型
procurements_group = {'standard': [], 'outsourcing': []}
for procurement, rule in procurements:
is_outsourcing = False
product = procurement.product_id
# 获取主 BOM
bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
if bom:
# 遍历 BOM 中的组件(即坯料等)
for line in bom.bom_line_ids:
raw_material = line.product_id
# 检查路线
for route in raw_material.route_ids:
# print('route.name:', route.name)
if route.name == '按订单补给外包商':
is_outsourcing = True
if is_outsourcing:
procurements_group['outsourcing'].append((procurement, rule))
else:
procurements_group['standard'].append((procurement, rule))
for key, value in procurements_group.items():
super(StockRuleInherit, self)._run_buy(value)
if key == 'outsourcing':
for procurement, rule in value:
supplier = procurement.values.get('supplier')
if supplier:
domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
supplier.partner_id)
logging.info("domain=============: %s", domain)
po = self.env['purchase.order'].sudo().search([
('partner_id', '=', supplier.partner_id.id),
('company_id', '=', procurement.company_id.id), # 保证公司一致
('origin', 'like', procurement.origin), # 根据来源匹配
('state', '=', 'draft') # 状态为草稿
], limit=1)
logging.info("po=: %s", po)
if po:
po.write({'purchase_type': 'outsourcing'})
# # 首先调用父类的 _run_buy 方法,以保留原有逻辑
# super(StockRuleInherit, self)._run_buy(procurements)
# 然后在这里添加自定义的逻辑
# for procurement, rule in procurements:
# product = procurement.product_id
# # 获取主 BOM
# bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
# if bom:
# # 遍历 BOM 中的组件(即坯料等)
# for line in bom.bom_line_ids:
# raw_material = line.product_id
# # 检查路线
# for route in raw_material.route_ids:
# # print('route.name:', route.name)
# if route.name == '按订单补给外包商': # 或者用 route.id 检查精确的路线
# print("按订单补给外包商============是")
# # 使用 procurement.values['supplier'] 获取供应商
# supplier = procurement.values.get('supplier')
# if supplier:
# domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
# supplier.partner_id)
# logging.info("domain=============: %s", domain)
# po = self.env['purchase.order'].sudo().search([
# ('partner_id', '=', supplier.partner_id.id),
# ('company_id', '=', procurement.company_id.id), # 保证公司一致
# ('origin', '=', procurement.origin), # 根据来源匹配
# ('state', '=', 'draft') # 状态为草稿
# ], limit=1)
# logging.info("po=: %s", po)
# if po:
# po.write({'purchase_type': 'outsourcing'})
# break

View File

@@ -9,7 +9,7 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing','jikimo_attachment_viewer'],
'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing'],
'data': [
'data/stock_data.xml',
'views/product_template_management_view.xml',

View File

@@ -2,7 +2,6 @@
<odoo>
<data>
<record id="mrp.product_template_action" model="ir.actions.act_window">
<field name="view_mode">tree,kanban,form,activity</field>
<field name="context">
{"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
</field>
@@ -16,10 +15,10 @@
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name='is_bfm' invisible="1"/>
<field name='categ_type' invisible="1"/>
<field name='part_name' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
<field name='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
<field name='machining_drawings' attrs="{'invisible': [('categ_type', '!=', '成品')]}" widget="adaptive_viewer"/>
<field name='quality_standard' attrs="{'invisible': [('categ_type', '!=', '成品')]}" widget="adaptive_viewer"/>
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/>
<field name="is_customer_provided" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
<field name="upload_model_file"
widget="many2many_binary"
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('is_bfm','=', True)]}"/>
@@ -66,9 +65,6 @@
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
domain="[('fixture_model_id','=',fixture_model_id)]"/>
</field>
<xpath expr="//field[@name='uom_id']" position="before">
<field name="is_manual_processing" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
</xpath>
<xpath expr="//label[@for='volume']" position="before">
<label for="length" string="尺寸"
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}"/>
@@ -115,17 +111,6 @@
'刀具')], 'required': True}
</attribute>
</xpath>
<xpath expr="//sheet//notebook" position="inside">
<page string="2D加工图纸" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])]}">
<field name='machining_drawings' widget="adaptive_viewer"/>
</page>
</xpath>
<xpath expr="//sheet//notebook" position="inside">
<page string="质检标准" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])]}">
<field name='quality_standard' widget="adaptive_viewer"/>
</page>
</xpath>
<!-- <xpath expr="//field[@name='default_code']" position="attributes">-->
<!-- <attribute name="attrs">{'readonly': [('categ_type', '=', '刀具')], 'invisible':-->
<!-- [('product_variant_count', '>' , 1)]}-->

View File

@@ -2,4 +2,3 @@
from . import hr_employee
from . import res_config_setting
from . import res_users

View File

@@ -11,52 +11,6 @@ class JkmPracticeEmployee(models.Model):
we_id = fields.Char(string='企微ID', index=True)
@api.model_create_multi
def create(self, vals_list):
for val in vals_list:
if 'work_email' in val:
val["we_id"] = self._get_we_id(val.get('work_email'))
return super(JkmPracticeEmployee, self).create(vals_list)
def write(self, vals):
if 'work_email' in vals:
vals["we_id"] = self._get_we_id(vals.get('work_email'))
return super(JkmPracticeEmployee, self).write(vals)
def unlink(self):
for record in self:
res_partner_obj = record.env['res.partner'].sudo().search([('email', '=', record.work_email)])
if res_partner_obj:
res_partner_obj.unlink()
res = super(JkmPracticeEmployee, self).unlink()
return res
@api.depends('work_contact_id', 'work_contact_id.mobile', 'work_contact_id.email')
def _compute_work_contact_details(self):
for employee in self:
if employee.work_contact_id:
employee.mobile_phone = employee.work_contact_id.mobile
employee.work_email = employee.work_contact_id.email
if employee.work_contact_id.email:
employee.we_id = self._get_we_id(employee.work_contact_id.email)
def _get_we_id(self, work_email):
json1 = {
'params': {
'work_email': work_email
}
}
url = '/api/get/we_id/info'
config = self.env['res.config.settings'].get_values()
ret = requests.post((config['ims_url'] + url), json=json1, data={})
result = ret.json()['result']
if result['code'] == 200:
if result['we_id']:
return result['we_id']
return None
def _employee_info_sync(self):
url = '/api/get/organization'
config = self.env['res.config.settings'].get_values()

View File

@@ -1,15 +0,0 @@
# -*- coding: utf-8 -*-
import random
from odoo import models, fields, api
from odoo.http import request
from odoo.exceptions import AccessDenied
import logging
_logger = logging.getLogger(__name__)
class ResUsers(models.Model):
_inherit = 'res.users'
we_employee_id = fields.Char(string=u'企业微信账号', related='employee_id.we_id', default="")

View File

@@ -1264,7 +1264,6 @@ class Sf_Dashboard_Connect(http.Controller):
"""
获取
"""
logging.info("kw=============:%s" % kw)
res = {'status': 1, 'message': '成功', 'data': {}}
# 连接数据库
conn = psycopg2.connect(**db_config)

View File

@@ -10,9 +10,6 @@
<searchpanel>
<field name="routing_type" select="multi" string="工序类型" icon="fa-building" enable_counters="1"/>
<field name="state" select="multi" string="状态" icon="fa-building" enable_counters="1"/>
<field name="construction_period_status" select="multi" icon="fa-building" enable_counters="1"/>
<field name="tag_type" select="multi" icon="fa-building" enable_counters="1"/>
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
</searchpanel>

View File

@@ -2,4 +2,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import wizard

View File

@@ -18,7 +18,6 @@
'views/equipment_maintenance_standards_views.xml',
'views/maintenance_request_views.xml',
'views/maintenance_equipment_category_views.xml',
'wizard/maintenance_request_wizard.xml',
],
'installable': True,
'application': False,

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
import json
import base64
import logging
from datetime import timedelta
import requests
from odoo.addons.sf_base.commons.common import Common
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.exceptions import UserError
class SfMaintenanceEquipmentCategory(models.Model):
@@ -123,13 +122,6 @@ class SfMaintenanceEquipment(models.Model):
'sf_maintenance_equipment_ids', string='设备维保标准')
eq_maintenance_id = fields.Many2one('equipment.maintenance.standards', string='设备保养标准',
domain="[('maintenance_type','=','保养')]")
initial_action_date = fields.Date(string='重置保养日期')
initial_action_date_old = fields.Date(string='重置保养日期(旧)')
next_action_date = fields.Date(string='下次预防保养')
initial_overhaul_date = fields.Date(string='重置维修日期')
initial_overhaul_date_old = fields.Date(string='重置维修日期(旧)')
overhaul_date = fields.Date(string='下次预防检修')
overhaul_period = fields.Integer(string='预防检修频次')
overhaul_duration = fields.Float(string='检修时长')
@@ -137,61 +129,6 @@ class SfMaintenanceEquipment(models.Model):
overhaul_id = fields.Many2one('equipment.maintenance.standards', string='设备检修标准',
domain="[('maintenance_type','=','检修')]")
def confirm_maintenance(self):
"""
确认保养/检修
"""
context = self.env.context
if context['type'] == '保养':
if not self.initial_action_date:
raise ValidationError('重置保养日期不能为空!!')
elif self.initial_action_date < fields.Date.today():
raise ValidationError('重置保养日期不能小于当前日期!!')
elif context['type'] == '检修':
if not self.initial_overhaul_date:
raise ValidationError('重置检修日期不能为空!!')
elif self.initial_overhaul_date < fields.Date.today():
raise ValidationError('重置检修日期不能小于当前日期!!')
request_ids = self.env['maintenance.request'].search([('stage_id.done', '=', False),
('equipment_id', '=', self.id),
('maintenance_type', '=', 'preventive'),
('sf_maintenance_type', '=', context['type'])])
if not request_ids:
return self.create_maintenance_request(context['type'])
else:
return {
"type": "ir.actions.act_window",
"res_model": "maintenance.request.wizard",
"views": [[False, "form"]],
"target": "new",
'context': {
'equipment_id': self.id
}
}
def create_maintenance_request(self, maintenance_request_type):
"""
根据条件创建维保计划
"""
if maintenance_request_type == '保养':
self._create_new_request(self.initial_action_date + timedelta(days=self.period))
self.initial_action_date_old = self.initial_action_date
elif maintenance_request_type == '检修':
self._create_new_request1(self.initial_overhaul_date + timedelta(days=self.overhaul_period))
self.initial_overhaul_date_old = self.initial_overhaul_date
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': f'创建{maintenance_request_type}计划',
'message': f'{maintenance_request_type}维保计划创建成功',
'type': 'success',
'next': {'type': 'ir.actions.act_window_close'},
}
}
@api.onchange('eq_maintenance_id', 'overhaul_id')
def _compute_equipment_maintenance_standards_ids(self):
for record in self:
@@ -654,13 +591,11 @@ class SfMaintenanceEquipment(models.Model):
('equipment_id', '=', equipment.id),
('sf_maintenance_type', '=', '保养'),
('stage_id.done', '!=', True),
('active', '!=', False),
('close_date', '=', False)], order="request_date asc", limit=1)
last_maintenance_done = self.env['maintenance.request'].search([
('equipment_id', '=', equipment.id),
('sf_maintenance_type', '=', '保养'),
('stage_id.done', '=', True),
('active', '!=', False),
('close_date', '!=', False)], order="close_date desc", limit=1)
if next_maintenance_todo and last_maintenance_done:
next_date = next_maintenance_todo.request_date
@@ -689,9 +624,7 @@ class SfMaintenanceEquipment(models.Model):
if next_date < date_now:
next_date = date_now
else:
if not equipment.initial_action_date:
raise ValidationError('重置保养日期不能为空!!!')
next_date = equipment.initial_action_date + timedelta(days=equipment.period)
next_date = equipment.effective_date + timedelta(days=equipment.period)
equipment.next_action_date = next_date
else:
self.next_action_date = False
@@ -702,13 +635,11 @@ class SfMaintenanceEquipment(models.Model):
('equipment_id', '=', equipment.id),
('sf_maintenance_type', '=', '检修'),
('stage_id.done', '!=', True),
('active', '!=', False),
('close_date', '=', False)], order="request_date asc", limit=1)
last_maintenance_done = self.env['maintenance.request'].search([
('equipment_id', '=', equipment.id),
('sf_maintenance_type', '=', '检修'),
('stage_id.done', '=', True),
('active', '!=', False),
('close_date', '!=', False)], order="close_date desc", limit=1)
if next_maintenance_todo and last_maintenance_done:
next_date = next_maintenance_todo.request_date
@@ -737,9 +668,7 @@ class SfMaintenanceEquipment(models.Model):
if next_date < date_now:
next_date = date_now
else:
if not equipment.initial_overhaul_date:
raise ValidationError('重置维修日期不能为空')
next_date = equipment.initial_overhaul_date + timedelta(days=equipment.overhaul_period)
next_date = equipment.effective_date + timedelta(days=equipment.overhaul_period)
equipment.overhaul_date = next_date
else:
self.overhaul_date = False
@@ -806,7 +735,6 @@ class SfMaintenanceEquipment(models.Model):
next_requests = self.env['maintenance.request'].search([('stage_id.done', '=', False),
('equipment_id', '=', equipment.id),
('maintenance_type', '=', 'preventive'),
('active', '=', True),
('request_date', '=', equipment.next_action_date),
('sf_maintenance_type', '=', '保养')])
if not next_requests:
@@ -815,7 +743,6 @@ class SfMaintenanceEquipment(models.Model):
next_requests = self.env['maintenance.request'].search([('stage_id.done', '=', False),
('equipment_id', '=', equipment.id),
('maintenance_type', '=', 'preventive'),
('active', '=', True),
('request_date', '=', equipment.overhaul_date),
('sf_maintenance_type', '=', '检修')])
if not next_requests:

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import re
import json
import logging
import datetime
import requests
from odoo import api, fields, models, _
@@ -63,22 +62,22 @@ class SfMaintenanceEquipmentOEE(models.Model):
("封存(报废)", "封存(报废)")],
default='正常', string="机床状态", related='equipment_id.state')
online_time = fields.Char('开机时长(小时)', readonly='True')
online_time = fields.Char('开机时长(小时)', reaonly='True')
offline_time = fields.Char('关机时长(小时)', readonly='True')
idle_nums = fields.Integer('待机次数', readonly='True')
offline_time = fields.Char('关机时长(小时)', reaonly='True')
idle_nums = fields.Integer('待机次数', reaonly='True')
# 待机时长
idle_time = fields.Char('待机时长(小时)', readonly='True')
idle_time = fields.Char('待机时长(小时)', reaonly='True')
# 待机率
idle_rate = fields.Char('待机率(%)', readonly='True')
idle_rate = fields.Char('待机率(%)', reaonly='True')
work_time = fields.Char('加工时长(小时)', readonly='True')
work_rate = fields.Char('可用率(%)', readonly='True')
fault_time = fields.Char('故障时长(小时)', readonly='True')
fault_rate = fields.Char('故障率(%)', readonly='True')
fault_nums = fields.Integer('故障次数', readonly='True')
work_time = fields.Char('加工时长(小时)', reaonly='True')
work_rate = fields.Char('可用率(%)', reaonly='True')
fault_time = fields.Char('故障时长(小时)', reaonly='True')
fault_rate = fields.Char('故障率(%)', reaonly='True')
fault_nums = fields.Integer('故障次数', reaonly='True')
# 设备故障日志
sf_maintenance_logs_ids = fields.One2many('sf.maintenance.logs', 'maintenance_equipment_oee_id', '设备故障日志',
@@ -91,18 +90,11 @@ class SfMaintenanceEquipmentOEE(models.Model):
def get_running_datas(self):
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
logging.info("base_url=============:%s" % base_url)
# 只有当原始 URL 使用 http 时才替换为 https
if base_url.startswith("http://"):
secure_base_url = base_url.replace("http://", "https://")
else:
secure_base_url = base_url
url_time = secure_base_url + '/api/RunningTimeDetail'
logging.info("url_time=============:%s" % url_time)
url_time = base_url + '/api/RunningTimeDetail'
cnc_list_obj = self.env['maintenance.equipment'].sudo().search(
[('function_type', '!=', False), ('active', '=', True)])
machine_list = list(map(lambda x: x.code, cnc_list_obj))
logging.info("machine_list=============:%s" % machine_list)
# print('machine_list: %s' % machine_list)
data_time = {
"machine_list": str(machine_list)
@@ -375,25 +367,25 @@ class SfMaintenanceEquipmentOEELog(models.Model):
[("ZXJGZX", "钻铣加工中心"), ("CXJGZX", "车削加工中心"), ("FHJGZX", "复合加工中心")],
default="", string="功能类型")
machine_tool_picture = fields.Binary('设备图片')
type_id = fields.Many2one('sf.machine_tool.type', '品牌型号', readonly='True')
type_id = fields.Many2one('sf.machine_tool.type', '品牌型号', reaonly='True')
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
("检修", "检修"), ("保养", "保养")], default="", string="实时状态")
online_time = fields.Char('开机时长', readonly='True')
online_time = fields.Char('开机时长', reaonly='True')
offline_time = fields.Char('关机时长', readonly='True')
offline_nums = fields.Integer('关机次数', readonly='True')
offline_time = fields.Char('关机时长', reaonly='True')
offline_nums = fields.Integer('关机次数', reaonly='True')
# 待机时长
idle_time = fields.Char('待机时长', readonly='True')
idle_time = fields.Char('待机时长', reaonly='True')
# 待机率
idle_rate = fields.Char('待机率', readonly='True')
idle_rate = fields.Char('待机率', reaonly='True')
work_time = fields.Char('加工时长', readonly='True')
work_rate = fields.Char('可用率', readonly='True')
fault_time = fields.Char('故障时长', readonly='True')
fault_rate = fields.Char('故障率', readonly='True')
fault_nums = fields.Integer('故障次数', readonly='True')
work_time = fields.Char('加工时长', reaonly='True')
work_rate = fields.Char('可用率', reaonly='True')
fault_time = fields.Char('故障时长', reaonly='True')
fault_rate = fields.Char('故障率', reaonly='True')
fault_nums = fields.Integer('故障次数', reaonly='True')
detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情')

View File

@@ -14,8 +14,6 @@ class SfMaintenanceEquipmentCategory(models.Model):
equipment_maintenance_id = fields.Many2one('equipment.maintenance.standards', string='设备维保标准',
domain="[('maintenance_type','=',sf_maintenance_type)]")
active = fields.Boolean('有效', default=True)
@api.onchange('sf_maintenance_type')
def _compute_equipment_maintenance_request_id(self):
for record in self:

View File

@@ -20,12 +20,7 @@ access_maintenance_equipment_agv_log,maintenance_equipment_agv_log,model_mainten
access_maintenance_system_user,equipment.request system user,maintenance.model_maintenance_request,base.group_user,1,1,1,0
access_maintenance_wizard_system_user,maintenance.request.wizard system user,model_maintenance_request_wizard,base.group_user,1,1,1,0
access_maintenance_sf_group_equipment_user,equipment.request_sf_group_equipment_user,maintenance.model_maintenance_request,sf_group_equipment_user,1,1,1,0
access_maintenance_wizard_sf_group_equipment_user,maintenance_wizard_sf_group_equipment_user,model_maintenance_request_wizard,sf_group_equipment_user,1,1,1,0
access_maintenance_sf_group_equipment_manager,equipment.request_sf_group_equipment_manager,maintenance.model_maintenance_request,sf_group_equipment_manager,1,1,1,0
access_maintenance_wizard_sf_group_equipment_manager,maintenance_wizard_sf_group_equipment_manager,model_maintenance_request_wizard,sf_group_equipment_manager,1,1,1,0
access_maintenance_system_user,equipment.request system user,maintenance.model_maintenance_request,base.group_user,1,0,0,0
access_maintenance_equipment_group_plan_dispatch,maintenance.equipment,maintenance.model_maintenance_equipment,sf_base.group_plan_dispatch,1,0,0,0
access_maintenance_equipment_oee_group_plan_dispatch,maintenance_equipment_oee,model_maintenance_equipment_oee,sf_base.group_plan_dispatch,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
20 access_maintenance_system_user equipment.request system user maintenance.model_maintenance_request base.group_user 1 1 0 1 0 0
21 access_maintenance_wizard_system_user access_maintenance_equipment_group_plan_dispatch maintenance.request.wizard system user maintenance.equipment model_maintenance_request_wizard maintenance.model_maintenance_equipment base.group_user sf_base.group_plan_dispatch 1 1 0 1 0 0
22 access_maintenance_sf_group_equipment_user access_maintenance_equipment_oee_group_plan_dispatch equipment.request_sf_group_equipment_user maintenance_equipment_oee maintenance.model_maintenance_request model_maintenance_equipment_oee sf_group_equipment_user sf_base.group_plan_dispatch 1 1 0 1 0 0
23 access_maintenance_wizard_sf_group_equipment_user access_sf_maintenance_logs_group_plan_dispatch maintenance_wizard_sf_group_equipment_user sf_maintenance_logs model_maintenance_request_wizard model_sf_maintenance_logs sf_group_equipment_user sf_base.group_plan_dispatch 1 1 0 1 0 0
access_maintenance_sf_group_equipment_manager equipment.request_sf_group_equipment_manager maintenance.model_maintenance_request sf_group_equipment_manager 1 1 1 0
access_maintenance_wizard_sf_group_equipment_manager maintenance_wizard_sf_group_equipment_manager model_maintenance_request_wizard sf_group_equipment_manager 1 1 1 0
access_maintenance_equipment_group_plan_dispatch maintenance.equipment maintenance.model_maintenance_equipment sf_base.group_plan_dispatch 1 0 0 0
access_maintenance_equipment_oee_group_plan_dispatch maintenance_equipment_oee model_maintenance_equipment_oee sf_base.group_plan_dispatch 1 0 0 0
access_sf_maintenance_logs_group_plan_dispatch sf_maintenance_logs model_sf_maintenance_logs sf_base.group_plan_dispatch 1 0 0 0
24 access_maintenance_standard_image_group_plan_dispatch maintenance_standard_image model_maintenance_standard_image sf_base.group_plan_dispatch 1 0 0 0
25 access_equipment_maintenance_standards_group_plan_dispatch equipment_maintenance_standards model_equipment_maintenance_standards sf_base.group_plan_dispatch 1 0 0 0
26 access_maintenance_standards_group_plan_dispatch maintenance_standards model_maintenance_standards sf_base.group_plan_dispatch 1 0 0 0

View File

@@ -80,19 +80,13 @@
<field name="user_id" string="维保人"/>
</xpath>
<xpath expr="//field[@name='close_date']" position="replace">
<field name="close_date" attrs="{'invisible': [('done', '!=', True)]}" readonly="True"
string="维保日期"/>
</xpath>
<xpath expr="//field[@name='request_date']" position="attributes">
<attribute name="string">计划维保日期</attribute>
<field name="close_date" attrs="{'invisible': [('done', '!=', True)]}" readonly="True" string="维保日期"/>
</xpath>
<sheet>
<notebook>
<page string="维保标准" attrs="{'invisible': [('equipment_maintenance_id', '=', False)]}"
context="{'default_standard_id': 'id'}">
<page string="维保标准" attrs="{'invisible': [('equipment_maintenance_id', '=', False)]}" context="{'default_standard_id': 'id'}">
<field name="maintenance_standards" widget="one2many_list">
<tree multi_edit="" editable="">
<field name="name" class="table_custom_required"/>
@@ -104,33 +98,11 @@
</field>
</page>
</notebook>
</sheet>
</field>
</record>
<record id="maintenance_request_view_tree_sf" model="ir.ui.view">
<field name="name">maintenance.request.view.tree.sf</field>
<field name="model">maintenance.request</field>
<field name="inherit_id" ref="maintenance.hr_equipment_request_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='request_date']" position="replace">
<field name="request_date" string="计划维保日期"/>
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<field name="sf_maintenance_type"/>
</xpath>
</field>
</record>
<record id="equipment_request_view_search_sf" model="ir.ui.view">
<field name="name">maintenance.request.view.search.sf</field>
<field name="model">maintenance.request</field>
<field name="inherit_id" ref="maintenance.hr_equipment_request_view_search"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='inactive']" position="replace">
<filter string="不活跃的" name="inactive" domain="[('archive', '=', True)]"/>
<filter string="已归档" name="in_active" domain="[('active', '=', False)]"/>
</xpath>
</field>
</record>
@@ -138,8 +110,8 @@
<record id="hr_equipment_request_action1" model="ir.actions.act_window">
<field name="name">维保计划</field>
<field name="res_model">maintenance.request</field>
<field name="view_mode">tree,kanban,form,pivot,graph,calendar</field>
<field name="view_id" ref="sf_maintenance.maintenance_request_view_tree_sf"/>
<field name="view_mode">kanban,tree,form,pivot,graph,calendar</field>
<field name="view_id" ref="maintenance.hr_equipment_request_view_kanban"/>
<field name="context">{'default_user_id': uid}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">

View File

@@ -60,9 +60,9 @@
<field name="function_type"/>
<field name="code" readonly="1"/>
<field name="equipment_type" invisible="1"/>
<field name="brand_id" force_save="1"/>
<field name="brand_id" force_save="1" />
<field name="type_id" attrs="{'required': [('equipment_type', '=', '机床')]}"
domain="[('brand_id', '=', brand_id)]"/>
domain="[('brand_id', '=', brand_id)]" />
<field name="machine_tool_category" readonly="1" attrs="{'invisible': [('type_id', '=', False)]}"
force_save="1"/>
<field name="run_time" force_save="1"/>
@@ -73,7 +73,7 @@
<group>
<group string="基础参数">
<field name="control_system_id" attrs="{'required': [('equipment_type', '=', '机床')]}"
options="{'no_create': True}"/>
options="{'no_create': True}" />
<label for="workbench_L" string="工作台尺寸(mm)"/>
<div class="test_model">
<label for="workbench_L" string="长"/>
@@ -134,7 +134,7 @@
<!-- <field name="guide_rail" required="1"/>-->
<field name="number_of_axles" attrs="{'required': [('equipment_type', '=', '机床')]}"
widget="radio"
options="{'horizontal': true}"/>
options="{'horizontal': true}" />
<label for="x_axis" string="加工行程(mm)"
attrs="{'invisible': [('number_of_axles', '=', False)]}"/>
<div class="test_model"
@@ -197,7 +197,7 @@
<field name="C_tool_time"/>
</group>
<group string="主轴">
<field name="taper_type_id" attrs="{'required': [('equipment_type', '=', '机床')]}"/>
<field name="taper_type_id" attrs="{'required': [('equipment_type', '=', '机床')]}" />
<label for="distance_min" string="主轴端面-工作台距离(mm)"/>
<div class="test_model">
<label for="distance_min" string="最小(min)"/>
@@ -256,14 +256,14 @@
attrs="{'invisible': [('equipment_type', '!=', 'AGV小车')]}">
<field name="agv_logs">
<tree create="1" edit="1" delete="1" editable="bottom">
<field name='run_type'/>
<field name='run_code'/>
<field name='run_first'/>
<field name='run_last'/>
<field name='production_line'/>
<field name='workorder'/>
<field name='time'/>
<field name='state'/>
<field name = 'run_type'/>
<field name = 'run_code'/>
<field name = 'run_first'/>
<field name = 'run_last'/>
<field name = 'production_line'/>
<field name = 'workorder'/>
<field name = 'time'/>
<field name = 'state'/>
</tree>
</field>
</page>
@@ -979,32 +979,16 @@
</group>
</xpath>
<xpath expr="//field[@name='next_action_date']" position="before">
<xpath expr="//page[@name='maintenance']" position="replace">
<page string="维保" name="maintenance">
<group>
<group string="保养">
<field name='eq_maintenance_id' force_save="1" widget="many2one"/>
<field name="initial_action_date"/>
<field name="next_action_date" string="下次预防保养"/>
<label for="period" string="预防保养频次"/>
<div class="o_row">
<field name="period"/>
days
</div>
<label for="maintenance_duration" string="保养时长"/>
<div class="o_row">
<field name="maintenance_duration"/>
hours
</div>
<div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">
<button name="confirm_maintenance" string="确认保养" type="object"
class="oe_highlight" context="{'type': '保养'}"/>
</div>
</group>
<group string="检修">
<field name='overhaul_id'/>
<field name="initial_overhaul_date"/>
</xpath>
<xpath expr="//div[hasclass('o_row')][field[@name='maintenance_duration']]" position="after">
<field name='overhaul_id' options="{'no_create':True}"/>
<field name="overhaul_date" string="下次预防检修"/>
<label for="overhaul_period" string="预防检修频次"/>
<div class="o_row">
@@ -1017,20 +1001,9 @@
<field name="overhaul_duration"/>
hours
</div>
<field name='equipment_maintenance_standards_ids' widget="many2many_tags"
invisible="1"/>
<field name='equipment_maintenance_standards_ids' widget="many2many_tags" invisible="1"/>
<div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">
<button name="confirm_maintenance" string="确认检修" type="object"
class="oe_highlight" context="{'type': '检修'}"/>
</div>
</group>
</group>
</page>
</xpath>
<xpath expr="//page[@name='description']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
@@ -1216,7 +1189,7 @@
<field name="name" readonly="1"/>
<field name="type" readonly="1"/>
<field name="image" widget="image" readonly="1"/>
<!-- <field name="equipment_id"/>-->
<!-- <field name="equipment_id"/>-->
<field name="active" invisible="1"/>
</tree>
</field>

View File

@@ -1 +0,0 @@
from . import maintenance_request_wizard

View File

@@ -1,26 +0,0 @@
from odoo import fields, models
class MaintenanceRequestWizard(models.TransientModel):
_name = 'maintenance.request.wizard'
_description = '维保二次确认弹窗'
name = fields.Char('')
def submit(self):
context = self.env.context
equipment_id = self.env['maintenance.equipment'].sudo().search([('id', '=', context['equipment_id'])])
request_ids = self.env['maintenance.request'].search([('stage_id.done', '=', False),
('equipment_id', '=', equipment_id.id),
('maintenance_type', '=', 'preventive'),
('sf_maintenance_type', '=', context['type'])])
request_ids.write({'active': False})
return equipment_id.create_maintenance_request(context['type'])
def cancel(self):
context = self.env.context
equipment_id = self.env['maintenance.equipment'].sudo().search([('id', '=', context['equipment_id'])])
if context['type'] == '保养':
equipment_id.initial_action_date = equipment_id.initial_action_date_old
elif context['type'] == '检修':
equipment_id.initial_overhaul_date = equipment_id.initial_overhaul_date_old

View File

@@ -1,29 +0,0 @@
<openerp>
<data>
<record id="action_maintenance_request_wizard" model="ir.actions.act_window">
<field name="name">维保计划</field>
<field name="res_model">maintenance.request.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record model="ir.ui.view" id="maintenance_request_wizard_form_view">
<field name="name">maintenance.request.wizard.form.view</field>
<field name="model">maintenance.request.wizard</field>
<field name="arch" type="xml">
<form>
<div>
<field name="name" invisible="1"/>
有未执行的历史维保计划,是否创建新维保计划!!
</div>
<footer>
<button string="确认" name="submit" type="object" class="oe_highlight"/>
<button string="取消" name="cancel" type="object" class="oe_highlight"/>
</footer>
</form>
</field>
</record>
</data>
</openerp>

View File

@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': '机企猫智能工厂 制造管理',
'version': '1.1',
'version': '1.0',
'summary': '智能工厂制造模块',
'sequence': 1,
'description': """
@@ -10,9 +10,8 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse','jikimo_attachment_viewer', 'jikimo_sale_multiple_supply_methods'],
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse'],
'data': [
'data/cron_data.xml',
'data/stock_data.xml',
'data/empty_racks_data.xml',
'data/panel_data.xml',
@@ -22,9 +21,6 @@
'wizard/workpiece_delivery_views.xml',
'wizard/rework_wizard_views.xml',
'wizard/production_wizard_views.xml',
'wizard/production_technology_wizard_views.xml',
'wizard/production_technology_re_adjust_wizard_views.xml',
'wizard/mrp_workorder_batch_replan_wizard_views.xml',
'views/mrp_views_menus.xml',
'views/agv_scheduling_views.xml',
'views/stock_lot_views.xml',
@@ -33,14 +29,10 @@
'views/production_line_view.xml',
'views/mrp_workcenter_views.xml',
'views/mrp_workorder_view.xml',
'views/stock_picking_view.xml',
'views/model_type_view.xml',
'views/agv_setting_views.xml',
'views/sf_maintenance_equipment.xml',
'views/res_config_settings_views.xml',
'views/sale_order_views.xml',
'views/mrp_workorder_batch_replan.xml',
'views/purchase_order_view.xml',
],
'assets': {
@@ -53,10 +45,6 @@
'sf_manufacturing/static/src/scss/kanban_change.scss',
'sf_manufacturing/static/src/xml/button_show_on_tree.xml',
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
'sf_manufacturing/static/src/js/agv_scheduling_resend_confirm.js',
'sf_manufacturing/static/src/js/agv_scheduling_cancel_confirm.js',
'sf_manufacturing/static/src/js/qr.js',
'sf_manufacturing/static/src/xml/qr.xml',
]
},

View File

@@ -1,3 +1,2 @@
from . import controllers
from . import workpiece
from . import main

View File

@@ -1,54 +0,0 @@
import logging
import json
import traceback
from odoo import http
from odoo.http import request
from odoo.addons.sf_bf_connect.controllers.controllers import Sf_Bf_Connect
_logger = logging.getLogger(__name__)
class JikimoSaleRoutePicking(Sf_Bf_Connect):
@http.route('/api/bfm_process_order/list', type='http', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_bfm_process_order_list(self, **kw):
"""
接收业务平台加工订单分配工厂时传送来的订单数据并生成销售订单和产品及坯料
:param kw:
:return:
"""
res = {'status': 1, 'factory_order_no': ''}
# _logger.info('get_bfm_process_order_list:%s' % kw['order_number'])
try:
product_id = request.env.ref('jikimo_sale_multiple_supply_methods.product_template_default').with_context(active_test=False).sudo().product_variant_id
_logger.info('product_id:%s' % product_id)
company_id = request.env.ref('base.main_company').sudo()
bfm_process_order_list = json.loads(kw['bfm_process_order_list'])
order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create(
company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'],
kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], kw['order_number'], state='draft')
i = 1
# 给sale_order的default_code字段赋值
# aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])
# _logger.info('get_bfm_process_or===================================:%s' % order_id.name)
order_id.default_code = kw['order_number']
if kw.get('logistics_way'):
order_id.logistics_way = kw['logistics_way']
for item in bfm_process_order_list:
if item.get('embryo_redundancy_id'):
item['embryo_redundancy'] = request.env['sf.embryo.redundancy'].sudo().search([('code', '=', item['embryo_redundancy_id'])], limit=1)
item['embryo_redundancy_id'] = item['embryo_redundancy'].id
product = request.env['product.template'].sudo().product_create(product_id, item, order_id,
kw['order_number'], i)
product.product_tmpl_id.is_customer_provided = True if item['embryo_redundancy_id'] else False
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
i += 1
res['factory_order_no'] = order_id.name
except Exception as e:
traceback_error = traceback.format_exc()
logging.error('get_bfm_process_order_list error: %s' % traceback_error)
res['status'] = -1
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
request.cr.rollback()
return json.JSONEncoder().encode(res)

View File

@@ -1,29 +0,0 @@
<odoo>
<data noupdate="1">
<record model="ir.cron" id="ir_cron_update_construction_period_status">
<field name="name">工期状态变更</field>
<field name="model_id" ref="model_mrp_workorder"/>
<field name="state">code</field>
<field name="code">model._corn_update_construction_period_status()</field>
<field name="interval_number">12</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="user_id" ref="base.user_root"/>
<field name="active" eval="True"/>
</record>
<record model="ir.cron" id="ir_cron_update_delivery_status">
<field name="name">交期状态变更</field>
<field name="model_id" ref="model_mrp_production"/>
<field name="state">code</field>
<field name="code">model._corn_update_delivery_status()</field>
<field name="interval_number">12</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="user_id" ref="base.user_root"/>
<field name="active" eval="True"/>
</record>
</data>
</odoo>

View File

@@ -1,12 +0,0 @@
# migrations/1.1.0/post-migrate.py
from odoo import api, SUPERUSER_ID
def migrate(cr, version):
# 获取环境
env = api.Environment(cr, SUPERUSER_ID, {})
# 示例:添加新字段
env.ref('sf_dlm.product_embryo_sf_self_machining').product_tmpl_id.write({'categ_type': '坯料'})
env.ref('sf_dlm.product_template_sf').product_tmpl_id.write({'categ_type': '成品'})
env.ref('sf_dlm.product_embryo_sf_outsource').product_tmpl_id.write({'categ_type': '坯料'})
env.ref('sf_dlm.product_embryo_sf_purchase').product_tmpl_id.write({'categ_type': '坯料'})

View File

@@ -11,8 +11,3 @@ from . import production_line_base
from . import agv_setting
from . import agv_scheduling
from . import res_config_setting
from . import sf_technology_design
from . import sf_production_common
from . import sale_order
from . import quick_easy_order
from . import purchase_order

View File

@@ -1,7 +1,6 @@
import logging
import requests
from datetime import timedelta
from odoo import models, fields, api, _
from odoo.exceptions import UserError
@@ -211,18 +210,9 @@ class AgvScheduling(models.Model):
def button_cancel(self):
# 弹出二次确认窗口后执行
for rec in self:
if rec.state != '待下发':
raise UserError('只有待下发状态的AGV调度任务才能取消')
rec.state = '已取消'
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '任务取消成功!',
'type': 'success',
'sticky': False,
'next': {'type': 'ir.actions.act_window_close'},
}
}
def finish_scheduling(self):
"""
@@ -242,7 +232,7 @@ class AgvScheduling(models.Model):
agv_route sf.agv.task.route对象
"""
for rec in self:
if rec.state not in ['待下发', '配送中']:
if rec.state != '待下发':
return False
_logger.info('AGV任务调度下发调度任务路线为%s' % agv_task_route)
rec.state = '配送中'
@@ -275,44 +265,6 @@ class AgvScheduling(models.Model):
})
return super().write(vals)
def button_cancel_confirm(self):
if self.task_delivery_time > fields.Datetime.now() - timedelta(minutes=10):
return {
'type': 'ir.actions.client',
'tag': 'agv_scheduling_cancel_confirm',
'params': {
'agv_scheduling_id': self.id,
}
}
else:
return self.button_cancel()
def button_resend_confirm(self):
if self.task_delivery_time > fields.Datetime.now() - timedelta(minutes=10):
return {
'type': 'ir.actions.client',
'tag': 'agv_scheduling_resend_confirm',
'params': {
'agv_scheduling_id': self.id,
'context': self.env.context,
}
}
else:
return self.button_resend()
def button_resend(self):
self.dispatch_scheduling(self.agv_route_id)
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '任务重新下发成功!',
'type': 'success',
'sticky': False,
'next': {'type': 'ir.actions.act_window_close'},
}
}
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'

View File

@@ -6,29 +6,42 @@ class ModelType(models.Model):
_description = '模型类型'
name = fields.Char('名称')
# embryo_tolerance = fields.Char('坯料容余')
embryo_tolerance_id = fields.Many2one('sf.embryo.redundancy', string='坯料冗余')
embryo_tolerance = fields.Integer('坯料容余')
product_routing_tmpl_ids = fields.One2many('sf.product.model.type.routing.sort', 'product_model_type_id',
'成品工序模板(自动化产线加工')
'成品工序模板')
embryo_routing_tmpl_ids = fields.One2many('sf.embryo.model.type.routing.sort', 'embryo_model_type_id',
'坯料工序模板(人工线下加工)')
'坯料工序模板')
surface_technics_routing_tmpl_ids = fields.One2many('sf.surface_technics.model.type.routing.sort',
'surface_technics_model_type_id',
'表面工艺工序模板')
manual_product_routing_tmpl_ids = fields.One2many('sf.manual.product.model.type.routing.sort',
'manual_product_model_type_id',
'成品工序模板(人工线下加工)')
class ProductModelTypeRoutingSort(models.Model):
_name = 'sf.product.model.type.routing.sort'
_description = '成品工序排序(自动化产线加工)'
_description = '成品工序排序'
sequence = fields.Integer('Sequence')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter',
domain=[('routing_type', 'in', ['装夹预调', 'CNC加工', '解除装夹'])])
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
# routing_type = fields.Selection([
# ('获取CNC加工程序', '获取CNC加工程序'),
# ('装夹', '装夹'),
# ('前置三元定位检测', '前置三元定位检测'),
# ('CNC加工', 'CNC加工'),
# ('后置三元质量检测', '后置三元质量检测'),
# ('解除装夹', '解除装夹'), ('切割', '切割'), ('表面工艺', '表面工艺')
# ], string="工序类型", compute='_compute_route_workcenter_id')
#
# @api.depends('route_workcenter_id')
# def _compute_route_workcenter_id(self):
# for record in self:
# if record:
# record.routing_type = record.route_workcenter_id.routing_type
routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type')
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
product_model_type_id = fields.Many2one('sf.model.type')
@@ -44,7 +57,24 @@ class EmbryoModelTypeRoutingSort(models.Model):
sequence = fields.Integer('Sequence')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['切割'])])
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
# routing_type = fields.Selection([
# ('获取CNC加工程序', '获取CNC加工程序'),
# ('装夹', '装夹'),
# ('前置三元定位检测', '前置三元定位检测'),
# ('CNC加工', 'CNC加工'),
# ('后置三元质量检测', '后置三元质量检测'),
# ('解除装夹', '解除装夹'), ('切割', '切割'), ('表面工艺', '表面工艺')
# ], string="工序类型", compute='_compute_route_workcenter_id')
#
# @api.depends('route_workcenter_id')
# def _compute_route_workcenter_id(self):
# for record in self:
# if record:
# record.routing_type = record.route_workcenter_id.routing_type
routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type')
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
embryo_model_type_id = fields.Many2one('sf.model.type')
@@ -60,7 +90,24 @@ class SurfaceTechnicsModelTypeRoutingSort(models.Model):
sequence = fields.Integer('Sequence')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['表面工艺'])])
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
# routing_type = fields.Selection([
# ('获取CNC加工程序', '获取CNC加工程序'),
# ('装夹', '装夹'),
# ('前置三元定位检测', '前置三元定位检测'),
# ('CNC加工', 'CNC加工'),
# ('后置三元质量检测', '后置三元质量检测'),
# ('解除装夹', '解除装夹'), ('切割', '切割'), ('表面工艺', '表面工艺')
# ], string="工序类型", compute='_compute_route_workcenter_id')
#
# @api.depends('route_workcenter_id')
# def _compute_route_workcenter_id(self):
# for record in self:
# if record:
# record.routing_type = record.route_workcenter_id.routing_type
routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type')
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
surface_technics_model_type_id = fields.Many2one('sf.model.type')
@@ -69,18 +116,3 @@ class SurfaceTechnicsModelTypeRoutingSort(models.Model):
'route_model_type_uniq', 'unique (route_workcenter_id,surface_technics_model_type_id)',
'表面工艺工序不能重复!')
]
class ManualProductModelTypeRoutingSort(models.Model):
_name = 'sf.manual.product.model.type.routing.sort'
_description = '成品工序排序(人工线下加工)'
sequence = fields.Integer('Sequence')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['人工线下加工'])])
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type')
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
manual_product_model_type_id = fields.Many2one('sf.model.type')
_sql_constraints = [
('route_model_type_uniq', 'unique (route_workcenter_id,manual_product_model_type_id)', '成品工序不能重复!')
]

View File

@@ -8,7 +8,6 @@ import re
import requests
from itertools import groupby
from collections import defaultdict, namedtuple
from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common
@@ -19,7 +18,6 @@ class MrpProduction(models.Model):
_inherit = 'mrp.production'
_description = "制造订单"
_order = 'create_date desc'
sale_order_id = fields.Many2one('sale.order', string='销售订单', compute='_compute_sale_order_id', store=True)
deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery')
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
@@ -36,140 +34,15 @@ class MrpProduction(models.Model):
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_sale_order_id(self):
for production in self:
# 初始化 sale_order_id 为 False
sale_order_id = False
# 使用正则表达式查找产品名称中的 'S' 开头的字母数字字符串
match = re.search(r'S\d+', production.product_id.with_context(lang='zh_CN').name) # 从字符串开始匹配
if match:
result = match.group(0)
try:
# 查找与匹配的字符串相符的销售订单
sale_order = self.env['sale.order'].search(
[('name', '=', result)], limit=1, order='id asc'
)
if sale_order:
production.sale_order_id = sale_order.id
else:
logging.warning("No sale order found for production {} with product {} (name match: {})".format(
production.id, production.product_id.name, result))
except Exception as e:
logging.error("Error while fetching sale order for production {}: {}".format(production.id, str(e)))
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_deadline_of_delivery(self):
for production in self:
# 确保 procurement_group_id 和相关字段存在
if production.procurement_group_id:
# 获取相关的 sale_id
sale_order_id = production.procurement_group_id.mrp_production_ids.mapped(
'move_dest_ids.group_id.sale_id')
# 确保 sale_order_id 是有效的 ID 列表
if sale_order_id:
# 获取 sale.order 记录
sale_id = self.env['sale.order'].sudo().browse(sale_order_id.ids) # 使用 mapped 返回的 ID 列表
# 处理 sale_id
sale_order_ids = production.procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id.ids
if not sale_order_ids or len(sale_order_ids) < 1:
continue
sale_id = self.env['sale.order'].sudo().browse(sale_order_ids[0])
if sale_id:
# 假设我们只需要第一个 sale_id
production.deadline_of_delivery = sale_id[0].deadline_of_delivery if sale_id else False
else:
production.deadline_of_delivery = False
else:
production.deadline_of_delivery = False
def _compute_default_delivery_status(self):
try:
if self.state == 'cancel':
return False
if not self.deadline_of_delivery:
return False
hours = self.get_hours_diff()
if hours >= 48:
return '正常'
elif hours > 0 and hours < 48 and self.state != 'done':
return '预警'
elif hours > 0 and hours < 48 and self.state == 'done':
return '正常'
else:
return '已逾期'
except Exception as e:
logging.error("Error processing production ID {}: {}".format(self.id, e))
raise e
@api.depends('state', 'deadline_of_delivery')
def _compute_delivery_status(self):
for production in self:
delivery_status = production._compute_default_delivery_status()
if delivery_status and production.delivery_status != delivery_status:
production.delivery_status = delivery_status
delivery_status = fields.Selection([('正常', '正常'), ('预警', '预警'), ('已逾期', '已逾期')], string='交期状态',
store=True,
compute='_compute_delivery_status',
default=lambda self: self._compute_default_delivery_status())
def get_page_all_records(self, model_name, func, domain, page_size=100):
# 获取模型对象
model = self.env[model_name].sudo()
# 初始化分页参数
page_number = 1
while True:
# 计算偏移量
offset = (page_number - 1) * page_size
# 获取当前页的数据
records = model.search(domain, limit=page_size, offset=offset)
# 如果没有更多记录,退出循环
if not records:
break
# 将当前页的数据添加到结果列表
func(records)
# 增加页码
page_number += 1
def run_compute_delivery_status(self, records):
records._compute_delivery_status()
def _corn_update_delivery_status(self):
need_list = [
'draft',
'technology_to_confirmed',
'confirmed',
'pending_cam',
'progress',
'rework',
'scrap',
'to_close',
]
# previous_workorder = self.env['mrp.production'].search([('state', 'in', need_list)])
self.get_page_all_records('mrp.production', self.run_compute_delivery_status,
[('state', 'in', need_list)], 100)
def get_hours_diff(self):
# 获取当前日期和时间
current_datetime = fields.Datetime.now()
# 将 date_field 转换为 datetime 对象
if self.deadline_of_delivery:
date_obj = fields.Date.from_string(self.deadline_of_delivery)
# 将 date 对象转换为 datetime 对象,设置时间为 00:00:00
date_obj = datetime.datetime.combine(date_obj, datetime.time.min)
# 计算两个日期之间的差值
delta = date_obj - current_datetime
# 返回差值的小时数
return int(delta.total_seconds() / 3600)
else:
return 0.0
production.deadline_of_delivery = sale_id.deadline_of_delivery
@api.depends('workorder_ids.tool_state_remark')
def _compute_tool_state_remark(self):
@@ -212,7 +85,6 @@ class MrpProduction(models.Model):
# ])
state = fields.Selection([
('draft', '草稿'),
('technology_to_confirmed', '待工艺确认'),
('confirmed', '待排程'),
('pending_cam', '待加工'),
('progress', '加工中'),
@@ -256,14 +128,12 @@ class MrpProduction(models.Model):
], string='工序状态', default='待装夹')
# 零件图号
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
part_number = fields.Char('零件图号', readonly=True)
# 上传零件图纸
part_drawing = fields.Binary('零件图纸', related='product_id.machining_drawings', readonly=True)
part_drawing = fields.Binary('零件图纸', readonly=True)
quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True)
part_name = fields.Char(string='零件名称', related='product_id.part_name', readonly=True)
quality_standard = fields.Binary('质检标准', readonly=True)
@api.depends('product_id.manual_quotation')
def _compute_manual_quotation(self):
@@ -275,8 +145,6 @@ class MrpProduction(models.Model):
is_remanufacture = fields.Boolean('是否重新制造', default=False)
remanufacture_count = fields.Integer("重新制造订单数量", compute='_compute_remanufacture_production_ids')
remanufacture_production_id = fields.Many2one('mrp.production', string='')
technology_design_ids = fields.One2many('sf.technology.design', 'production_id', string='工艺设计')
is_adjust = fields.Boolean('是否退回调整', default=False)
@api.depends('remanufacture_production_id')
def _compute_remanufacture_production_ids(self):
@@ -303,7 +171,7 @@ class MrpProduction(models.Model):
@api.depends(
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state',
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state', 'programming_state', 'is_adjust')
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state')
def _compute_state(self):
for production in self:
if not production.state or not production.product_uom_id:
@@ -333,196 +201,43 @@ class MrpProduction(models.Model):
precision_rounding=move.product_uom.rounding or move.product_id.uom_id.rounding)
for move in production.move_raw_ids if move.product_id):
production.state = 'progress'
# 新添加的状态逻辑
if production.state in ['to_close', 'progress',
'technology_to_confirmed'] and production.schedule_state == '未排':
if not production.workorder_ids or production.is_adjust is True:
production.state = 'technology_to_confirmed'
else:
if production.is_adjust is True:
production.state = 'technology_to_confirmed'
else:
# # 新添加的状态逻辑
if (
production.state == 'to_close' or production.state == 'progress') and production.schedule_state == '未排':
production.state = 'confirmed'
elif production.state == 'pending_cam' and production.schedule_state == '未排':
production.state = 'confirmed'
elif production.state == 'to_close' and production.schedule_state == '已排':
production.state = 'pending_cam'
elif production.state == 'confirmed' and production.is_adjust is True:
production.state = 'technology_to_confirmed'
if production.state == 'confirmed' and production.schedule_state == '已排':
production.state = 'pending_cam'
if production.state == 'progress':
if all(wo_state not in ('progress', 'done', 'rework', 'scrap') for wo_state in
production.workorder_ids.mapped('state')):
production.state = 'pending_cam'
if production.is_rework is True:
production.state = 'rework'
if (production.state == 'rework' and production.tool_state == '0'
and production.schedule_state == '已排' and production.is_rework is False):
production.state = 'pending_cam'
# if production.state == 'pending_cam':
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
# production.state = 'done'
if any((wo.test_results == '返工' and wo.state == 'done' and production.programming_state in ['已编程'])
or (wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中', '已编程'])
for wo in production.workorder_ids):
if any(
(
wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
'已编程']) or (
wo.state == 'rework' and production.programming_state == '编程中') or (
wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
'已编程'])
for wo in
production.workorder_ids):
production.state = 'rework'
if any(wo.test_results == '报废' and wo.state == 'done' for wo in production.workorder_ids):
production.state = 'scrap'
if any(dr.test_results == '报废' and dr.handle_result == '已处理' for dr in
production.detection_result_ids):
production.state = 'cancel'
if production.workorder_ids and all(wo_state in ('done', 'rework', 'cancel') for wo_state in production.workorder_ids.mapped('state')):
if production.state not in ['scrap', 'rework', 'cancel']:
production.state = 'done'
# 退回调整
def technology_back_adjust(self):
process_parameters = []
domain = [('state', '=', 'confirmed'), ('origin', '=', self.origin)]
if self.production_type == '自动化产线加工':
cloud_programming = self._cron_get_programming_state()
if cloud_programming['send_state'] == 'sending':
raise UserError(_("编程文件正在下发中,请稍后重试"))
domain += [('programming_no', '=', self.programming_no)]
# 带排程的制造订单
production_confirmed = self.env['mrp.production'].search(domain)
for special in production_confirmed.technology_design_ids:
if special.process_parameters_id:
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
if not product_production_process:
if special.process_parameters_id not in process_parameters:
process_parameters.append(special.process_parameters_id.display_name)
if process_parameters:
raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters)))
if production_confirmed:
production_count = self.env['mrp.production'].search_count([
('origin', '=', self.origin),
('product_id', '=', self.product_id.id),
('state', '=', 'confirmed')
])
if production_count > 1:
return {
'name': _('退回调整'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_manufacturing.sf_production_technology_re_adjust_wizard_form_view').id,
'form')],
'res_model': 'sf.production.technology.re_adjust.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
else:
return {
'name': _('退回调整'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_manufacturing.sf_production_technology_re_adjust_wizard_confirm_form_view').id,
'form')],
'res_model': 'sf.production.technology.re_adjust.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
# 工艺确认
def technology_confirm(self):
process_parameters = []
account_moves = []
parameters_not = []
# 获取原有的工单对应的工序
origin_designs = self.workorder_ids.technology_design_id
# 获取已删除的工序
deleted_designs = origin_designs - self.technology_design_ids
if deleted_designs:
for deleted_design in deleted_designs:
workorder = self.env['mrp.workorder'].search([('technology_design_id', '=', deleted_design.id)])
purchase = workorder._get_surface_technics_purchase_ids()
account = self.env['account.move'].search([('id', 'in', purchase.invoice_ids.ids)])
if account.state not in ['cancel', False]:
if purchase.name not in account_moves:
account_moves.append(purchase.name)
special_design = self.technology_design_ids.filtered(
lambda a: a.routing_tag == 'special' and a.is_auto is False)
for special in special_design:
if special.route_id.routing_type == '表面工艺' and not special.process_parameters_id:
parameters_not.append(special.route_id.name)
if special.process_parameters_id:
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
if not product_production_process:
if special.process_parameters_id not in process_parameters:
process_parameters.append(special.process_parameters_id.display_name)
if account_moves:
raise UserError(_("请联系工厂生产经理对采购订单为%s生成的账单进行取消", ", ".join(account_moves)))
if parameters_not:
raise UserError(_("【工艺设计】-【工序】为%s未选择参数,请选择", ", ".join(parameters_not)))
if process_parameters:
raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters)))
# 判断同一个加工面的标准工序的顺序是否依次排序
error_panel = []
technology_design = self.technology_design_ids.filtered(lambda a: a.routing_tag == 'standard').sorted(
key=lambda m: m.sequence)
for index, design in enumerate(technology_design):
routing_type = design.route_id.routing_type
if index < len(technology_design) - 1:
next_index = index + 1
next_design = technology_design[next_index]
next_design_routing_type = next_design.route_id.routing_type
# logging.info('当前工序和加工面: %s-%s' % (design.route_id.name, design.panel))
# logging.info('下一个工序和加工面: %s-%s' % (next_design.route_id.name, next_design.panel))
if design.panel is not False:
if design.panel != next_design.panel:
if index == 0:
raise UserError('【加工面】为%s的标准工序里含有其他加工面的工序,请调整后重试' % design.panel)
if routing_type not in ['解除装夹']:
raise UserError('【加工面】为%s的标准工序顺序有误,请调整后重试' % design.panel)
if design.panel == next_design.panel:
if (routing_type == '装夹预调' and next_design_routing_type == '解除装夹') or (
routing_type == 'CNC加工' and next_design_routing_type == '装夹预调'):
if design.panel not in error_panel:
error_panel.append(design.panel)
else:
if not error_panel and not process_parameters:
production_count = self.env['mrp.production'].search_count([
('origin', '=', self.origin),
('product_id', '=', self.product_id.id),
('state', '=', 'technology_to_confirmed')
])
if production_count > 1:
return {
'name': _('工艺确认'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_manufacturing.sf_production_technology_wizard_form_view').id,
'form')],
'res_model': 'sf.production.technology.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
else:
return {
'name': _('工艺确认'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_manufacturing.sf_production_technology_wizard_confirm_form_view').id,
'form')],
'res_model': 'sf.production.technology.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
if error_panel:
raise UserError(_("【加工面】为%s的标准工序顺序有误,请调整后重试", ", ".join(error_panel)))
return True
# 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
if production.tool_state == '2':
production.state = 'rework'
def action_check(self):
"""
@@ -724,8 +439,8 @@ class MrpProduction(models.Model):
if self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_line_ids:
self.move_finished_ids.filtered(
lambda m: m.product_id == self.product_id).move_line_ids.lot_id = self.lot_producing_id
# if self.product_id.tracking == 'serial':
# self._set_qty_producing()
if self.product_id.tracking == 'serial':
self._set_qty_producing()
# 重载根据工序生成工单的程序如果产品BOM中没有工序时
# 根据产品对应的模板类型中工序,去生成工单;
@@ -761,89 +476,121 @@ class MrpProduction(models.Model):
'operation_id': operation.id,
'state': 'pending',
}]
if production.product_id.categ_id.type in ['成品', '坯料']:
# # 根据工序设计生成工单
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 ['表面工艺']:
if production.product_id.categ_id.type == '成品':
# # 根据加工面板的面数及对应的工序模板生成工单
i = 0
processing_panel_len = len(production.product_id.model_processing_panel.split(','))
for k in (production.product_id.model_processing_panel.split(',')):
product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
[('product_model_type_id', '=', production.product_id.product_model_type_id.id)],
order='sequence asc'
)
i += 1
for route in product_routing_workcenter:
if route.is_repeat is True:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(production, route))
else:
self.env['mrp.workorder'].json_workorder_str(k, production, route, item))
# if i == processing_panel_len and route.routing_type == '解除装夹':
# workorders_values.append(
# self.env['mrp.workorder'].json_workorder_str(k, production, route))
# 表面工艺工序
# 获取表面工艺id
# 工序id
surface_technics_arr = []
route_workcenter_arr = []
for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
if item.route_workcenter_id.surface_technics_id.id:
for process_param in production.product_id.model_process_parameters_ids:
logging.info('process_param:%s%s' % (process_param.id, process_param.name))
if item.route_workcenter_id.surface_technics_id == process_param.process_id:
logging.info(
'surface_technics_id:%s%s' % (item.route_workcenter_id.surface_technics_id.id,
item.route_workcenter_id.surface_technics_id.name))
surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id)
route_workcenter_arr.append(item.route_workcenter_id.id)
if surface_technics_arr:
production_process = self.env['sf.production.process'].search(
[('id', 'in', surface_technics_arr)],
order='sequence asc'
)
for p in production_process:
logging.info('production_process:%s' % p.name)
# if production_process:
process_parameter = production.product_id.model_process_parameters_ids.filtered(
lambda pm: pm.process_id.id == p.id)
if process_parameter:
# 产品为表面工艺服务的供应商
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', route.process_parameters_id.id)])
[('server_product_process_parameters_id', '=', process_parameter.id)])
if product_production_process:
route_production_process = self.env[
'mrp.routing.workcenter'].search(
[('surface_technics_id', '=', p.id),
('id', 'in', route_workcenter_arr)])
if route_production_process:
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(
production, route, product_production_process.seller_ids[0].partner_id.id))
production, route_production_process,
process_parameter,
product_production_process.seller_ids[0].partner_id.id))
elif production.product_id.categ_id.type == '坯料':
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
[('embryo_model_type_id', '=', production.product_id.embryo_model_type_id.id)],
order='sequence asc'
)
for route in embryo_routing_workcenter:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str('', production, route))
production.workorder_ids = workorders_values
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
# 外协出入库单处理
def get_subcontract_pick_purchase(self):
production_all = self.sorted(lambda x: x.id)
product_id_to_production_names = {}
grouped_product_ids = {k: list(g) for k, g in
groupby(production_all, key=lambda x: x.product_id.id)}
for product_id, pd in grouped_product_ids.items():
product_id_to_production_names[product_id] = [p.name for p in pd]
for production in production_all:
proc_workorders = []
# for production_item in productions:
process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
('is_subcontract', '=', True)])
if process_parameter_workorder:
# 将这些特殊表面工艺工单的采购单与调拨单置为失效
for workorder in process_parameter_workorder:
workorder._get_surface_technics_purchase_ids().write({'state': 'cancel'})
workorder.move_subcontract_workorder_ids.write({'state': 'cancel'})
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'cancel'})
is_pick = False
consecutive_workorders = []
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
# for i, workorder in enumerate(sorted_workorders):
# # 检查当前工作订单和下一个工作订单是否连续,并且供应商相同
# if i == 0:
# consecutive_workorders.append(workorder)
# elif workorder.sequence == sorted_workorders[
# i - 1].sequence + 1 and workorder.supplier_id.id == sorted_workorders[i - 1].supplier_id.id:
# consecutive_workorders.append(workorder)
# else:
# # 处理连续组,如果它不为空
# if consecutive_workorders:
# proc_workorders.append(consecutive_workorders)
# # 创建外协出入库单和采购订单
# # self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production, sorted_workorders)
# # self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
# # product_id_to_production_names)
# if i < len(sorted_workorders) - 1:
# # 重置连续组,并添加当前工作订单
# consecutive_workorders = [workorder]
# else:
# # 判断最后一笔:
# if workorder.sequence == sorted_workorders[
# i - 1].sequence and workorder.supplier_id.id == sorted_workorders[
# i - 1].supplier_id.id:
# consecutive_workorders = [workorder]
# else:
# proc_workorders.append([workorder])
# # 立即创建外协出入库单和采购订单
# # self.env['stock.picking'].create_outcontract_picking(workorder, production)
# # self.env['purchase.order'].get_purchase_order(workorder, production,
# # product_id_to_production_names)
# consecutive_workorders = []
#
# # 处理最后一个组,即使它可能只有一个工作订单
# if consecutive_workorders:
# proc_workorders.append(consecutive_workorders)
# self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
# self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
# product_id_to_production_names)
for workorders in reversed(sorted_workorders):
self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)
m = 0
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
for i in range(len(sorted_workorders) - 1):
if m == 0:
is_pick = False
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
if sorted_workorders[i] not in consecutive_workorders:
consecutive_workorders.append(sorted_workorders[i])
consecutive_workorders.append(sorted_workorders[i + 1])
m += 1
continue
else:
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
if is_pick is False:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
production)
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
if is_pick is False and m == 0:
if len(sorted_workorders) == 1:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production)
else:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production)
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
# 工单排序
def _reset_work_order_sequence1(self, k):
@@ -911,85 +658,7 @@ class MrpProduction(models.Model):
self._reset_work_order_sequence1(k)
return True
# 需对不连续工单对应的采购单和外协出入库单做处理
def _reset_subcontract_pick_purchase(self):
production_all = self.sorted(lambda x: x.id)
product_id_to_production_names = {}
grouped_product_ids = {k: list(g) for k, g in
groupby(production_all, key=lambda x: x.product_id.id)}
for product_id, pd in grouped_product_ids.items():
product_id_to_production_names[product_id] = [p.name for p in pd]
for item in production_all:
production_process = product_id_to_production_names.get(item.product_id.id)
workorder_sf = item.workorder_ids.filtered(lambda sf: sf.routing_type == '表面工艺')
for i, workorder in enumerate(workorder_sf):
if i == 0:
continue
elif workorder.sequence != workorder_sf[i - 1].sequence + 1:
# workorder.picking_ids.move_ids = False
workorder.picking_ids = False
purchase_order = self.env['purchase.order'].search(
[('state', '=', 'draft'), ('origin', '=', item.name),
('purchase_type', '=', 'consignment')])
server_template = self.env['product.template'].search(
[('server_product_process_parameters_id', '=',
workorder.surface_technics_parameters_id.id),
('detailed_type', '=', 'service')])
for po in purchase_order:
for line in po.order_line:
if line.product_id == server_template.product_variant_id:
continue
if server_template.server_product_process_parameters_id != line.product_id.server_product_process_parameters_id:
purchase_order_line = self.env['purchase.order.line'].search(
[('product_id', '=', server_template.product_variant_id.id), ('id', '=', line.id),
('product_qty', '=', 1)], limit=1, order='id desc')
if purchase_order_line:
line.unlink()
def _reset_work_order_sequence(self):
"""
工单工序排序方法(新)
"""
for rec in 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 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):
"""
工单工序排序方法(旧)
"""
for rec in self:
workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework'))
# 产品模型类型
@@ -1085,32 +754,52 @@ class MrpProduction(models.Model):
self._reset_work_order_sequence()
return True
def production_process(self, pro_plan):
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
# 最后一次加工结束时间
last_time = pro_plan.date_planned_start
# 预置时间
works = self.workorder_ids
for index, work in enumerate(works):
count = type_map.get(work.routing_type)
date_planned_end = None
date_planned_start = None
if self.production_type == '自动化产线加工':
date_planned_start, date_planned_end, last_time = work.auto_production_process(last_time, count,
type_map)
elif self.production_type == '':
date_planned_start, date_planned_end, last_time = work.manual_offline_process(last_time, index)
work.update_work_start_end(date_planned_start, date_planned_end)
# def
def process_range_time(self):
for production in self:
works = production.workorder_ids
pro_plan = self.env['sf.production.plan'].search([('production_id', '=', production.id)], limit=1)
if not pro_plan:
continue
if production.production_type:
production.production_process(pro_plan)
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
# 最后一次加工结束时间
last_time = pro_plan.date_planned_start
# 预置时间
for work in works:
count = type_map.get(work.routing_type)
date_planned_end = None
date_planned_start = None
duration_expected = datetime.timedelta(minutes=work.duration_expected)
reserve_time = datetime.timedelta(minutes=work.reserved_duration)
if not count:
# 第一轮加工
if work.routing_type == '装夹预调':
date_planned_end = last_time - reserve_time
date_planned_start = date_planned_end - duration_expected
elif work.routing_type == 'CNC加工':
date_planned_start = last_time
date_planned_end = last_time + duration_expected
last_time = date_planned_end
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
last_time = date_planned_end
type_map.update({work.routing_type: True})
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
last_time = date_planned_end
work.leave_id.write({
'date_from': date_planned_start,
'date_to': date_planned_end,
})
# work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end})
work.date_planned_start = date_planned_start
work.date_planned_finished = date_planned_end
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', work.routing_type)])
work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end,
'duration_expected': routing_workcenter.time_cycle})
# 修改标记已完成方法
def button_mark_done1(self):
@@ -1134,7 +823,6 @@ class MrpProduction(models.Model):
backorders = backorders - productions_to_backorder
productions_not_to_backorder._post_inventory(cancel_backorder=True)
# 查出最后一张工单完成入库操作
# if self.workorder_ids.filtered(lambda w: w.routing_type in ['表面工艺']):
# move_finish = self.env['stock.move'].search([('created_production_id', '=', self.id)])
# if move_finish:
@@ -1235,16 +923,6 @@ class MrpProduction(models.Model):
cloud_programming = None
if self.programming_state in ['已编程']:
cloud_programming = self._cron_get_programming_state()
result_ids = self.detection_result_ids.filtered(lambda dr: dr.handle_result == '待处理')
work_id_list = []
if result_ids:
work_id_list = [self.workorder_ids.filtered(
lambda wk: (wk.name == result_id.routing_type and wk.processing_panel == result_id.processing_panel
and wk.state == 'done')).id
for result_id in result_ids]
workorder_ids = self.workorder_ids.filtered(
lambda wk: wk.technology_design_id.routing_tag == 'standard' and wk.state not in ['rework', 'cancel'])
logging.info('标准工艺工单【%s' % workorder_ids)
return {
'name': _('返工'),
'type': 'ir.actions.act_window',
@@ -1253,8 +931,6 @@ class MrpProduction(models.Model):
'target': 'new',
'context': {
'default_production_id': self.id,
'default_workorder_ids': workorder_ids.ids if workorder_ids.ids != [] else self.workorder_ids.ids,
'default_hidden_workorder_ids': ','.join(map(str, work_id_list)) if work_id_list != [] else '',
'default_reprogramming_num': cloud_programming['reprogramming_num'],
'default_programming_state': cloud_programming['programming_state'],
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
@@ -1408,15 +1084,13 @@ class MrpProduction(models.Model):
raise_user_error=not self.env.context.get('from_orderpoint'))
productions = self.env['mrp.production'].sudo().search(
[('origin', '=', self.origin)], order='id desc', limit=1)
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True})
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc')
for mo in move:
domain = []
if mo.location_id.barcode == 'WH-POSTPRODUCTION' and mo.rule_id.picking_type_id.barcode == 'PC':
if mo.procure_method == 'make_to_order' and mo.name != productions.name:
if mo.name == '/':
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
elif mo.location_id.barcode == 'PL' and mo.rule_id.picking_type_id.barcode == 'INT':
elif mo.name == '':
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
if domain:
picking_type = self.env['stock.picking.type'].search(domain)
mo.write({'picking_type_id': picking_type.id})
mo._assign_picking()
@@ -1434,6 +1108,7 @@ class MrpProduction(models.Model):
mo_move.write({'reference': sfp_move.reference, 'partner_id': sfp_move.partner_id.id,
'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id,
'production_id': False})
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True})
# productions.procurement_group_id.mrp_production_ids.move_dest_ids.write(
# {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])})
stock_picking_remanufacture = self.env['stock.picking'].search([('origin', '=', productions.name)])
@@ -1467,6 +1142,7 @@ class MrpProduction(models.Model):
if purchase_orders.origin.find(productions.name) == -1:
purchase_orders.origin += ',' + productions.name
if item['is_reprogramming'] is False:
productions._create_workorder(item)
productions.programming_state = '已编程'
else:
productions.programming_state = '编程中'
@@ -1496,131 +1172,6 @@ class MrpProduction(models.Model):
'user_id': production.user_id.id}
return production_values_str
# 增加制造订单类型
production_type = fields.Selection(
[('自动化产线加工', '自动化产线加工'), ('人工线下加工', '人工线下加工')],
string='制造类型',
compute='_compute_production_type',
store=True
)
@api.depends('product_id.is_manual_processing')
def _compute_production_type(self):
for production in self:
production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工'
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_sale_order_count(self):
for production in self:
if production.sale_order_id:
production.sale_order_count = 1
else:
production.sale_order_count = 0
def action_view_sale_orders(self):
if self.sale_order_id:
action = {
'res_model': 'sale.order',
'type': 'ir.actions.act_window',
}
action.update({
'view_mode': 'form',
'res_id': self.sale_order_id.id,
})
return action
@api.model_create_multi
def create(self, vals_list):
"""
重载创建制造订单的方法,单个制造订单,同一成品只创建一个采购组,用于后续单据的创建
"""
product_group_id = {}
for vals in vals_list:
if not vals.get('name', False) or vals['name'] == _('New'):
picking_type_id = vals.get('picking_type_id')
if not picking_type_id:
picking_type_id = self._get_default_picking_type_id(vals.get('company_id', self.env.company.id))
vals['picking_type_id'] = picking_type_id
vals['name'] = self.env['stock.picking.type'].browse(picking_type_id).sequence_id.next_by_id()
if not vals.get('procurement_group_id'):
product_id = self.env['product.product'].browse(vals['product_id'])
if product_id.product_tmpl_id.single_manufacturing:
if product_id.id not in product_group_id.keys():
procurement_group_vals = self._prepare_procurement_group_vals(vals)
group_id = self.env["procurement.group"].create(procurement_group_vals).id
vals['procurement_group_id'] = group_id
product_group_id[product_id.id] = group_id
else:
vals['procurement_group_id'] = product_group_id[product_id.id]
return super(MrpProduction, self).create(vals_list)
@api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id',
'procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id')
def _compute_purchase_order_count(self):
for production in self:
# 找到来源的第一张制造订单的采购组
if production.product_id.product_tmpl_id.single_manufacturing == True:
first_production = self.env['mrp.production'].search(
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)], limit=1,
order='id asc')
production.purchase_order_count = len(
first_production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
first_production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id)
else:
production.purchase_order_count = len(
production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id)
@api.depends('procurement_group_id', 'procurement_group_id.stock_move_ids.group_id')
def _compute_picking_ids(self):
for order in self:
if order.product_id.product_tmpl_id.single_manufacturing == True:
first_order = self.env['mrp.production'].search(
[('origin', '=', order.origin), ('product_id', '=', order.product_id.id)], limit=1, order='id asc')
order.picking_ids = self.env['stock.picking'].search([
('group_id', '!=', False), ('state', '!=', 'cancel'),
'|', # 表示“或”的开始
('group_id', '=', order.procurement_group_id.id),
('related_group_ids', '=', order.procurement_group_id.id)
])
order.delivery_count = len(first_order.picking_ids)
else:
order.picking_ids = self.env['stock.picking'].search([
('group_id', '!=', False),('state', '!=', 'cancel'),
'|', # 表示“或”的开始
('group_id', '=', order.procurement_group_id.id),
('related_group_ids', '=', order.procurement_group_id.id)
])
order.delivery_count = len(order.picking_ids)
def action_view_purchase_orders(self):
self.ensure_one()
if self.is_remanufacture:
production = self
elif self.product_id.product_tmpl_id.single_manufacturing == True:
production = self.env['mrp.production'].search(
[('origin', '=', self.origin), ('product_id', '=', self.product_id.id)], limit=1, order='id asc')
else:
production = self
purchase_order_ids = (
production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id).ids
action = {
'res_model': 'purchase.order',
'type': 'ir.actions.act_window',
}
if len(purchase_order_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': purchase_order_ids[0],
})
else:
action.update({
'name': _("Purchase Order generated from %s", self.name),
'domain': [('id', 'in', purchase_order_ids)],
'view_mode': 'tree,form',
})
return action
class sf_detection_result(models.Model):
_name = 'sf.detection.result'
@@ -1650,6 +1201,10 @@ class sf_detection_result(models.Model):
'type': 'ir.actions.act_window',
'res_id': self.id,
'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')],
# 'view_mode': 'form',
# 'context': {
# 'default_id': self.id
# },
'target': 'new'
}

View File

@@ -7,25 +7,21 @@ class ResMrpRoutingWorkcenter(models.Model):
_inherit = 'mrp.routing.workcenter'
routing_type = fields.Selection([
# ('获取CNC加工程序', '获取CNC加工程序'),
('装夹预调', '装夹预调'),
# ('前置三元定位检测', '前置三元定位检测'),
('CNC加工', 'CNC加工'),
# ('后置三元质量检测', '后置三元质量检测'),
('解除装夹', '解除装夹'),
('切割', '切割'),
('表面工艺', '表面工艺'),
('线切割', '线切割'),
('人工线下加工', '人工线下加工')
('表面工艺', '表面工艺')
], string="工序类型")
routing_tag = fields.Selection([
('standard', '标准'),
('special', '特殊')
], string="标签")
is_repeat = fields.Boolean('重复', default=False)
workcenter_id = fields.Many2one('mrp.workcenter', required=False)
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
bom_id = fields.Many2one('mrp.bom', required=False)
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
def get_no(self):
international_standards = self.search(
[('code', '!=', ''), ('active', 'in', [True, False])],
@@ -82,22 +78,3 @@ class ResMrpRoutingWorkcenter(models.Model):
else:
workcenter_id = workcenter_ids[0]
return workcenter_id
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if self._context.get('production_id'):
route_ids = []
technology_design = self.env['sf.technology.design'].search(
[('production_id', '=', self._context.get('production_id'))])
for t in technology_design.filtered(lambda a: a.routing_tag == 'special'):
if not t.process_parameters_id:
route_ids.append(t.route_id.surface_technics_id.id)
domain = [('id', 'not in', route_ids), ('routing_tag', '=', 'special')]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
if self._context.get('is_duplicate') and self._context.get('model_name'):
# 查询出已经选择的工序
model_type = self.env[self._context.get('model_name')].search_read([],['route_workcenter_id'])
route_workcenter_ids = [item['route_workcenter_id'][0] if item['route_workcenter_id'] else False for item in model_type]
domain = args + [('id', 'not in', route_workcenter_ids)]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid)

View File

@@ -23,28 +23,28 @@ class ResMrpWorkOrder(models.Model):
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
product_tmpl_id_length = fields.Float(string='坯料长度(mm)', related='material_length', readonly=True, store=False)
product_tmpl_id_width = fields.Float(string='坯料度(mm)', related='material_width', readonly=True, store=False)
product_tmpl_id_height = fields.Float(string='坯料高度(mm)', related='material_height', readonly=True, store=False)
# product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True,
# string="坯料长度(mm)")
# product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True,
# string="坯料宽度(mm)")
# product_tmpl_id_height = fields.Float(related='production_id.product_tmpl_id.height', readonly=True, store=True,
# string="坯料高度(mm)")
product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True,
string="坯料度(mm)")
product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True,
string="坯料宽度(mm)")
product_tmpl_id_height = fields.Float(related='production_id.product_tmpl_id.height', readonly=True, store=True,
string="坯料高度(mm)")
product_tmpl_id_materials_id = fields.Many2one(related='production_id.product_tmpl_id.materials_id', readonly=True,
store=True, check_company=True, string="材料")
product_tmpl_id_materials_type_id = fields.Many2one(related='production_id.product_tmpl_id.materials_type_id',
readonly=True, store=True, check_company=True, string="型号")
# workcenter_id = fields.Many2one('mrp.workcenter', string='工作中心', required=False)
workcenter_id = fields.Many2one('mrp.workcenter', string='工作中心', required=False)
users_ids = fields.Many2many("res.users", 'users_workorder', related="workcenter_id.users_ids")
processing_panel = fields.Char('加工面')
sequence = fields.Integer(string='工序')
routing_type = fields.Selection([
# ('获取CNC加工程序', '获取CNC加工程序'),
('装夹预调', '装夹预调'),
# ('前置三元定位检测', '前置三元定位检测'),
('CNC加工', 'CNC加工'),
# ('后置三元质量检测', '后置三元质量检测'),
('解除装夹', '解除装夹'),
('切割', '切割'), ('表面工艺', '表面工艺'), ('线切割', '线切割'), ('人工线下加工', '人工线下加工')
('切割', '切割'), ('表面工艺', '表面工艺')
], string="工序类型")
results = fields.Char('结果')
state = fields.Selection([
@@ -59,8 +59,7 @@ class ResMrpWorkOrder(models.Model):
compute='_compute_state', store=True,
default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True)
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效',
tracking=True)
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效')
@api.depends('production_id.manual_quotation')
def _compute_manual_quotation(self):
@@ -137,11 +136,8 @@ class ResMrpWorkOrder(models.Model):
glb_file = fields.Binary("glb模型文件", related='production_id.model_file')
is_subcontract = fields.Boolean(string='是否外协')
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
picking_ids = fields.Many2many('stock.picking', string='外协出入库单',
compute='_compute_surface_technics_picking_ids')
purchase_id = fields.Many2many('purchase.order', string='外协采购单')
picking_ids = fields.Many2many('stock.picking', string='外协出入库单')
# purchase_id = fields.Many2one('purchase.order', string='外协采购单')
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
surface_technics_purchase_count = fields.Integer("外协采购", compute='_compute_surface_technics_purchase_ids')
@@ -149,106 +145,20 @@ class ResMrpWorkOrder(models.Model):
is_trayed = fields.Boolean(string='是否绑定托盘', default=False)
tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True)
technology_design_id = fields.Many2one('sf.technology.design')
def _compute_default_construction_period_status(self):
need_list = ['pending', 'waiting', 'ready', 'progress', 'to be detected', 'done']
try:
if self.state not in need_list:
return False
if not self.date_planned_finished:
return False
hours = self.get_hours_diff()
if hours >= 12:
return '正常'
elif hours > 0 and hours < 12 and self.state != 'done':
return '预警'
elif hours > 0 and hours < 12 and self.state == 'done':
return '正常'
else:
return '已逾期'
except Exception as e:
logging.error("Error processing production ID {}: {}".format(self.id, e))
raise e
@api.depends('state', 'date_planned_finished')
def _compute_construction_period_status(self):
for worker in self:
construction_period_status = worker._compute_default_construction_period_status()
if construction_period_status and worker.construction_period_status != construction_period_status:
worker.construction_period_status = construction_period_status
construction_period_status = fields.Selection([('正常', '正常'), ('预警', '预警'), ('已逾期', '已逾期')],
string='工期状态',
store=True,
compute='_compute_construction_period_status',
default=lambda
self: self._compute_default_construction_period_status())
def get_page_all_records(self, model_name, func, domain, page_size=100):
# 获取模型对象
model = self.env[model_name].sudo()
# 初始化分页参数
page_number = 1
while True:
# 计算偏移量
offset = (page_number - 1) * page_size
# 获取当前页的数据
records = model.search(domain, limit=page_size, offset=offset)
# 如果没有更多记录,退出循环
if not records:
break
# 将当前页的数据添加到结果列表
func(records)
# 增加页码
page_number += 1
def run_compute_construction_period_status(self, records):
records._compute_construction_period_status()
def _corn_update_construction_period_status(self):
need_list = ['pending', 'waiting', 'ready', 'progress', 'to be detected']
# need_list = [
# 'progress',
# 'to be detected']
self.get_page_all_records('mrp.workorder', self.run_compute_construction_period_status,
[('state', 'in', need_list)], 100)
def get_hours_diff(self):
# 获取当前日期和时间
current_datetime = fields.Datetime.now()
# 将 date_field 转换为 datetime 对象
if self.date_planned_finished:
date_obj = fields.Datetime.from_string(self.date_planned_finished)
# 将 date 对象转换为 datetime 对象,设置时间为 00:00:00
# date_obj = datetime.datetime.combine(date_obj, datetime.time.min)
# 计算两个日期之间的差值
delta = date_obj - current_datetime
# 返回差值的小时数
return int(delta.total_seconds() / 3600)
else:
return 0.0
@api.depends('name', 'production_id.name')
def _compute_surface_technics_picking_ids(self):
for workorder in self:
if workorder.routing_type == '表面工艺':
domain = [('origin', '=', workorder.production_id.name), ('state', 'not in', ['cancel']),
('partner_id', '=', workorder.supplier_id.id)]
domain = [('origin', '=', workorder.production_id.name)]
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:
# domain += [('surface_technics_parameters_id', '=', workorder.surface_technics_parameters_id.id)]
# else:
if previous_workorder:
process_product = self.env['product.template']._get_process_parameters_product(
previous_workorder.surface_technics_parameters_id)
domain += [('partner_id', '=', process_product.partner_id.id)]
else:
domain += [('surface_technics_parameters_id', '=', workorder.surface_technics_parameters_id.id)]
picking_ids = self.env['stock.picking'].search(domain, order='id asc')
workorder.surface_technics_picking_count = len(picking_ids)
@@ -273,73 +183,35 @@ class ResMrpWorkOrder(models.Model):
@api.depends('state', 'production_id.name')
def _compute_surface_technics_purchase_ids(self):
for order in self:
if order.routing_type == '表面工艺' and order.state not in ['cancel']:
# if order.production_id.production_type == '自动化产线加工':
# domain = [('programming_no', '=', order.production_id.programming_no)]
# else:buzhdiao
# domain = [('origin', '=', order.production_id.origin)]
# production_programming = self.env['mrp.production'].search(domain, order='name asc')
# production_list = [production.name for production in production_programming]
# production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
# technology_design = self.env['sf.technology.design'].search(
# [('process_parameters_id', '=', order.surface_technics_parameters_id.id),
# ('production_id', '=', order.production_id.id)])
# if technology_design.is_auto is False:
# domain = [('origin', '=', order.production_id.name)]
# else:
domain = [('purchase_type', '=', 'consignment'), ('origin', '=', order.production_id.name),
('state', '!=', 'cancel')]
purchase = self.env['purchase.order'].search(domain)
purchase_num = 0
if not purchase:
order.surface_technics_purchase_count = 0
for po in purchase:
for line in po.order_line:
if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id:
if line.product_qty == 1:
purchase_num += 1
order.surface_technics_purchase_count = purchase_num
if order.routing_type == '表面工艺':
production_programming = self.env['mrp.production'].search(
[('programming_no', '=', order.production_id.programming_no)], order='name asc')
production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
production_list = [production.name for production in production_programming]
purchase = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
for line in purchase.order_line:
if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id and line.product_qty == len(
production_no_remanufacture):
order.surface_technics_purchase_count = len(purchase)
else:
order.surface_technics_purchase_count = 0
def action_view_surface_technics_purchase(self):
self.ensure_one()
# if self.routing_type == '表面工艺':
# if self.production_id.production_type == '自动化产线加工':
# domain = [('programming_no', '=', self.production_id.programming_no)]
# else:
# domain = [('origin', '=', self.production_id.origin)]
# production_programming = self.env['mrp.production'].search(domain, order='name asc')
# production_list = [production.name for production in production_programming]
# production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
# technology_design = self.env['sf.technology.design'].search(
# [('process_parameters_id', '=', self.surface_technics_parameters_id.id),
# ('production_id', '=', self.production_id.id)])
# if technology_design.is_auto is False:
# domain = [('origin', '=', self.production_id.name)]
# else:
purchase_orders_id = self._get_surface_technics_purchase_ids()
production_programming = self.env['mrp.production'].search(
[('programming_no', '=', self.production_id.programming_no)], order='name asc')
production_list = [production.name for production in production_programming]
purchase_orders = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
result = {
"type": "ir.actions.act_window",
"res_model": "purchase.order",
"res_id": purchase_orders_id.id,
"res_id": purchase_orders.id,
# "domain": [['id', 'in', self.purchase_id]],
"name": _("Purchase Orders"),
'view_mode': 'form',
}
return result
def _get_surface_technics_purchase_ids(self):
domain = [('origin', '=', self.production_id.name), ('purchase_type', '=', 'consignment')]
purchase_orders = self.env['purchase.order'].search(domain)
purchase_orders_id = self.env['purchase.order']
for po in purchase_orders:
for line in po.order_line:
if line.product_id.server_product_process_parameters_id == self.surface_technics_parameters_id:
if line.product_qty == 1:
purchase_orders_id = line.order_id
return purchase_orders_id
supplier_id = fields.Many2one('res.partner', string='外协供应商')
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True)
# 保存名称
@@ -355,7 +227,6 @@ class ResMrpWorkOrder(models.Model):
part_number = fields.Char(related='production_id.part_number', string='零件图号')
machining_drawings = fields.Binary('2D加工图纸', related='production_id.part_drawing', readonly=True)
quality_standard = fields.Binary('质检标准', related='production_id.quality_standard', readonly=True)
part_name = fields.Char(related='production_id.part_name', string='零件名称')
# 工序状态
process_state = fields.Selection([
@@ -407,11 +278,8 @@ class ResMrpWorkOrder(models.Model):
保存名称
"""
for record in self:
if record.processing_panel:
tem_name = record.production_id.name.replace('/', '_')
record.save_name = tem_name + '_' + record.processing_panel
else:
record.save_name = ''
schedule_state = fields.Selection(related='production_id.schedule_state', store=True)
# 工件装夹信息
@@ -437,7 +305,6 @@ class ResMrpWorkOrder(models.Model):
is_delivery = fields.Boolean('是否配送完成', default=False)
rfid_code = fields.Char('RFID码')
rfid_code_old = fields.Char('RFID码(已解除)')
is_test_env = fields.Boolean('测试环境', default=False)
production_line_id = fields.Many2one('sf.production.line', related='production_id.production_line_id',
string='生产线', store=True, tracking=True)
@@ -453,13 +320,9 @@ class ResMrpWorkOrder(models.Model):
detailed_reason = fields.Text('详细原因')
is_rework = fields.Boolean(string='是否返工', default=False)
def button_change_env(self):
self.is_test_env = not self.is_test_env
@api.constrains('blocked_by_workorder_ids')
def _check_no_cyclic_dependencies(self):
if self.production_id.state not in ['rework', 'technology_to_confirmed', 'confirmed'] and self.state not in [
'rework']:
if self.production_id.state not in ['rework'] and self.state not in ['rework']:
if not self._check_m2m_recursion('blocked_by_workorder_ids'):
raise ValidationError(_("您不能创建周期性的依赖关系."))
@@ -470,7 +333,6 @@ class ResMrpWorkOrder(models.Model):
for workorder in self.blocked_by_workorder_ids:
if workorder.state in ['done', 'cancel', 'rework']:
continue
if workorder.production_id.state not in ['technology_to_confirmed', 'confirmed']:
workorder._plan_workorder(replan)
start_date = max(start_date, workorder.date_planned_finished)
# Plan only suitable workorders
@@ -802,14 +664,35 @@ class ResMrpWorkOrder(models.Model):
}}
# 拼接工单对象属性值
def json_workorder_str(self, production, route):
def json_workorder_str(self, k, production, route, item):
# 计算预计时长duration_expected
routing_types = ['切割', '装夹预调', 'CNC加工', '解除装夹']
if route.route_id.routing_type in routing_types:
if route.routing_type in routing_types:
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', route.routing_type if hasattr(route, 'routing_type') else route.route_id.routing_type)])
[('name', '=', route.routing_type)])
duration_expected = routing_workcenter.time_cycle
reserved_duration = routing_workcenter.reserved_duration
# if route.routing_type == '切割':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '切割')]).time_cycle
# # elif route.routing_type == '获取CNC加工程序':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '获取CNC加工程序')]).time_cycle
# elif route.routing_type == '装夹预调':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '装夹预调')]).time_cycle
# # elif route.routing_type == '前置三元定位检测':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '前置三元定位检测')]).time_cycle
# elif route.routing_type == 'CNC加工':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', 'CNC加工')]).time_cycle
# # elif route.routing_type == '后置三元质量检测':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '后置三元质量检测')]).time_cycle
# elif route.routing_type == '解除装夹':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '解除装夹')]).time_cycle
else:
duration_expected = 60
reserved_duration = 30
@@ -817,22 +700,26 @@ class ResMrpWorkOrder(models.Model):
'product_uom_id': production.product_uom_id.id,
'qty_producing': 0,
'operation_id': False,
'name': route.name if hasattr(route, 'routing_type') else route.route_id.name,
'processing_panel': False if hasattr(route, 'routing_type') else route.panel,
'sequence': route.sequence,
'quality_point_ids': False if hasattr(route, 'routing_type') else route.route_id.quality_point_ids,
'routing_type': route.routing_type if hasattr(route, 'routing_type') else route.route_id.routing_type,
'workcenter_id': False if hasattr(route, 'routing_type') else self.env[
'mrp.routing.workcenter'].get_workcenter(route.route_id.workcenter_ids.ids,
route.route_id.routing_type,
'name': route.route_workcenter_id.name,
'processing_panel': k,
'quality_point_ids': route.route_workcenter_id.quality_point_ids,
'routing_type': route.routing_type,
# 'work_state': '待发起',
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
# 设定初始化值避免出现变成bool问题
'date_planned_start': datetime.now(),
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': duration_expected,
'duration': 0,
'technology_design_id': route.id,
# 'tag_type': '重新加工' if item is False else False,
'tag_type': '重新加工' if item is False else False,
'cnc_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cnc.processing']._json_cnc_processing(
k, item),
'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k,
item),
# 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list(
# production)
'reserved_duration': reserved_duration,
}]
return workorders_values_str
@@ -863,24 +750,22 @@ class ResMrpWorkOrder(models.Model):
]
# 拼接工单对象属性值(表面工艺)
def _json_workorder_surface_process_str(self, production, route, supplier_id):
def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id):
workorders_values_str = [0, '', {
'product_uom_id': production.product_uom_id.id,
'qty_producing': 0,
'operation_id': False,
'name': route.process_parameters_id.display_name,
'name': '%s-%s' % (route.name, process_parameter.name),
'processing_panel': '',
'sequence': route.sequence,
'technology_design_id': route.id,
'routing_type': '表面工艺',
'surface_technics_parameters_id': route.process_parameters_id.id,
'surface_technics_parameters_id': process_parameter.id,
'work_state': '',
'supplier_id': supplier_id,
'is_subcontract': True if route.process_parameters_id.gain_way == '外协' else False,
'is_subcontract': True if process_parameter.gain_way == '外协' else False,
'workcenter_id': self.env[
'mrp.workcenter'].get_process_outsourcing_workcenter() if route.process_parameters_id.gain_way == '外协' else
self.env['mrp.routing.workcenter'].get_workcenter(route.route_id.workcenter_ids.ids,
route.route_id.routing_type,
'mrp.workcenter'].get_process_outsourcing_workcenter() if process_parameter.gain_way == '外协' else
self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
'date_planned_start': datetime.now(),
'date_planned_finished': datetime.now() + timedelta(days=1),
@@ -1032,221 +917,195 @@ class ResMrpWorkOrder(models.Model):
return workorders_values_str
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
'production_id.programming_state')
'production_id.tool_state')
def _compute_state(self):
# super()._compute_state()
super()._compute_state()
for workorder in self:
re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
cnc_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('routing_type', '=', 'CNC加工'), ('state', 'in', ['done', 'rework']),
('test_results', '=', '返工')])
cnc_workorder_pending = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('routing_type', '=', 'CNC加工'), ('state', 'in', ['pending'])])
unclamp_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('sequence', '=', workorder.sequence - 1),
('state', 'in', ['done'])])
if workorder.state not in ['cancel', 'progress', 'rework']:
if workorder.production_id.state == 'rework':
if workorder.routing_type == '装夹预调' and workorder.state not in ['done', 'rework',
'cancel']:
# # 有返工工单
# if re_work:
# 新工单
if workorder.is_rework is False:
if workorder.production_id.programming_state == '已编程' and workorder.production_id.is_rework is False:
if re_work or cnc_workorder:
workorder.state = 'ready'
else:
if workorder.production_id.is_rework is True:
if re_work or cnc_workorder:
workorder.state = 'waiting'
elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'rework', 'cancel']:
pre_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('routing_type', '=', '装夹预调'), ('state', '=', 'done')])
if pre_workorder:
if re_work:
workorder.state = 'waiting'
elif workorder.routing_type == '解除装夹' and workorder.state not in ['done', 'rework', 'cancel']:
if cnc_workorder:
if not cnc_workorder_pending or unclamp_workorder.test_results == '报废':
workorder.state = 'waiting'
# else:
# if workorder.production_id.is_rework is True:
# workorder.state = 'waiting'
elif workorder.production_id.state == 'progress':
if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已编程' and \
workorder.is_rework is False and workorder.state not in [
'done', 'rework',
'cancel']:
if workorder.production_id.is_rework is False:
if re_work or cnc_workorder or unclamp_workorder:
workorder.state = 'ready'
# if (re_work or cnc_workorder) and workorder.production_id.is_rework is False:
# workorder.state = 'ready'
if workorder.routing_type == '表面工艺' and workorder.state not in ['done', 'progress']:
if unclamp_workorder:
if workorder.is_subcontract is False:
workorder.state = 'ready'
else:
production_programming = self.env['mrp.production'].search(
[('programming_no', '=', self.production_id.programming_no)], order='name asc')
production_no_remanufacture = production_programming.filtered(
lambda a: a.is_remanufacture is False)
production_list = [production.name for production in production_programming]
purchase_orders = self.env['purchase.order'].search(
[('origin', 'ilike', ','.join(production_list))])
for line in purchase_orders.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id and line.product_qty == len(
production_no_remanufacture):
if purchase_orders.state == 'purchase':
workorder.state = 'ready'
else:
workorder.state = 'waiting'
elif workorder.production_id.state == 'scrap':
if workorder.routing_type == '解除装夹' and unclamp_workorder.test_results == '报废':
workorder.state = 'waiting'
if workorder.routing_type == '装夹预调' and workorder.state in ['waiting', 'ready', 'pending']:
workorder_ids = workorder.production_id.workorder_ids
work_bo = True
for wo in workorder_ids.filtered(lambda a: a.routing_type == '装夹预调' and a.state == 'rework'):
if not workorder_ids.filtered(
lambda a: (a.routing_type == '装夹预调' and a.state not in ['rework', 'cancel']
and a.processing_panel == wo.processing_panel)):
work_bo = False
break
if (workorder.production_id.programming_state == '已编程' and work_bo
and not workorder_ids.filtered(lambda a: a.sequence == 0)):
# 当工单对应制造订单的功能刀具状态为 【无效刀】时,先对的第一个装夹预调工单状态设置为 【等待组件】
if workorder.production_id.tool_state in ['1', '2']:
if workorder.state in ['ready']:
workorder.state = 'waiting'
continue
elif workorder.state in ['waiting']:
continue
elif workorder.state == 'pending' and workorder == self.search(
[('production_id', '=', workorder.production_id.id),
('routing_type', '=', '装夹预调'),
('state', 'not in', ['rework', 'done', 'cancel'])],
limit=1,
order="sequence"):
workorder.state = 'waiting'
continue
elif workorder.production_id.tool_state in ['0']:
if workorder_ids.filtered(lambda a: a.state == 'rework'):
if not workorder_ids.filtered(
lambda a: (a.routing_type not in ['装夹预调'] and
a.state not in ['pending', 'done', 'rework', 'cancel'])):
# 查询工序最小的非完工、非返工的装夹预调工单
work_id = self.search(
[('production_id', '=', workorder.production_id.id),
('state', 'not in', ['rework', 'done', 'cancel'])],
limit=1,
order="sequence")
if work_id.routing_type == '装夹预调':
if workorder == work_id:
if workorder.production_id.reservation_state == 'assigned':
workorder.state = 'ready'
elif workorder.production_id.reservation_state != 'assigned':
workorder.state = 'waiting'
continue
elif (workorder.name == '装夹预调' and
workorder.state not in ['rework', 'done', 'cancel']):
if workorder.state != 'pending':
workorder.state = 'pending'
if workorder.production_id.tool_state in ['1', '2'] and workorder.state == 'ready':
workorder.state = 'waiting'
continue
# elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'cancel', 'progress',
# 'rework']:
# per_work = self.env['mrp.workorder'].search(
# [('routing_type', '=', '装夹预调'), ('production_id', '=', workorder.production_id.id),
# ('processing_panel', '=', workorder.processing_panel), ('is_rework', '=', True)])
# if per_work:
# workorder.state = 'waiting'
# if workorder.routing_type == 'CNC加工' and workorder.state == 'progress':
# workorder.state = 'to be detected'
# for workorder in self:
# if workorder.sequence != 1:
# previous_workorder = self.env['mrp.workorder'].search(
# [('production_id', '=', workorder.production_id.id),
# ('sequence', '=', workorder.sequence - 1)])
# if workorder.is_rework is True and workorder.state == 'done':
# cnc_work = self.env['mrp.workorder'].search([('routing_type','=','CNC加工'),('production_id','=',workorder.production_id.id)])
# if cnc_work:
# cnc_work.state = 'waiting'
# if workorder.state == 'pending':
# if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
# if workorder.production_id.reservation_state == 'assigned' and workorder.production_id.schedule_state == '已排':
# if ((workorder.sequence == 1 and not workorder.blocked_by_workorder_ids)
# or (workorder.blocked_by_workorder_ids.state in ('done', 'cancel')
# and workorder.blocked_by_workorder_ids.test_results not in ['报废', '返工'])
# or (previous_workorder.state in ('done', 'cancel')
# and not workorder.blocked_by_workorder_ids
# and previous_workorder.test_results not in ['报废', '返工'])
# ):
# workorder.state = 'ready'
# continue
# if workorder.production_id.schedule_state == '未排' and workorder.state in ('waiting', 'ready'):
# if workorder.sequence != 1:
# workorder.state = 'pending'
# workorder.state = 'ready' if workorder.production_id.reservation_state == 'assigned' else 'waiting'
# continue
# if workorder.state not in ('waiting', 'ready'):
# continue
# if workorder.state in (
# 'waiting') and workorder.sequence == 1 and workorder.production_id.schedule_state == '已排':
# workorder.state = 'ready'
# continue
# if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
# workorder.state = 'pending'
# if workorder.state in ['waiting']:
# if previous_workorder.state == 'waiting':
# workorder.state = 'pending'
# if workorder.sequence == 1 and workorder.state == 'pending':
# workorder.state = 'waiting'
# continue
# if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'):
# continue
# if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting' and workorder.production_id.schedule_state == '已排':
# if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting':
# workorder.state = 'ready'
# elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready':
# workorder.state = 'waiting'
for workorder in self:
# 如果工单的工序没有进行排序则跳出循环
if workorder.production_id.workorder_ids.filtered(lambda wk: wk.sequence == 0):
continue
# ===== 对所有按序号排序的非[进行中、完成、返工、取消]状态的工单,除了第一条之外的工单状态都设置为[等待其他工单] =====
# logging.info(workorder.state)
work_ids = workorder.production_id.workorder_ids.filtered(
lambda wk: wk.state not in ['done', 'rework', 'cancel'])
if not work_ids:
continue
min_sequence_wk = min(work_ids, key=lambda wk: wk.sequence)
if workorder.state in ['done', 'rework', 'cancel', 'progress', 'to be detected']:
continue
else:
if workorder != min_sequence_wk:
if workorder.state != 'pending':
workorder.state = 'pending'
continue
# ================= 如果制造订单制造类型为【人工线下加工】==========================
if (workorder.production_id.production_type == '人工线下加工'
and workorder.production_id.schedule_state == '已排'
and len(workorder.production_id.picking_ids.filtered(
lambda w: w.state not in ['done', 'cancel'])) == 0):
if workorder.is_subcontract is True:
purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id.state == 'purchase':
workorder.state = 'ready'
continue
else:
workorder.state = 'waiting'
continue
else:
workorder.state = 'ready'
continue
# ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]==========================
if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework'
or workorder.production_id.schedule_state != '已排'
or workorder.production_id.reservation_state not in ['assigned']):
if workorder.state != 'waiting':
workorder.state = 'waiting'
continue
if workorder.production_id.programming_state == '已编程':
workorder.state = 'ready'
elif workorder.state != 'waiting':
workorder.state = 'waiting'
# =========== 特殊工艺工单处理 ===================
# if workorder.routing_type == '表面工艺' and workorder.is_subcontrac:
# purchase_order = self.env['purchase.order'].search(
# [('origin', 'ilike', workorder.production_id.name)])
# if purchase_order.picking_ids.filtered(lambda p: p.state in ['waiting', 'confirmed', 'assigned']):
# workorder.state = 'waiting'
# continue
if workorder.technology_design_id.routing_tag == 'special':
if workorder.is_subcontract is False:
workorder.state = 'ready'
else:
# production_programming = self.env['mrp.production'].search(
# [('origin', '=', self.production_id.origin)], order='name asc')
# production_no_remanufacture = production_programming.filtered(
# lambda a: a.is_remanufacture is False)
# production_list = [production.name for production in production_programming]
# purchase_orders = self.env['purchase.order'].search(
# [('origin', 'ilike', ','.join(production_list))])
# for line in purchase_orders.order_line:
# if (
# line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id
# and line.product_qty == len(production_no_remanufacture)):
# if all(pur_order.state == 'purchase' for pur_order in purchase_orders):
# workorder.state = 'ready'
# else:
# workorder.state = 'waiting'
purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id:
workorder.state = 'ready' if purchase_orders_id.state == 'purchase' else 'waiting'
else:
workorder.state = 'waiting'
# re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
# ('processing_panel', '=', workorder.processing_panel),
# ('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
# cnc_workorder = self.env['mrp.workorder'].search(
# [('production_id', '=', workorder.production_id.id),
# ('processing_panel', '=', workorder.processing_panel),
# ('routing_type', '=', 'CNC加工'), ('state', 'in', ['done', 'rework']),
# ('test_results', '=', '返工')])
# cnc_workorder_pending = self.env['mrp.workorder'].search(
# [('production_id', '=', workorder.production_id.id),
# ('processing_panel', '=', workorder.processing_panel),
# ('routing_type', '=', 'CNC加工'), ('state', 'in', ['pending'])])
# unclamp_workorder = self.env['mrp.workorder'].search(
# [('production_id', '=', workorder.production_id.id),
# ('sequence', '=', workorder.sequence - 1),
# ('state', 'in', ['done'])])
# if workorder.state not in ['cancel', 'progress', 'rework']:
# if workorder.production_id.state == 'rework':
# if workorder.routing_type == '装夹预调':
# # # 有返工工单
# # if re_work:
# # 新工单
# if workorder.is_rework is False:
# if (workorder.production_id.programming_state == '已编程'
# and workorder.production_id.is_rework is False):
# if re_work or cnc_workorder:
# workorder.state = 'ready'
# else:
# if workorder.production_id.is_rework is True:
# if re_work or cnc_workorder:
# workorder.state = 'waiting'
#
# elif workorder.routing_type == 'CNC加工':
# pre_workorder = self.env['mrp.workorder'].search(
# [('production_id', '=', workorder.production_id.id),
# ('processing_panel', '=', workorder.processing_panel),
# ('routing_type', '=', '装夹预调'), ('state', '=', 'done')])
# if pre_workorder:
# if re_work:
# workorder.state = 'waiting'
# elif workorder.routing_type == '解除装夹':
# if cnc_workorder:
# if not cnc_workorder_pending or unclamp_workorder.test_results == '报废':
# workorder.state = 'waiting'
# # else:
# # if workorder.production_id.is_rework is True:
# # workorder.state = 'waiting'
# elif workorder.production_id.state == 'progress':
# if (workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已编程'
# and workorder.is_rework is False and workorder.state not in ['done', 'rework', 'cancel']):
# if workorder.production_id.is_rework is False:
# if re_work or cnc_workorder or unclamp_workorder:
# workorder.state = 'ready'
# # if (re_work or cnc_workorder) and workorder.production_id.is_rework is False:
# # workorder.state = 'ready'
# elif workorder.production_id.state == 'scrap':
# if workorder.routing_type == '解除装夹' and unclamp_workorder.test_results == '报废':
# workorder.state = 'waiting'
# 重写工单开始按钮方法
def button_start(self):
# 判断工单状态是否为等待组件
if self.state in ['waiting', 'pending']:
raise UserError('制造订单【%s】缺少组件信息!' % self.production_id.name)
if self.routing_type == 'CNC加工':
self.env['sf.production.plan'].sudo().search([('name', '=', self.production_id.name)]).write({
'state': 'processing',
'actual_start_time': datetime.now()
})
if self.sequence == 1:
if self.routing_type == '装夹预调':
# 判断是否有坯料的序列号信息
boolean = False
if self.production_id.move_raw_ids:
if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料':
if self.production_id.move_raw_ids[0].move_line_ids:
if self.production_id.move_raw_ids[0].move_line_ids:
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
boolean = True
else:
boolean = True
if not boolean:
raise UserError('制造订单【%s】缺少组件的序列号信息!' % self.production_id.name)
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name
# cnc校验
if self.production_id.production_type == '自动化产线加工':
cnc_workorder = self.search(
[('production_id', '=', self.production_id.id), ('routing_type', '=', 'CNC加工')],
limit=1, order='id asc')
# if not cnc_workorder.cnc_ids:
# raise UserError(_('该制造订单还未下发CNC程序请稍后再试'))
if not cnc_workorder.cnc_ids:
raise UserError(_('该制造订单还未下发CNC程序请稍后再试'))
else:
if self.production_id.tool_state in ['1', '2']:
if self.production_id.tool_state == '1':
@@ -1263,33 +1122,15 @@ class ResMrpWorkOrder(models.Model):
# 表面工艺外协出库单
if self.routing_type == '表面工艺':
if self.is_subcontract is True:
move_out = self.move_subcontract_workorder_ids[1]
# move_out = self.env['stock.move'].search(
# [('location_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
# ('location_dest_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'VL-SPOC')]).id),
# ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])])
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(self.production_id, self))
# product_qty = mo.product_uom._compute_quantity(
# mo.product_uom_qty, mo.product_id.uom_id, rounding_method='HALF-UP')
# available_quantity = self.env['stock.quant']._get_available_quantity(
# mo.product_id,
# mo.location_id,
# lot_id=mo.move_line_ids.lot_id,
# strict=False,
# )
# mo._update_reserved_quantity(
# product_qty,
# available_quantity,
# mo.location_id,
# lot_id=mo.move_line_ids.lot_id,
# strict=False,
# )
move_out = self.env['stock.move'].search(
[('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.production_id.name)])
if move_out.state != 'done':
move_out.write({'state': 'assigned', 'production_id': False})
self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self))
# move_out._action_assign()
if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress':
@@ -1390,17 +1231,23 @@ class ResMrpWorkOrder(models.Model):
'''
record.date_finished = datetime.now()
if record.routing_type == '表面工艺':
logging.info('record.picking_ids:%s' % record.picking_ids)
logging.info('record.picking_out:%s' % record.picking_ids[0])
if record.picking_ids:
picks = record.picking_ids.filtered(lambda p: p.state not in ('done'))
if picks:
raise UserError('请先完成该工单的工艺外协再进行操作')
# 表面工艺外协,最后一张工单
workorders = self.production_id.workorder_ids
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True).sorted('sequence')
if self == subcontract_workorders[-1]:
# 给下一个库存移动就绪
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
# self.production_id.button_mark_done()
for pick_item in record.picking_ids:
if pick_item.state not in ['done']:
raise UserError(
'请先完成该工单的工艺外协再进行操作')
picking_out = record.env['stock.move.line'].search(
[('picking_id', '=', record.picking_ids[0].id)])
logging.info('picking_out:%s' % picking_out.picking_id.name)
# if picking_out:
# order_line_ids = []
# logging.info('surface_technics_parameters_id:%s' % record.surface_technics_parameters_id.name)
#
# else:
# raise UserError(
# '请先在产品中配置表面工艺为%s相关的外协服务产品' % item.surface_technics_parameters_id.name)
tem_date_planned_finished = record.date_planned_finished
tem_date_finished = record.date_finished
logging.info('routing_type:%s' % record.routing_type)
@@ -1419,38 +1266,36 @@ class ResMrpWorkOrder(models.Model):
# record.recreateManufacturingOrWorkerOrder()
is_production_id = False
rework_workorder = record.production_id.workorder_ids.filtered(lambda p: p.state == 'rework')
done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state in ['done'])
done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state == 'done')
if (len(rework_workorder) + len(done_workorder) == len(record.production_id.workorder_ids)) or (
len(done_workorder) == len(record.production_id.workorder_ids)):
is_production_id = True
if record.routing_type in ['解除装夹'] or (
record.is_rework is True and record.routing_type in ['装夹预调']):
record.is_rework is True and record.routing_type in ['装夹预调']) or (
record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']):
for workorder in record.production_id.workorder_ids:
if workorder.processing_panel == record.processing_panel:
rfid_code = workorder.rfid_code
workorder.write({'rfid_code_old': rfid_code,
'rfid_code': False})
self.env['stock.lot'].sudo().search([('rfid', '=', rfid_code)]).write(
{'tool_material_status': '可用'})
if workorder.rfid_code:
raise ValidationError(f'{workorder.name}】工单解绑失败,请重新点击完成按钮!!!')
# workorder.rfid_code_old = rfid_code
# workorder.rfid_code = False
logging.info('workorder.rfid_code:%s' % workorder.rfid_code)
# if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺', '切割']:
if is_production_id is True:
if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']:
logging.info('product_qty:%s' % record.production_id.product_qty)
for move_raw_id in record.production_id.move_raw_ids:
move_raw_id.quantity_done = move_raw_id.product_uom_qty
record.process_state = '已完工'
record.production_id.process_state = '已完工'
# if record.routing_type in ['表面工艺']:
# raw_move = self.env['stock.move'].sudo().search(
# [('origin', '=', record.production_id.name),
# ('procure_method', 'in', ['make_to_order', 'make_to_stock']),
# ('state', '!=', 'done')])
# if raw_move:
# raw_move.write({'state': 'done'})
if record.routing_type in ['表面工艺']:
raw_move = self.env['stock.move'].sudo().search(
[('origin', '=', record.production_id.name),
('procure_method', 'in', ['make_to_order', 'make_to_stock']),
('state', '!=', 'done')])
if raw_move:
raw_move.write({'state': 'done'})
record.production_id.button_mark_done1()
# record.production_id.state = 'done'
@@ -1571,8 +1416,6 @@ class ResMrpWorkOrder(models.Model):
# 'default_feeder_station_start_id': feeder_station_start_id,
}}
move_subcontract_workorder_ids = fields.One2many('stock.move', 'subcontract_workorder_id', string='组件')
class CNCprocessing(models.Model):
_name = 'sf.cnc.processing'
@@ -1781,7 +1624,6 @@ class SfWorkOrderBarcodes(models.Model):
if workorder_rfid:
for item in workorder_rfid:
item.write({'rfid_code': barcode})
lot.sudo().write({'tool_material_status': '在用'})
logging.info("Rfid[%s]绑定成功!!!" % barcode)
else:
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
@@ -2096,56 +1938,3 @@ class CMMprogram(models.Model):
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
}))
return cmm_program
def update_work_start_end(self, date_planned_start, date_planned_end):
self.leave_id.write({
'date_from': date_planned_start,
'date_to': date_planned_end,
})
self.date_planned_finished = datetime.datetime.today() + datetime.timedelta(days=100)
self.date_planned_start = date_planned_start
self.date_planned_finished = date_planned_end
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', self.routing_type)])
self.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end,
'duration_expected': routing_workcenter.time_cycle})
def auto_production_process(self, last_time, is_first, type_map):
date_planned_end = None
date_planned_start = None
duration_expected = datetime.timedelta(minutes=self.duration_expected)
reserve_time = datetime.timedelta(minutes=self.reserved_duration)
if is_first:
# 第一轮加工
if self.routing_type == '装夹预调':
date_planned_end = last_time - reserve_time
date_planned_start = date_planned_end - duration_expected
elif self.routing_type == 'CNC加工':
date_planned_start = last_time
date_planned_end = last_time + duration_expected
last_time = date_planned_end
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
last_time = date_planned_end
type_map.update({self.routing_type: True})
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
last_time = date_planned_end
return date_planned_start, date_planned_end, last_time
def manual_offline_process(self, last_time, is_first):
date_planned_end = None
date_planned_start = None
duration_expected = datetime.timedelta(minutes=self.duration_expected)
reserve_time = datetime.timedelta(minutes=self.reserved_duration)
if is_first:
date_planned_start = last_time
date_planned_end = last_time + duration_expected
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
return date_planned_start, date_planned_end, last_time

View File

@@ -16,12 +16,6 @@ from OCC.Extend.DataExchange import write_stl_file
class ResProductMo(models.Model):
_inherit = 'product.template'
def _get_machining_precision(self):
machinings = self.env['sf.machining.accuracy'].sudo().search([])
list = [(m.sync_id, m.name) for m in machinings]
return list
model_file = fields.Binary('模型文件')
categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True)
model_name = fields.Char('模型名称')
@@ -29,7 +23,12 @@ class ResProductMo(models.Model):
model_width = fields.Float('模型宽(mm)', digits=(16, 3))
model_height = fields.Float('模型高(mm)', digits=(16, 3))
model_volume = fields.Float('模型体积(m³)')
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度')
model_machining_precision = fields.Selection([
('0.10', '±0.10mm'),
('0.05', '±0.05mm'),
('0.03', '±0.03mm'),
('0.02', '±0.02mm'),
('0.01', '±0.01mm')], string='加工精度')
model_processing_panel = fields.Char('模型加工面板')
model_remark = fields.Char('模型备注说明')
length = fields.Float('长(mm)', digits=(16, 3))
@@ -141,11 +140,11 @@ class ResProductMo(models.Model):
cutting_tool_coarse_medium_fine = fields.Selection(related='cutting_tool_model_id.integral_coarse_medium_fine', string='粗/中/精')
# cutting_tool_model_id.integral_coarse_medium_fine
# cutting_tool_run_out_accuracy_max = fields.Float('端跳精度max', digits=(6, 1))
cutting_tool_run_out_accuracy_max = fields.Char(related='cutting_tool_model_id.integral_run_out_accuracy_max', string='端跳精度max')
cutting_tool_run_out_accuracy_max = fields.Char(related='cutting_tool_model_id.integral_run_out_accuracy_max', string='端跳精度max', digits=(6, 1))
# cutting_tool_model_id.integral_run_out_accuracy_max
# cutting_tool_run_out_accuracy_min = fields.Float('端跳精度min', digits=(6, 1))
cutting_tool_run_out_accuracy_min = fields.Char(related='cutting_tool_model_id.integral_run_out_accuracy_min',
string='端跳精度min')
string='端跳精度min', digits=(6, 1))
# cutting_tool_model_id.integral_run_out_accuracy_min
# cutting_tool_blade_tip_working_size = fields.Char('刀尖倒角度(°)', size=20)
cutting_tool_blade_tip_working_size = fields.Char(related='specification_id.blade_tip_working_size',
@@ -774,10 +773,10 @@ class ResProductMo(models.Model):
# bfm下单
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
part_number = fields.Char(string='零件图号', readonly=True)
machining_drawings = fields.Binary('2D加工图纸', readonly=True)
quality_standard = fields.Binary('质检标准', readonly=True)
part_name = fields.Char(string='零件名称', readonly=True)
part_number = fields.Char(string='零件图号', readonly=True)
@api.constrains('tool_length')
def _check_tool_length_size(self):
if self.tool_length > 1000000:
@@ -838,34 +837,22 @@ class ResProductMo(models.Model):
else:
return self.env.ref('sf_dlm.product_uom_cubic_millimeter')
def attachment_update(self, name, res_id, res_field, mimetype):
attachment_info = self.env['ir.attachment'].sudo().search(
[('res_id', '=', res_id), ('res_field', '=', res_field)], limit=1)
attachment_info.write({'name': name, 'mimetype': mimetype})
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品
def product_create(self, product_id, item, order_id, order_number, i):
copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
copy_product_id.product_tmpl_id.active = True
model_type = self.env['sf.model.type'].search([], limit=1)
attachment = self.attachment_create(item['model_name'], item['model_data'])
# 获取坯料冗余配置
if not item.get('embryo_redundancy'):
embryo_redundancy_id = model_type.embryo_tolerance_id
else:
embryo_redundancy_id = item.get('embryo_redundancy')
if not embryo_redundancy_id:
raise UserError('请先配置模型类型内的坯料冗余')
vals = {
'name': '%s-%s-%s' % ('P', order_id.name, i),
'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['model_long'] + embryo_redundancy_id.long) * (
item['model_width'] + embryo_redundancy_id.width) * (
item['model_height'] + embryo_redundancy_id.height)),
'model_long': item['model_long'] + model_type.embryo_tolerance,
'model_width': item['model_width'] + model_type.embryo_tolerance,
'model_height': item['model_height'] + model_type.embryo_tolerance,
'model_volume': (item['model_long'] + model_type.embryo_tolerance) * (
item['model_width'] + model_type.embryo_tolerance) * (
item['model_height'] + model_type.embryo_tolerance),
'product_model_type_id': model_type.id,
'model_processing_panel': item['processing_panel_detail'],
# 'model_processing_panel': 'R',
'model_machining_precision': item['model_machining_precision'],
'model_code': item['barcode'],
'length': item['model_long'],
@@ -873,8 +860,8 @@ class ResProductMo(models.Model):
'height': item['model_height'],
'volume': item['model_long'] * item['model_width'] * item['model_height'],
'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']),
'model_name': attachment.name if attachment else None,
'upload_model_file': [(6, 0, [attachment.id])] if attachment else None,
'model_name': attachment.name,
'upload_model_file': [(6, 0, [attachment.id])],
'list_price': item['price'],
'materials_id': self.env['sf.production.materials'].search(
[('materials_no', '=', item['texture_code'])]).id,
@@ -888,10 +875,8 @@ class ResProductMo(models.Model):
'manual_quotation': item['manual_quotation'] or False,
'part_number': item.get('part_number') or '',
'active': True,
'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(
item['machining_drawings']),
'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(item['machining_drawings']),
'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
'part_name': item.get('part_name') or '',
}
tax_id = self.env['account.tax'].sudo().search(
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
@@ -899,28 +884,8 @@ class ResProductMo(models.Model):
vals.update({'taxes_id': [(6, 0, [int(tax_id)])]})
copy_product_id.sudo().write(vals)
product_id.product_tmpl_id.active = False
if item['machining_drawings'] and item['machining_drawings_name'] and item['machining_drawings_mimetype']:
self.attachment_update(item['machining_drawings_name'], copy_product_id.product_tmpl_id.id,
'machining_drawings', item['machining_drawings_mimetype'])
if item['quality_standard'] and item['quality_standard_name'] and item['quality_standard_mimetype']:
self.attachment_update(item['quality_standard_name'], copy_product_id.product_tmpl_id.id,
'quality_standard', item['quality_standard_mimetype'])
return copy_product_id
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)
def _get_ids(self, param):
type_ids = []
if not param:
@@ -937,8 +902,6 @@ class ResProductMo(models.Model):
return [(6, 0, process_parameters_ids)]
def attachment_create(self, name, data):
if not data:
return None
attachment = self.env['ir.attachment'].create({
'datas': base64.b64decode(data),
'type': 'binary',
@@ -956,40 +919,31 @@ class ResProductMo(models.Model):
# if surface_technology:
# no_bom_copy_product_id.route_ids |= surface_technology
no_bom_copy_product_id.product_tmpl_id.active = True
logging.info('no_bom_copy_product_id[is_manual_processing]:%s' % no_bom_copy_product_id.is_manual_processing)
materials_id = self.env['sf.production.materials'].search(
[('materials_no', '=', item['texture_code'])])
materials_type_id = self.env['sf.materials.model'].search(
[('materials_no', '=', item['texture_type_code'])])
model_type = self.env['sf.model.type'].search([], limit=1)
supplier = self.env['mrp.bom'].get_supplier(materials_type_id)
# 获取坯料冗余配置
if not item.get('embryo_redundancy_id'):
embryo_redundancy_id = model_type.embryo_tolerance_id
else:
embryo_redundancy_id = item.get('embryo_redundancy_id')
if not embryo_redundancy_id:
raise UserError('请先配置模型类型内的坯料冗余')
logging.info('no_bom_copy_product_supplier-vals:%s' % supplier)
vals = {
'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R',
order_id.name, i, materials_id.name, materials_type_id.name,
item['model_long'] + embryo_redundancy_id.long,
item['model_width'] + embryo_redundancy_id.width,
item['model_height'] + embryo_redundancy_id.height),
'length': item['model_long'] + embryo_redundancy_id.long,
'width': item['model_width'] + embryo_redundancy_id.width,
'height': item['model_height'] + embryo_redundancy_id.height,
'volume': (item['model_long'] + embryo_redundancy_id.long) * (
item['model_width'] + embryo_redundancy_id.width) * (
item['model_height'] + embryo_redundancy_id.height),
item['model_long'] + model_type.embryo_tolerance,
item['model_width'] + model_type.embryo_tolerance,
item['model_height'] + model_type.embryo_tolerance),
'length': item['model_long'] + model_type.embryo_tolerance,
'width': item['model_width'] + model_type.embryo_tolerance,
'height': item['model_height'] + model_type.embryo_tolerance,
'volume': (item['model_long'] + model_type.embryo_tolerance) * (
item['model_width'] + model_type.embryo_tolerance) * (
item['model_height'] + model_type.embryo_tolerance),
'embryo_model_type_id': model_type.id,
'list_price': item['price'],
'materials_id': materials_id.id,
'materials_type_id': materials_type_id.id,
'single_manufacturing': product_id.single_manufacturing,
'is_bfm': True,
'active': True,
'active': True
}
# 外协和采购生成的坯料需要根据材料型号绑定供应商
if route_type == 'subcontract' or route_type == 'purchase':

View File

@@ -1,43 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import api, fields, models, _
from odoo.tools import OrderedSet
# _get_surface_technics_purchase_ids
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
def button_confirm(self):
super().button_confirm()
workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id)])
for workorder in workorders:
if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True:
move_out = workorder.move_subcontract_workorder_ids[1]
# move_out = self.env['stock.move'].search(
# [('location_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
# ('location_dest_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'VL-SPOC')]).id),
# ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])])
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
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
related_product = fields.Many2one('product.product',compute='_compute_related_product', string='关联产品',help='经此产品工艺加工成的成品')
@api.depends('order_id.origin')
def _compute_related_product(self):
for record in self:
if record.product_id.detailed_type:
production_id = self.env['mrp.production'].search([('name', '=', record.order_id.origin)])
record.related_product = production_id.product_id if production_id else False
else:
record.related_product = False

View File

@@ -1,71 +0,0 @@
from datetime import datetime
from odoo import models
import logging
import base64
import hashlib
_logger = logging.getLogger(__name__)
class QuickEasyOrder(models.Model):
_inherit = 'quick.easy.order'
def distribute_to_factory(self, obj):
"""
多供货方式,重写派单到工厂
:return:
"""
try:
_logger.info('---------派单到工厂-------')
res = {'bfm_process_order_list': []}
for item in obj:
attachment = item.upload_model_file[0]
base64_data = base64.b64encode(attachment.datas)
base64_datas = base64_data.decode('utf-8')
barcode = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
# logging.info('model_file-size: %s' % len(item.model_file))
res['bfm_process_order_list'].append({
'model_long': item.model_length,
'model_width': item.model_width,
'model_height': item.model_height,
'model_volume': item.model_volume,
'model_machining_precision': item.machining_precision,
'model_name': attachment.name,
'model_data': base64_datas,
'model_file': base64.b64encode(item.model_file).decode('utf-8'),
'texture_code': item.material_id.materials_no,
'texture_type_code': item.material_model_id.materials_no,
# 'surface_process_code': self.env['jikimo.surface.process']._json_surface_process_code(item),
'process_parameters_code': self.env[
'sf.production.process.parameter']._json_production_process_item_code(
item),
'price': item.price,
'number': item.quantity,
'total_amount': item.price,
'remark': '',
'manual_quotation': True,
'barcode': barcode,
'part_number': item.part_drawing_number,
'machining_drawings_name': '',
'quality_standard_name': '',
'machining_drawings_mimetype': '',
'quality_standard_mimetype': '',
'machining_drawings': item.machining_drawings,
'quality_standard': '',
'part_name': '',
})
company_id = self.env.ref('base.main_company').sudo()
product_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_default').sudo().with_context(active_test=False).product_variant_id
# user_id = request.env.ref('base.user_admin').sudo()
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
str(datetime.now()), '现结', '支付宝', state='draft')
order_id.default_code = obj.name
i = 1
for item in res['bfm_process_order_list']:
product = self.env['product.template'].sudo().product_create(product_id, item, order_id,
obj.name, i)
order_id.with_user(self.env.ref("base.user_admin")).sale_order_create_line(product, item)
return order_id
except Exception as e:
return UserError('工厂创建销售订单和产品失败,请联系管理员')

View File

@@ -1,222 +0,0 @@
import logging
import json
from odoo import models, fields, api
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class SaleOrder(models.Model):
_inherit = 'sale.order'
state = fields.Selection([
('draft', "报价"),
('sent', "报价已发送"),
('supply method', "供货方式待确认"),
('sale', "销售订单"),
('processing', "加工中"),
('physical_distribution', "物流中"),
('delivered', "已交付"),
('done', "已锁定"),
('cancel', "已取消"),
])
def confirm_to_supply_method(self):
self.state = 'supply method'
def action_confirm(self):
# 判断是否所有产品都选择了供货方式
filter_line = self.order_line.filtered(lambda line: not line.supply_method)
if filter_line:
raise UserError('当前订单内(%s)产品未选择路线,请选择后重试' % ','.join(filter_line.mapped('product_id.name')))
for line in self.order_line:
bom_type = ''
# 根据供货方式修改成品模板
if line.supply_method == 'automation':
bom_type = 'normal'
product_template_id = self.env.ref('sf_dlm.product_template_sf').sudo().product_tmpl_id
elif line.supply_method == 'outsourcing':
bom_type = 'subcontract'
product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_outsourcing').sudo()
elif line.supply_method == 'purchase':
product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_purchase').sudo()
elif line.supply_method == 'manual':
bom_type = 'normal'
product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo()
# 复制成品模板上的属性
line.product_id.product_tmpl_id.copy_template(product_template_id)
# 将模板上的single_manufacturing属性复制到成品上
line.product_id.single_manufacturing = product_template_id.single_manufacturing
order_id = self
product = line.product_id
# 拼接方法需要的item结构
item = {
'texture_code': product.materials_id.materials_no,
'texture_type_code': product.materials_type_id.materials_no,
'model_long': product.length,
'model_width': product.width,
'model_height': product.height,
'price': product.list_price,
'embryo_redundancy_id': line.embryo_redundancy_id,
}
# 获取成品名结尾-n的n
product_seria = int(product.name.split('-')[-1])
# 成品供货方式为采购则不生成bom
if line.supply_method != 'purchase':
bom_data = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).get_bom(product)
_logger.info('bom_data:%s' % bom_data)
if bom_data:
bom = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).bom_create(product, 'normal', False)
bom.with_user(self.env.ref("base.user_admin")).bom_create_line_has(bom_data)
else:
# 当成品上带有客供料选项时,生成坯料时选择“客供料”路线
if line.embryo_redundancy_id:
# 将成品模板的内容复制到成品上
customer_provided_embryo = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').sudo()
# 创建坯料客供料的批量不需要创建bom
material_customer_provided_embryo = self.env['product.template'].sudo().no_bom_product_create(
customer_provided_embryo.with_context(active_test=False).product_variant_id,
item,
order_id, 'material_customer_provided', product_seria, product)
# 成品配置bom
product_bom_material_customer_provided = self.env['mrp.bom'].with_user(
self.env.ref("base.user_admin")).bom_create(
product, bom_type, 'product')
product_bom_material_customer_provided.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
material_customer_provided_embryo)
elif line.product_id.materials_type_id.gain_way == '自加工':
self_machining_id = self.env.ref('sf_dlm.product_embryo_sf_self_machining').sudo()
# 创建坯料
self_machining_embryo = self.env['product.template'].sudo().no_bom_product_create(
self_machining_id,
item,
order_id, 'self_machining', product_seria, product)
# 创建坯料的bom
self_machining_bom = self.env['mrp.bom'].with_user(
self.env.ref("base.user_admin")).bom_create(
self_machining_embryo, 'normal', False)
# 创建坯料里bom的组件
self_machining_bom_line = self_machining_bom.with_user(
self.env.ref("base.user_admin")).bom_create_line(
self_machining_embryo)
if not self_machining_bom_line:
raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配')
# 产品配置bom
product_bom_self_machining = self.env['mrp.bom'].with_user(
self.env.ref("base.user_admin")).bom_create(
product, bom_type, 'product')
product_bom_self_machining.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
self_machining_embryo)
elif line.product_id.materials_type_id.gain_way == '外协':
outsource_id = self.env.ref('sf_dlm.product_embryo_sf_outsource').sudo()
# 创建坯料
outsource_embryo = self.env['product.template'].sudo().no_bom_product_create(outsource_id,
item,
order_id,
'subcontract',
product_seria, product)
if outsource_embryo == -3:
raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配')
# 创建坯料的bom
outsource_bom = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).bom_create(
outsource_embryo,
'subcontract', True)
# 创建坯料的bom的组件
outsource_bom_line = outsource_bom.with_user(
self.env.ref("base.user_admin")).bom_create_line(outsource_embryo)
if not outsource_bom_line:
raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配')
# 产品配置bom
product_bom_outsource = self.env['mrp.bom'].with_user(
self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product')
product_bom_outsource.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
outsource_embryo)
elif line.product_id.materials_type_id.gain_way == '采购':
purchase_id = self.env.ref('sf_dlm.product_embryo_sf_purchase').sudo()
purchase_embryo = self.env['product.template'].sudo().no_bom_product_create(purchase_id,
item,
order_id,
'purchase', product_seria,
product)
if purchase_embryo == -3:
raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配')
else:
# 产品配置bom
product_bom_purchase = self.env['mrp.bom'].with_user(
self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product')
product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
purchase_embryo)
result = super(SaleOrder, self).action_confirm()
self.merge_picking()
return result
def merge_picking(self):
"""
合并多个stock.picking为一个新的stock.picking。
:param picking_ids: 需要合并的stock.picking记录ID列表
:return: 合并后的新的stock.picking对象
"""
picking_idss = self.env['stock.picking'].search([('retrospect_ref', 'ilike','%'+ self.name +'%')])
for pick in picking_idss:
print('qfwowio',pick)
picking_ids = self.env['stock.picking'].search([('retrospect_ref', '=', self.default_code)])
if not picking_ids:
return
# 获取需要合并的 stock.picking 记录
# 创建一个新的 stock.picking 作为合并结果
group = self.env['procurement.group'].create({
'name': self.name,
'partner_id': self.partner_id.id,
})
new_picking = self.env['stock.picking'].create({
'partner_id': picking_ids[0].partner_id.id,
'location_id': picking_ids[0].location_id.id,
'location_dest_id': picking_ids[0].location_dest_id.id,
'move_ids': False,
'sale_id': self.id,
'picking_type_id': picking_ids[0].picking_type_id.id,
'origin': '合并自: ' + ','.join([p.origin for p in picking_ids]),
'retrospect_ref':self.default_code,
'person_of_delivery':picking_ids[0].person_of_delivery,
'telephone_of_delivery': picking_ids[0].telephone_of_delivery,
'address_of_delivery': picking_ids[0].address_of_delivery,
'group_id':picking_ids[0].group_id.id
})
# 合并所有 move_lines
for picking in picking_ids:
for move in picking.move_ids:
# 复制 move_lines 到新的 picking
new_move_vals = move.copy_data()[0]
new_move_vals['picking_id']=new_picking.id
self.env['stock.move'].create(new_move_vals)
for pick in picking_idss:
print('qfwowio',pick)
# 处理合并后的配送单状态
new_picking.action_confirm() # 确认新的配送单
for pick in picking_idss:
print('qfwowio',pick)
new_picking.action_assign() # 分配新的配送单
# 删除原有的配送单
picking_ids.write({'state': 'cancel'}) # 将原配送单状态更改为取消
return new_picking
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
# 供货方式
supply_method = fields.Selection([
('automation', "自动化产线加工"),
('manual', "人工线下加工"),
('purchase', "外购"),
('outsourcing', "委外加工"),
], string='供货方式')
def write(self, vals):
if 'supply_method' in vals:
for line in self:
if vals['supply_method'] == 'automation' and line.manual_quotation:
raise UserError('当前(%s)产品为人工编程产品,不能选择自动化产线加工' % ','.join(line.mapped('product_id.name')))
return super(SaleOrderLine, self).write(vals)

View File

@@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
import logging
from odoo import fields, models, api
from odoo.exceptions import UserError
class SfProductionProcessParameter(models.Model):
_inherit = 'sf.production.process.parameter'
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if self._context.get('route_id'):
parameter = []
routing = self.env['mrp.routing.workcenter'].search([('id', '=', self._context.get('route_id'))])
technology_design = self.env['sf.technology.design'].search(
[('production_id', '=', self._context.get('production_id')), ('routing_tag', '=', 'special'),
('route_id', '=', self._context.get('route_id'))])
for t in technology_design:
if t.process_parameters_id:
parameter.append(t.process_parameters_id.id)
domain = [('process_id', '=', routing.surface_technics_id.id), ('id', 'not in', parameter)]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid)

View File

@@ -1,110 +0,0 @@
# -*- coding: utf-8 -*-
from collections import Counter
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
class sf_technology_design(models.Model):
_name = 'sf.technology.design'
_description = "工艺设计"
group_uniq_id = fields.Integer('同一制造订单唯一id',default=0)
sequence = fields.Integer('序号')
route_id = fields.Many2one('mrp.routing.workcenter', '工序')
process_parameters_id = fields.Many2one('sf.production.process.parameter', string='表面工艺参数')
panel = fields.Char('加工面')
routing_tag = fields.Selection(related='route_id.routing_tag', string='标签', store=True)
time_cycle_manual = fields.Float(related='route_id.time_cycle_manual', string='预计时长')
production_id = fields.Many2one('mrp.production')
is_auto = fields.Boolean('是否自动生成', default=False)
active = fields.Boolean('有效', default=True)
# @api.depends('production_id')
# def _compute_group_uniq_id(self):
# for record in self:
def json_technology_design_str(self, k, route, i, process_parameter):
workorders_values_str = [0, '', {
'route_id': route.id if route.routing_type in ['表面工艺'] else route.route_workcenter_id.id,
'panel': k,
'process_parameters_id': False if route.routing_type != '表面工艺' else self.env[
'sf.production.process.parameter'].search(
[('id', '=', process_parameter.id)]).id,
'sequence': i,
'is_auto': True}]
return workorders_values_str
def write(self, vals):
print('qwfojkqwfkio')
return super(sf_technology_design, self).write(vals)
def unlink_technology_design(self):
self.active = False
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if not vals.get('route_id'):
raise ValidationError(_("工序不能为空"))
result = super(sf_technology_design, self).create(vals_list)
for res in result:
record = self.search([('production_id', '=', res.production_id.id), ('active', 'in', [True, False])], order='group_uniq_id desc', limit=1)
res.group_uniq_id=record.group_uniq_id + 1
return result
def get_duplicates_with_inactive(self,technology_designs):
# 统计每个 'sequence' 出现的次数
sequence_count = Counter(technology_design.sequence for technology_design in technology_designs)
# 筛选出 'sequence' 重复且 'active' 为 False 的元素
result = [
technology_design for technology_design in technology_designs
if sequence_count[technology_design.sequence] > 1 and technology_design.active is False
]
return result
# def rearrange_numbering(self,self_technology_designs):
# inactive_designs = self.get_duplicates_with_inactive(self_technology_designs)
# if inactive_designs:
# max_design = max(self_technology_designs, key=lambda x: x.sequence)
# max_sequence = max_design.sequence if max_design else 0
# for designs in inactive_designs:
# max_sequence += 1
# designs.sequence = max_sequence
# self_technology_designs.sorted(key=lambda techology_design:techology_design.sequence)
# return self_technology_designs
def get_technology_design(self):
return {
'sequence':self.sequence,
'route_id': self.route_id.id,
'process_parameters_id': self.process_parameters_id.id,
'panel': self.panel,
'routing_tag': self.routing_tag,
'time_cycle_manual': self.time_cycle_manual,
'is_auto': self.is_auto,
'active': self.active,
'group_uniq_id':self.group_uniq_id,
}
def sync_technology_designs(self,production_technology_designs, self_technology_designs):
production_id = production_technology_designs[0].production_id.id
self_technology_design_dict = {item.group_uniq_id:item for item in self_technology_designs}
production_technology_designs_dict = {item.group_uniq_id:item for item in production_technology_designs}
for technology_design in production_technology_designs:
if not self_technology_design_dict.get(technology_design.group_uniq_id):
technology_design.write({'production_id': False})
else:
technology_design.write(self_technology_design_dict.get(technology_design.group_uniq_id).get_technology_design())
for technology_design in self_technology_designs:
if not production_technology_designs_dict.get(technology_design.group_uniq_id):
technology_design = technology_design.get_technology_design()
technology_design.update({'production_id': production_id})
technology_design.pop('group_uniq_id')
self.env['sf.technology.design'].create(technology_design)
def unified_procedure_multiple_work_orders(self,self_technology_designs,production_item):
technology_designs = self.env['sf.technology.design'].sudo().search(
[('production_id', '=', production_item.id), ('active', 'in', [True, False])])
self.sync_technology_designs(self_technology_designs=self_technology_designs,production_technology_designs=technology_designs)

View File

@@ -182,11 +182,6 @@ class StockRule(models.Model):
moves._action_confirm()
return True
def attachment_update(self, name, res_id, res_field):
attachment_info = self.env['ir.attachment'].sudo().search(
[('res_id', '=', res_id), ('res_field', '=', res_field)], limit=1)
attachment_info.write({'name': name})
@api.model
def _run_manufacture(self, procurements):
productions_values_by_company = defaultdict(list)
@@ -208,15 +203,6 @@ class StockRule(models.Model):
'''创建制造订单'''
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
productions_values)
# 将这一批制造订单的采购组根据成品设置为不同的采购组
product_group_id = {}
for index, production in enumerate(productions):
if production.product_id.id not in product_group_id.keys():
product_group_id[production.product_id.id] = production.procurement_group_id.id
else:
productions_values[index].update({'name': production.name})
procurement_group_vals = production._prepare_procurement_group_vals(productions_values[index])
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_finished_values())
@@ -281,22 +267,23 @@ class StockRule(models.Model):
workorder_duration += workorder.duration_expected
sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)])
# 如果订单为空,则获取来源制造订单的销售
if not sale_order:
mrp_production = self.env['mrp.production'].sudo().search([('name', '=', production.origin)],
limit=1)
if mrp_production:
sale_order = self.env['sale.order'].sudo().search([('name', '=', mrp_production.origin)])
# 根据销售订单号查询快速订
quick_easy_order = self.env['quick.easy.order'].sudo().search([('sale_order_id', '=', sale_order.id)])
if quick_easy_order:
production.write({'part_number': quick_easy_order.part_drawing_number,
'part_drawing': quick_easy_order.machining_drawings})
else:
mrp_production = production
# if sale_order:
production.write({'part_number': production.product_id.part_number,
'part_drawing': production.product_id.machining_drawings,
'quality_standard': production.product_id.quality_standard})
if sale_order:
# sale_order.write({'schedule_status': 'to schedule'})
self.env['sf.production.plan'].sudo().with_company(company_id).create({
'name': production.name,
'order_deadline': sale_order.deadline_of_delivery,
'production_id': production.id,
'date_planned_start': production.date_planned_start,
'origin': mrp_production.origin,
'origin': production.origin,
'product_qty': production.product_qty,
'product_id': production.product_id.id,
'state': 'draft',
@@ -310,85 +297,77 @@ class StockRule(models.Model):
# 为同一个product_id创建一个生产订单名称列表
product_id_to_production_names[product_id] = [production.name for production in all_production]
for production_item in productions:
technology_design_values = []
production_programming = self.env['mrp.production'].search(
[('product_id.id', '=', production_item.product_id.id),
('origin', '=', production_item.origin)],
limit=1, order='id asc')
if production_item.product_id.id in product_id_to_production_names:
# 同一个产品多个制造订单对应一个编程单和模型库
# 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production_item.programming_no and production_item.production_type == '自动化产线加工':
if not production_programming.programming_no:
if production_item.product_id.model_process_parameters_ids:
is_purchase = False
sorted_process_parameters = sorted(production_item.product_id.model_process_parameters_ids,
key=lambda w: w.id)
consecutive_process_parameters = []
m = 0
for i in range(len(sorted_process_parameters) - 1):
if m == 0:
is_purchase = False
if self.env['product.template']._get_process_parameters_product(
sorted_process_parameters[i]).partner_id == self.env[
'product.template']._get_process_parameters_product(sorted_process_parameters[
i + 1]).partner_id and \
sorted_process_parameters[i].gain_way == '外协':
if sorted_process_parameters[i] not in consecutive_process_parameters:
consecutive_process_parameters.append(sorted_process_parameters[i])
consecutive_process_parameters.append(sorted_process_parameters[i + 1])
m += 1
continue
else:
if m == len(consecutive_process_parameters) - 1 and m != 0:
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
production_item,
product_id_to_production_names)
if sorted_process_parameters[i] in consecutive_process_parameters:
is_purchase = True
consecutive_process_parameters = []
m = 0
# 当前面的连续外协采购单生成再生成当前外协采购单
if is_purchase is False:
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
production_item,
product_id_to_production_names)
if m == len(consecutive_process_parameters) - 1 and m != 0:
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
production_item,
product_id_to_production_names)
if sorted_process_parameters[i] in consecutive_process_parameters:
is_purchase = True
consecutive_process_parameters = []
m = 0
if m == len(consecutive_process_parameters) - 1 and m != 0:
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
production_item,
product_id_to_production_names)
if is_purchase is False and m == 0:
if len(sorted_process_parameters) == 1:
self.env['purchase.order'].get_purchase_order(sorted_process_parameters,
production_item,
product_id_to_production_names)
else:
self.env['purchase.order'].get_purchase_order(sorted_process_parameters[i],
production_item,
product_id_to_production_names)
# # 同一个产品多个制造订单对应一个编程单和模型库
# # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production_item.programming_no:
if not production_programming.programming_no:
production_item.fetchCNC(
', '.join(product_id_to_production_names[production_item.product_id.id]))
else:
production_item.write({'programming_no': production_programming.programming_no,
'programming_state': '编程中'})
i = 0
if production_item.product_id.categ_id.type == '成品':
# 根据加工面板的面数及成品工序模板生成工序设计
if production_item.production_type == '自动化产线加工':
model = 'sf.product.model.type.routing.sort'
domain = [
('product_model_type_id', '=', production_item.product_id.product_model_type_id.id)]
else:
model = 'sf.manual.product.model.type.routing.sort'
domain = [('manual_product_model_type_id', '=',
production_item.product_id.product_model_type_id.id)]
product_routing_workcenter = self.env[model].search(domain, order='sequence asc')
if production_item.production_type == '自动化产线加工':
for k in (production_item.product_id.model_processing_panel.split(',')):
for route in product_routing_workcenter:
i += 1
technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str(k, route, i, False))
else:
for route in product_routing_workcenter:
i += 1
technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str(False, route, i, False))
elif production_item.product_id.categ_id.type == '坯料':
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
[('embryo_model_type_id', '=', production_item.product_id.embryo_model_type_id.id)],
order='sequence asc'
)
for route_embryo in embryo_routing_workcenter:
i += 1
technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str(False, route_embryo, i,
False))
surface_technics_arr = []
route_workcenter_arr = []
for item in production_item.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
if item.route_workcenter_id.surface_technics_id.id:
for process_param in production_item.product_id.model_process_parameters_ids:
if item.route_workcenter_id.surface_technics_id == process_param.process_id:
surface_technics_arr.append(
item.route_workcenter_id.surface_technics_id.id)
route_workcenter_arr.append(item.route_workcenter_id.id)
if surface_technics_arr:
production_process = self.env['sf.production.process'].search(
[('id', 'in', surface_technics_arr)],
order='sequence asc'
)
for p in production_process:
logging.info('production_process:%s' % p.name)
process_parameter = production_item.product_id.model_process_parameters_ids.filtered(
lambda pm: pm.process_id.id == p.id)
if process_parameter:
i += 1
route_production_process = self.env[
'mrp.routing.workcenter'].search(
[('surface_technics_id', '=', p.id),
('id', 'in', route_workcenter_arr)])
technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str(False,
route_production_process,
i,
process_parameter))
production_item.technology_design_ids = technology_design_values
productions.write({'state': 'technology_to_confirmed'})
return True
@@ -423,8 +402,6 @@ class ProductionLot(models.Model):
"""Generate `lot_names` from a string."""
if first_lot.__contains__(display_name):
first_lot = first_lot[(len(display_name) + 1):]
else:
first_lot = first_lot[-3:]
# We look if the first lot contains at least one digit.
caught_initial_number = regex_findall(r"\d+", first_lot)
@@ -479,14 +456,6 @@ class ProductionLot(models.Model):
if product.categ_id.name == '刀具':
return self.env['stock.lot'].get_tool_generate_lot_names1(company, product)
else:
# 对last_serial的name进行检测如果不是以产品名称+数字的形式的就重新搜索
if product.name.split('[')[0] not in last_serial.name:
last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id),
('name', 'ilike', product.name.split('[')[0])],
limit=1, order='name desc')
if not last_serial:
return "%s-%03d" % (product.name, 1)
return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name, 2)[1]
now = datetime.now().strftime("%Y%m%d")
if product.cutting_tool_model_id:
@@ -583,57 +552,6 @@ class StockPicking(models.Model):
_inherit = 'stock.picking'
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
person_of_delivery = fields.Char('收货人', compute='_compute_move_ids', store=True)
telephone_of_delivery = fields.Char('电话号码', compute='_compute_move_ids', store=True)
address_of_delivery = fields.Char('联系地址', compute='_compute_move_ids', store=True)
retrospect_ref = fields.Char('平台订单号', compute='_compute_move_ids', store=True)
sale_name = fields.Char('销售订单', compute='_compute_move_ids', store=True)
picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code')
related_group_ids = fields.Many2many(
'procurement.group', string='关联捕获组',
readonly=True, compute='_compute_related_group_ids', store=True)
@api.depends('origin')
def _compute_related_group_ids(self):
for record in self:
if not record.origin:
continue
names = record.origin.split(',')
related_group_ids = self.env['procurement.group'].search([('name', 'in', names)])
record.related_group_ids = [(6, 0, related_group_ids.ids)]
@api.depends('move_ids', 'move_ids.product_id')
def _compute_move_ids(self):
for item in self:
if item.move_ids:
if item.picking_type_id.sequence_code == 'DL':
sale_name = item.move_ids[0].product_id.with_context(lang='zh_CN').name.split('-')[1]
item.sale_name=sale_name
if 'S' in sale_name:
sale_id = self.env['sale.order'].sudo().search([('name', '=', sale_name)])
item.person_of_delivery = sale_id.person_of_delivery
item.telephone_of_delivery = sale_id.telephone_of_delivery
item.address_of_delivery = sale_id.address_of_delivery
else:
raise ValidationError('坯料名称格式错误,正确格式为[R-S???-?]')
move_ids = []
for move_id in item.move_ids:
move_ids.append(move_id.product_id.id)
boms = self.env['mrp.bom'].sudo().search([('bom_line_ids.product_id', 'in', move_ids)])
if boms:
codes_list = []
for bom in boms:
if bom.product_tmpl_id.default_code:
code_list = bom.product_tmpl_id.default_code.split('-')
if len(code_list) >= 4:
code = '-'.join(code_list[:4])
if code not in codes_list:
codes_list.append(code)
else:
raise ValidationError('坯料成品的内部参考值格式错误')
item.retrospect_ref = ','.join(codes_list)
elif item.picking_type_id.sequence_code in ['INT', 'PC']:
pass
# 设置外协出入单的名称
def _get_name_Res(self, rescode):
@@ -647,89 +565,84 @@ class StockPicking(models.Model):
return '%s%s' % (rescode, num)
def button_validate(self):
move_out = self.env['stock.move'].search(
[('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)])
# if self.id == move_out.picking_id.id:
# if move_out.move_line_ids.workorder_id.state not in ['progress']:
# raise UserError(
# _('该出库单里源单据内的单号为%s的工单还未开始不能进行验证操作' % move_out.move_line_ids.workorder_id.name))
# 入库单验证
move_in = self.env['stock.move'].search(
[('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin), ('picking_id', '=', self.id)])
if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id:
if move_out.origin == move_in.origin:
move_in.write({'production_id': False})
if move_out.picking_id.state != 'done':
raise UserError(
_('该入库单对应的单号为%s的出库单还未完成,不能进行验证操作!' % move_out.picking_id.name))
res = super().button_validate()
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
if res is True and self.picking_type_id.id == picking_type_in:
# 如果是最后一张外协入库单,则设置库存位置的预留数量
move_in = self.move_ids
if move_in:
workorder = move_in.subcontract_workorder_id
workorders = workorder.production_id.workorder_ids
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True).sorted('sequence')
if workorder == subcontract_workorders[-1]:
self.env['stock.quant']._update_reserved_quantity(
move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
lot_id=move_in.move_line_ids.lot_id,
package_id=False, owner_id=False, strict=False
)
workorder.button_finish()
picking_type_out = self.env.ref('sf_manufacturing.outcontract_picking_out').id
if res and self.picking_type_id.id == picking_type_out:
move_out = self.move_ids
if move_out:
workorder = move_out.subcontract_workorder_id
workorder.button_start()
if res is True:
if self.id == move_out.picking_id.id:
# if move_out.move_line_ids.workorder_id.state == 'progress':
move_in = self.env['stock.move'].search(
[('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)])
production = self.env['mrp.production'].search([('name', '=', self.origin)])
if move_in.state != 'done':
move_in.write({'state': 'assigned'})
self.env['stock.move.line'].create(move_in.get_move_line(production, None))
return res
# 创建 外协出库入单
def create_outcontract_picking(self, workorders, item, sorted_workorders):
for workorder in workorders:
if workorder.move_subcontract_workorder_ids:
workorder.move_subcontract_workorder_ids.write({'state': 'waiting'})
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'waiting'})
else:
# 创建一个新的补货组
procurement_group_id = self.env['procurement.group'].create({
'name': workorder.name,
'partner_id': self.partner_id.id,
})
move_dest_id = False
# 如果当前工单是是制造订单的最后一个工艺外协工单
if workorder == next((workorder for workorder in reversed(item.workorder_ids) if workorder.is_subcontract), None):
move_dest_id = item.move_raw_ids[0].id
else:
# 从sorted_workorders中找到上一工单的move
if len(sorted_workorders) > 1:
move_dest_id = sorted_workorders[sorted_workorders.index(workorder)+1].move_subcontract_workorder_ids[1].id
def create_outcontract_picking(self, sorted_workorders_arr, item):
m = 0
for sorted_workorders in sorted_workorders_arr:
# pick_ids = []
if m == 0:
outcontract_stock_move = self.env['stock.move'].search(
[('workorder_id', '=', sorted_workorders.id), ('production_id', '=', item.id)])
if not outcontract_stock_move:
new_picking = True
location_id = self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id,
location_dest_id = self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id,
outcontract_picking_type_in = self.env.ref(
'sf_manufacturing.outcontract_picking_in').id,
outcontract_picking_type_out = self.env.ref(
'sf_manufacturing.outcontract_picking_out').id,
moves_in = self.env['stock.move'].sudo().create(
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in,
procurement_group_id.id, move_dest_id))
picking_in = self.create(
moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/'))
# pick_ids.append(picking_in.id)
moves_in.write(
{'picking_id': picking_in.id, 'state': 'waiting'})
moves_in._assign_picking_post_process(new=new_picking)
moves_out = self.env['stock.move'].sudo().create(
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out,
procurement_group_id.id, moves_in.id))
workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]})
self.env['stock.move']._get_stock_move_values_Res(item, location_dest_id, location_id,
outcontract_picking_type_out))
picking_out = self.create(
moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/'))
moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/'))
# pick_ids.append(picking_out.id)
moves_out.write(
{'picking_id': picking_out.id, 'state': 'waiting'})
{'picking_id': picking_out.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
moves_out._assign_picking_post_process(new=new_picking)
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
def _compute_state(self):
super(StockPicking, self)._compute_state()
for picking in self:
# 外协出库单根据工单状态,采购单状态来确定
picking_type_id = self.env.ref('sf_manufacturing.outcontract_picking_out').id
if picking.picking_type_id.id == picking_type_id:
if picking.move_ids:
workorder = picking.move_ids[0].subcontract_workorder_id
if picking.state == 'assigned':
if workorder.state in ['pending',
'waiting'] or workorder._get_surface_technics_purchase_ids().state in [
'draft', 'sent']:
picking.state = 'waiting'
moves_in = self.env['stock.move'].sudo().create(
self.env['stock.move']._get_stock_move_values_Res(item, location_id, location_dest_id,
outcontract_picking_type_in))
picking_in = self.create(
moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/'))
# pick_ids.append(picking_in.id)
moves_in.write(
{'picking_id': picking_in.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
moves_in._assign_picking_post_process(new=new_picking)
m += 1
# sorted_workorders.write({'picking_ids': [(6, 0, pick_ids)]})
class ReStockMove(models.Model):
@@ -739,21 +652,17 @@ class ReStockMove(models.Model):
materiel_width = fields.Float(string='物料宽度', digits=(16, 4))
materiel_height = fields.Float(string='物料高度', digits=(16, 4))
def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False):
route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id
stock_rule = self.env['stock.rule'].sudo().search(
[('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)])
def _get_stock_move_values_Res(self, item, location_src_id, location_dest_id, picking_type_id):
route = self.env['stock.route'].sudo().search([('name', '=', '表面工艺外协')])
move_values = {
'name': '',
'company_id': item.company_id.id,
'product_id': item.bom_id.bom_line_ids.product_id.id,
'product_uom': item.bom_id.bom_line_ids.product_uom_id.id,
'product_uom_qty': 1.0,
'location_id': stock_rule.location_src_id.id,
'location_dest_id': stock_rule.location_dest_id.id,
'location_id': location_src_id,
'location_dest_id': location_dest_id,
'origin': item.name,
'group_id': group_id,
'move_dest_ids': [(6, 0, [move_dest_ids])] if move_dest_ids else False,
# 'route_ids': False if not route else [(4, route.id)],
'date_deadline': datetime.now(),
'picking_type_id': picking_type_id,
@@ -777,7 +686,7 @@ class ReStockMove(models.Model):
'picking_type_id': picking_type_id,
'location_id': self.mapped('location_id').id,
'location_dest_id': self.mapped('location_dest_id').id,
'state': 'waiting',
'state': 'confirmed',
}
def get_move_line(self, production_id, sorted_workorders):
@@ -790,7 +699,7 @@ class ReStockMove(models.Model):
'picking_id': self.picking_id.id,
'reserved_uom_qty': 1.0,
'lot_id': production_id.move_line_raw_ids.lot_id.id,
'company_id': self.env.company.id,
'company_id': self.company_id.id,
# 'workorder_id': '' if not sorted_workorders else sorted_workorders.id,
# 'production_id': '' if not sorted_workorders else sorted_workorders.production_id.id,
'state': 'assigned',
@@ -834,8 +743,6 @@ class ReStockMove(models.Model):
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
else:
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
if self.picking_type_id.sequence_code == 'DL' and not self.move_line_nosuggest_ids:
self.action_assign_serial_show_details()
elif self.product_id.tracking == "lot":
self._put_tool_lot(self.company_id, self.product_id, self.origin)
@@ -988,42 +895,6 @@ class ReStockMove(models.Model):
qty_by_location[loc.id] += 1
return move_lines_commands
def _merge_moves_fields(self):
"""
合并制造订单的完成move单据
"""
res = super(ReStockMove, self)._merge_moves_fields()
if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨', '生产入库', '客供料入库']:
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
productions = self.env['mrp.production'].search(
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)])
res['origin'] = ','.join(productions.mapped('name'))
return res
def _get_new_picking_values(self):
"""
创建调拨单时,在此新增或修改调拨单的数据
"""
res = super(ReStockMove, self)._get_new_picking_values()
## 制造订单报废生成的新制造订单不走合并
production_remanufacture = None
if 'origin' in res:
if self.picking_type_id.name in ['生产发料', '内部调拨']:
production_remanufacture = self.env['mrp.production'].search(
[('name', '=', res['origin']), ('is_remanufacture', '=', True)])
if not production_remanufacture:
if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨']:
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
productions = self.env['mrp.production'].search(
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)])
if productions.mapped('name'):
res['origin'] = ','.join(productions.mapped('name'))
res['retrospect_ref'] = production.product_id.name
return res
subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True,
index='btree_not_null')
class ReStockQuant(models.Model):
_inherit = 'stock.quant'

View File

@@ -1,9 +1,5 @@
<odoo>
<data noupdate="1">
<record id="group_show_button" model="res.groups">
<field name="name">演示模式</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<data>
</data>
</odoo>

View File

@@ -11,11 +11,11 @@ access_sf_model_type_group_sale_director,sf_model_type_group_sale_director,model
access_sf_model_type_group_purchase_director,sf_model_type_group_purchase_director,model_sf_model_type,sf_base.group_purchase_director,1,0,0,0
access_sf_model_type_group_plan_director,sf_model_type_group_plan_director,model_sf_model_type,sf_base.group_plan_director,1,0,0,0
access_sf_product_model_type_routing_sort_group_sf_mrp_user,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
access_sf_product_model_type_routing_sort_manager,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
access_sf_product_model_type_routing_sort_manager,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0
access_sf_embryo_model_type_routing_sort_group_sf_mrp_user,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
access_sf_embryo_model_type_routing_sort_manager,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
access_sf_embryo_model_type_routing_sort_manager,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0
access_sf_surface_technics_model_type_routing_sort,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
access_sf_surface_technics_model_type_routing_sort_manager,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
access_sf_surface_technics_model_type_routing_sort_manager,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0
access_sf_production_line_group_sf_mrp_user,sf.production.line,model_sf_production_line,sf_base.group_sf_mrp_user,1,1,1,0
access_sf_production_line_manager,sf.production.line,model_sf_production_line,sf_base.group_sf_mrp_manager,1,1,1,0
access_maintenance_equipment_tool_group_sf_mrp_user,maintenance_equipment_tool,model_maintenance_equipment_tool,sf_base.group_sf_mrp_user,1,0,0,0
@@ -52,7 +52,6 @@ access_mrp_routing_workcenter_manager_group_sf_mrp_user,mrp.routing.workcenter.m
access_mrp_bom_manager_group_sf_mrp_user,mrp.bom.manager,mrp.model_mrp_bom,sf_base.group_sf_mrp_user,1,1,1,0
access_mrp_bom_line_manager_group_sf_mrp_user,mrp.bom.line.manager,mrp.model_mrp_bom_line,sf_base.group_sf_mrp_user,1,1,1,0
access_mrp_bom_line_group_plan_director,mrp_bom_line_group_plan_director,mrp.model_mrp_bom_line,sf_base.group_plan_director,1,1,1,0
access_mrp_bom_line_group_sf_stock_user,mrp_bom_line_group_sf_stock_user,mrp.model_mrp_bom_line,sf_base.group_sf_stock_user,1,1,1,0
access_mrp_bom_line_group_sale_director,mrp_bom_line_group_sale_director,mrp.model_mrp_bom_line,sf_base.group_sale_director,1,1,1,0
access_mrp_bom_line_group_sale_salemanager,mrp_bom_line_group_sale_salemanager,mrp.model_mrp_bom_line,sf_base.group_sale_salemanager,1,0,1,0
access_mrp_bom_line_group_purchase_director,mrp_bom_line_group_purchase_director,mrp.model_mrp_bom_line,sf_base.group_purchase_director,1,1,1,0
@@ -117,7 +116,7 @@ access_mrp_production_group_quality,mrp_production,model_mrp_production,sf_base.
access_mrp_production_group_quality_director,mrp_production,model_mrp_production,sf_base.group_quality_director,1,1,0,0
access_mrp_workorder_group_quality,mrp_workorder,model_mrp_workorder,sf_base.group_quality,1,1,0,0
access_mrp_workorder_group_quality_director,mrp_workorder,model_mrp_workorder,sf_base.group_quality_director,1,1,0,0
access_mrp_workorder,mrp_workorder,model_mrp_workorder,sf_base.group_plan_dispatch,1,1,1,0
access_mrp_workorder,mrp_workorder,model_mrp_workorder,sf_base.group_plan_dispatch,1,1,0,0
access_sf_production_line_group_plan_dispatch,sf.production.line,model_sf_production_line,sf_base.group_plan_dispatch,1,0,0,0
access_sf_production_line_group_plan_director,sf.production.line,model_sf_production_line,sf_base.group_plan_director,1,1,1,0
access_sf_production_line,sf.production.line,model_sf_production_line,sf_maintenance.sf_group_equipment_user,1,1,1,0
@@ -166,21 +165,7 @@ access_sf_agv_scheduling_group_sf_order_user,sf_agv_scheduling_group_sf_order_us
access_sf_agv_scheduling_group_sf_mrp_manager,sf_agv_scheduling_group_sf_mrp_manager,model_sf_agv_scheduling,sf_base.group_sf_mrp_manager,1,1,1,0
access_sf_agv_scheduling_group_sf_equipment_user,sf_agv_scheduling_group_sf_equipment_user,model_sf_agv_scheduling,sf_base.group_sf_equipment_user,1,1,1,0
access_sf_technology_design_group_plan_dispatch,sf_technology_design_group_plan_dispatch,model_sf_technology_design,sf_base.group_plan_dispatch,1,1,1,0
access_sf_technology_design_group_sf_mrp_manager,sf_technology_design_group_sf_mrp_manager,model_sf_technology_design,sf_base.group_sf_mrp_manager,1,0,0,0
access_sf_technology_design_group_production_engineer,sf_technology_design_group_production_engineer,model_sf_technology_design,sf_base.group_production_engineer,1,1,1,0
access_sf_technology_design_group_sf_equipment_user,sf_technology_design_group_sf_equipment_user,model_sf_technology_design,sf_base.group_sf_equipment_user,1,0,0,0
access_sf_technology_design_group_sf_order_user,sf_technology_design_group_sf_order_user,model_sf_technology_design,sf_base.group_sf_order_user,1,0,0,0
access_sf_production_technology_wizard_group_plan_dispatch,sf_production_technology_wizard_group_plan_dispatch,model_sf_production_technology_wizard,sf_base.group_plan_dispatch,1,1,1,0
access_sf_production_technology_wizard_group_sf_mrp_manager,sf_production_technology_wizard_group_sf_mrp_manager,model_sf_production_technology_wizard,sf_base.group_sf_mrp_manager,1,0,0,0
access_sf_production_technology_wizard_group_production_engineer,sf_production_technology_wizard_group_production_engineer,model_sf_production_technology_wizard,sf_base.group_production_engineer,1,1,1,0
access_sf_production_technology_re_adjust_wizard_group_plan_dispatch,sf_production_technology_re_adjust_wizard_group_plan_dispatch,model_sf_production_technology_re_adjust_wizard,sf_base.group_plan_dispatch,1,1,1,0
access_sf_production_technology_re_adjust_wizard_group_sf_mrp_manager,sf_production_technology_re_adjust_wizard_group_sf_mrp_manager,model_sf_production_technology_re_adjust_wizard,sf_base.group_sf_mrp_manager,1,1,1,0
access_sf_production_technology_re_adjust_wizard_group_production_engineer,sf_production_technology_re_adjust_wizard_group_production_engineer,model_sf_production_technology_re_adjust_wizard,sf_base.group_production_engineer,1,1,1,0
access_sf_manual_product_model_type_routing_sort_group_sf_mrp_user,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
access_sf_manual_product_model_type_routing_sort_manager,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
access_sf_manual_product_model_type_routing_sort_group_plan_dispatch,sf_manual_product_model_type_routing_sort_group_plan_dispatch,model_sf_manual_product_model_type_routing_sort,sf_base.group_plan_dispatch,1,0,0,0
access_sf_detection_result_manager,sf_detection_result_manager,model_sf_detection_result,,1,1,1,1
access_mrp_workorder_batch_replan_wizard_group_plan_dispatch,mrp_workorder_batch_replan_wizard_group_plan_dispatch,model_mrp_workorder_batch_replan_wizard,sf_base.group_plan_dispatch,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
11 access_sf_model_type_group_purchase_director sf_model_type_group_purchase_director model_sf_model_type sf_base.group_purchase_director 1 0 0 0
12 access_sf_model_type_group_plan_director sf_model_type_group_plan_director model_sf_model_type sf_base.group_plan_director 1 0 0 0
13 access_sf_product_model_type_routing_sort_group_sf_mrp_user sf_product_model_type_routing_sort model_sf_product_model_type_routing_sort sf_base.group_sf_mrp_user 1 0 0 0
14 access_sf_product_model_type_routing_sort_manager sf_product_model_type_routing_sort model_sf_product_model_type_routing_sort sf_base.group_sf_mrp_manager 1 1 1 1 0
15 access_sf_embryo_model_type_routing_sort_group_sf_mrp_user sf_embryo_model_type_routing_sort model_sf_embryo_model_type_routing_sort sf_base.group_sf_mrp_user 1 0 0 0
16 access_sf_embryo_model_type_routing_sort_manager sf_embryo_model_type_routing_sort model_sf_embryo_model_type_routing_sort sf_base.group_sf_mrp_manager 1 1 1 1 0
17 access_sf_surface_technics_model_type_routing_sort sf_surface_technics_model_type_routing_sort model_sf_surface_technics_model_type_routing_sort sf_base.group_sf_mrp_user 1 0 0 0
18 access_sf_surface_technics_model_type_routing_sort_manager sf_surface_technics_model_type_routing_sort model_sf_surface_technics_model_type_routing_sort sf_base.group_sf_mrp_manager 1 1 1 1 0
19 access_sf_production_line_group_sf_mrp_user sf.production.line model_sf_production_line sf_base.group_sf_mrp_user 1 1 1 0
20 access_sf_production_line_manager sf.production.line model_sf_production_line sf_base.group_sf_mrp_manager 1 1 1 0
21 access_maintenance_equipment_tool_group_sf_mrp_user maintenance_equipment_tool model_maintenance_equipment_tool sf_base.group_sf_mrp_user 1 0 0 0
52 access_mrp_bom_line_manager_group_sf_mrp_user mrp.bom.line.manager mrp.model_mrp_bom_line sf_base.group_sf_mrp_user 1 1 1 0
53 access_mrp_bom_line_group_plan_director mrp_bom_line_group_plan_director mrp.model_mrp_bom_line sf_base.group_plan_director 1 1 1 0
54 access_mrp_bom_line_group_sf_stock_user access_mrp_bom_line_group_sale_director mrp_bom_line_group_sf_stock_user mrp_bom_line_group_sale_director mrp.model_mrp_bom_line sf_base.group_sf_stock_user sf_base.group_sale_director 1 1 1 0
access_mrp_bom_line_group_sale_director mrp_bom_line_group_sale_director mrp.model_mrp_bom_line sf_base.group_sale_director 1 1 1 0
55 access_mrp_bom_line_group_sale_salemanager mrp_bom_line_group_sale_salemanager mrp.model_mrp_bom_line sf_base.group_sale_salemanager 1 0 1 0
56 access_mrp_bom_line_group_purchase_director mrp_bom_line_group_purchase_director mrp.model_mrp_bom_line sf_base.group_purchase_director 1 1 1 0
57 access_mrp_bom_byproduct_manager_group_sf_mrp_user mrp.bom.byproduct manager mrp.model_mrp_bom_byproduct sf_base.group_sf_mrp_user 1 1 1 0
116 access_mrp_workorder_group_quality mrp_workorder model_mrp_workorder sf_base.group_quality 1 1 0 0
117 access_mrp_workorder_group_quality_director mrp_workorder model_mrp_workorder sf_base.group_quality_director 1 1 0 0
118 access_mrp_workorder mrp_workorder model_mrp_workorder sf_base.group_plan_dispatch 1 1 1 0 0
119 access_sf_production_line_group_plan_dispatch sf.production.line model_sf_production_line sf_base.group_plan_dispatch 1 0 0 0
120 access_sf_production_line_group_plan_director sf.production.line model_sf_production_line sf_base.group_plan_director 1 1 1 0
121 access_sf_production_line sf.production.line model_sf_production_line sf_maintenance.sf_group_equipment_user 1 1 1 0
122 access_mrp_workcenter mrp_workcenter model_mrp_workcenter sf_base.group_plan_dispatch 1 1 1 0
165
166
167
access_sf_production_technology_re_adjust_wizard_group_production_engineer sf_production_technology_re_adjust_wizard_group_production_engineer model_sf_production_technology_re_adjust_wizard sf_base.group_production_engineer 1 1 1 0
access_sf_manual_product_model_type_routing_sort_group_sf_mrp_user sf_manual_product_model_type_routing_sort model_sf_manual_product_model_type_routing_sort sf_base.group_sf_mrp_user 1 0 0 0
access_sf_manual_product_model_type_routing_sort_manager sf_manual_product_model_type_routing_sort model_sf_manual_product_model_type_routing_sort sf_base.group_sf_mrp_manager 1 1 1 1
access_sf_manual_product_model_type_routing_sort_group_plan_dispatch sf_manual_product_model_type_routing_sort_group_plan_dispatch model_sf_manual_product_model_type_routing_sort sf_base.group_plan_dispatch 1 0 0 0
access_sf_detection_result_manager sf_detection_result_manager model_sf_detection_result 1 1 1 1
access_mrp_workorder_batch_replan_wizard_group_plan_dispatch mrp_workorder_batch_replan_wizard_group_plan_dispatch model_mrp_workorder_batch_replan_wizard sf_base.group_plan_dispatch 1 1 1 0
168
169
170
171

View File

@@ -1,50 +0,0 @@
odoo.define('sf_manufacturing.agv_scheduling_button_confirm', function (require) {
const core = require('web.core');
const ajax = require('web.ajax');
const Dialog = require('web.Dialog');
var rpc = require('web.rpc');
var _t = core._t;
async function agv_scheduling_cancel_confirm(parent, {params}) {
const dialog = new Dialog(parent, {
title: "确认",
$content: $('<div>').append("工件正在配送中,确定取消"),
buttons: [
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
{ text: "取消", close: true },
],
});
dialog.open();
async function dispatchConfirmed(parent, params) {
rpc.query({
model: 'sf.agv.scheduling',
method: 'button_cancel',
args: [params.agv_scheduling_id],
kwargs: {
context: params.context,
}
}).then(res => {
parent.services.action.doAction({
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '取消成功!',
'type': 'success',
'sticky': false,
'next': {'type': 'ir.actions.act_window_close'},
}
});
parent.services.action.doAction({
'type': 'ir.actions.client',
'tag': 'reload',
});
})
}
}
core.action_registry.add('agv_scheduling_cancel_confirm', agv_scheduling_cancel_confirm);
return agv_scheduling_cancel_confirm;
});

View File

@@ -1,50 +0,0 @@
odoo.define('sf_manufacturing.agv_scheduling_resend_confirm', function (require) {
const core = require('web.core');
const ajax = require('web.ajax');
const Dialog = require('web.Dialog');
var rpc = require('web.rpc');
var _t = core._t;
async function agv_scheduling_resend_confirm(parent, {params}) {
const dialog = new Dialog(parent, {
title: "确认",
$content: $('<div>').append("工件正在配送中,确定重新下发"),
buttons: [
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
{ text: "取消", close: true },
],
});
dialog.open();
async function dispatchConfirmed(parent, params) {
rpc.query({
model: 'sf.agv.scheduling',
method: 'button_resend',
args: [params.agv_scheduling_id],
kwargs: {
context: params.context,
}
}).then(res => {
parent.services.action.doAction({
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '重新下发成功!',
'type': 'success',
'sticky': false,
'next': {'type': 'ir.actions.act_window_close'},
}
});
parent.services.action.doAction({
'type': 'ir.actions.client',
'tag': 'reload',
});
})
}
}
core.action_registry.add('agv_scheduling_resend_confirm', agv_scheduling_resend_confirm );
return agv_scheduling_resend_confirm;
});

View File

@@ -1,115 +0,0 @@
/** @odoo-module **/
import { registry } from '@web/core/registry';
import { Component } from '@odoo/owl';
class QRCodeWidget extends Component {
// 初始化组件
setup() {
console.log('QRCodeWidget setup');
this.qrCodeValue = ''; // 初始化为空字符串,用于存储条码
this.inputBuffer = ''; // 存储临时输入的字符
this.inputTimer = null; // 定时器
// 显式绑定上下文
this.onGlobalKeyDown = this.onGlobalKeyDown.bind(this);
window.addEventListener('keydown', this.onGlobalKeyDown);
}
// 清理事件监听器,防止内存泄漏
willUnmount() {
window.removeEventListener('keydown', this.onGlobalKeyDown);
if (this.inputTimer) {
clearTimeout(this.inputTimer);
}
}
// 全局键盘事件监听器
onGlobalKeyDown(event) {
// 如果是Tab键表示扫码输入结束
if (event.key === 'Tab' || event.key === 'Enter') {
this.qrCodeValue = this.inputBuffer; // 完整条码赋值
console.log('完整条码:', this.qrCodeValue);
this.onQRCodeChange(this.qrCodeValue); // 调用父组件的 onQRCodeChange 方法
this.inputBuffer = ''; // 清空临时缓冲区
event.preventDefault(); // 阻止Tab键的默认行为
return;
}
// 只处理可打印字符
if (event.key.length === 1) {
this.inputBuffer += event.key; // 添加到缓冲区
// console.log('当前缓冲区:', this.inputBuffer);
// 清除之前的定时器,重新开始计时
if (this.inputTimer) {
clearTimeout(this.inputTimer);
}
// 启动一个定时器如果500ms内没有新的输入则认为条码输入完成
this.inputTimer = setTimeout(() => {
this.qrCodeValue = this.inputBuffer;
// console.log('定时器触发,完整条码:', this.qrCodeValue);
this.inputBuffer = ''; // 清空缓冲区
}, 500); // 可以根据需要调整时间
}
}
// 处理二维码输入变更
async onQRCodeChange(qrCodeValue) {
console.log('onQRCodeChange二维码输入变更', qrCodeValue); // 检查二维码的输入是否被捕获
if (qrCodeValue) {
// console.log('二维码输入变更');
try {
// 发起 RPC 请求
const result = await this.env.services.rpc('/web/dataset/call_kw', {
model: 'mrp.workorder',
method: 'search_read',
args: [
[['rfid_code', '=', qrCodeValue]], // 查询条件
['id'] // 返回的字段
],
kwargs: {}
});
if (result.length > 0) {
console.log('该二维码对应的工单存在!');
} else {
console.log('未找到对应的工单。');
const routingTypeField = document.querySelector('[name="routing_type"]');
if (routingTypeField) {
let fieldValue = routingTypeField.querySelector('span').getAttribute('raw-value');
console.log('Routing Type Value:', fieldValue);
// 清理多余的引号
fieldValue = fieldValue ? fieldValue.replace(/["]+/g, '') : null;
console.log(fieldValue);
if (fieldValue && fieldValue === '装夹预调') {
// console.log('routing_type 为装夹预调');
// 检查 RFID 值
if (!qrCodeValue || qrCodeValue.length <= 3) return;
// 查找 name="button_start" 按钮并触发点击事件
const startButton = document.querySelector('[name="button_start"]');
if (startButton) {
startButton.click();
}
}
}
}
} catch (error) {
console.error('查询工单时出错:', error);
}
}
}
// 返回模板名称
static template = 'sf_manufacturing.QRCodeWidgetTemplate';
}
// 将自定义字段注册到字段注册表
registry.category('fields').add('qrcode_widget', QRCodeWidget);

View File

@@ -22,7 +22,8 @@ odoo.define('sf_manufacturing.action_dispatch_confirm', function (require) {
rpc.query({
model: 'sf.workpiece.delivery.wizard',
method: 'confirm',
args: [params.active_id],
args: [params.active_id]
,
kwargs: {
context: params.context,
}

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<t t-name="sf_manufacturing.QRCodeWidgetTemplate" owl="1">
<!-- <div> -->
<!-- <input type="text" t-att-value="props.value" placeholder="Scan QR code here" /> -->
<!-- </div> -->
<div t-esc="props.value">
</div>
</t>
</templates>

Some files were not shown because too many files have changed in this diff Show More