Compare commits

..

2 Commits

Author SHA1 Message Date
liaodanlong
77c303e1fe Merge remote-tracking branch 'origin/develop' into develop 2024-11-05 10:59:36 +08:00
liaodanlong
ba9ce1f0ee 刀具标准库图片相关的参数更新 2024-11-05 10:58:46 +08:00
149 changed files with 1342 additions and 5865 deletions

View File

@@ -139,7 +139,6 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
owl.onMounted(() => {
this.activeElement = this.uiService.activeElement;
this.setRequired()
this.listherHeaderBodyNum()
})
return this._super(...arguments);
},
@@ -166,26 +165,6 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
} catch (e) {
console.log(e)
}
},
listherHeaderBodyNum() {
const dom = this.tableRef.el
try {
const thead = $(dom).children('thead')
const tbody = $(dom).children('tbody')
const thead_tr = thead.children().eq(0)
const tbody_tr = tbody.children().eq(0)
const thead_th_num = thead_tr.children().length
const tbody_tr_num = tbody_tr.children().length
const num = thead_th_num - tbody_tr_num
if(num == -1) {
tbody.children('tr').each(function () {
$(this).children('td').eq(0).remove()
})
}
} catch (e) {
console.log(e)
}
}
})

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,42 +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', 'purchase_tier_validation', 'documents', 'purchase_request', 'account', 'purchase_order_approved'],
# 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',
],
'assets': {
'web.assets_backend': [
'jikimo_purchase_tier_validation/static/src/js/ir_model_extend.js',
],
},
}

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,215 +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']
_description = "采购订单"
_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(_('此操作需要至少对一条记录进行审批。\n请发起审批申请。'))
if record.state in ['to approve']:
raise ValidationError(_('请先完成审批。'))
if record.state == 'approved':
record.state = 'purchase'
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))
# 添加通知消息
if hasattr(record, 'message_post'):
current_user = self.env.user.name
record.message_post(
body=f"<strong>{current_user}</strong> 提交审批",
message_type='notification',
subtype_xmlid='mail.mt_note'
)
res = super(jikimo_purchase_tier_validation, self).request_validation()
self.state = 'to approve'
return res
def restart_validation(self):
res = super(jikimo_purchase_tier_validation, self).restart_validation()
self.state = 'draft'
return res
def _validate_tier(self, tiers=False):
res = super(jikimo_purchase_tier_validation, self)._validate_tier(tiers)
tier_reviews = tiers or self.review_ids
# 检查是否所有审批都已通过
all_approved = all(
tier_review.status == 'approved'
for tier_review in tier_reviews
)
if all_approved and tier_reviews: # 确保有审批记录
self.state = 'approved'
return res
def _rejected_tier(self, tiers=False):
res = super(jikimo_purchase_tier_validation, self)._rejected_tier(tiers)
self.state = 'draft'
return res
@api.model
def _get_under_validation_exceptions(self):
res = super(jikimo_purchase_tier_validation, self)._get_under_validation_exceptions()
res.append("state")
return res
# 上传合同文件
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,
}
}
class jikimo_purchase_request(models.Model):
_inherit = 'purchase.request'
_description = "采购申请"
class jikimo_account_payment(models.Model):
_inherit = 'account.payment'
_description = "付款单"
class jikimo_account_move(models.Model):
_inherit = 'account.move'
_description = "发票账单"

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,14 +0,0 @@
/** @odoo-module **/
import {registerPatch} from "@mail/model/model_core";
registerPatch({
name: "ir.model.review",
fields: {
availableWebViews: {
compute() {
return ["list", "form", "activity"];
},
},
},
});

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,98 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record model="ir.ui.view" id="tier_validation_view_approved_purchase_order_form_inherit">
<field name="name">tier_validation_view_approved_purchase_order_form_inherit</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase_order_approved.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='button_release']" position="replace">
</xpath>
</field>
</record>
<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_approve']" position="replace">
</xpath>
<xpath expr="//header/button[@name='button_cancel']" position="replace">
</xpath>
<xpath expr="//header/field[@name='state']" position="replace">
<field name="state" widget="statusbar" statusbar_visible="draft,sent,to approve, approved, purchase" readonly="1"/>
</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), ('state', 'not in', ['draft', 'sent'])]}"/>]}"/>
<button name="delete_contract_file" string="删除合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', False)]}"/>
</xpath>
<xpath expr="//notebook/page[1]" position="before">
<page string="合同" name="contract_documents"
attrs="{'invisible': [('contract_document_id', '=', False)]}"
autofocus="autofocus">
<group>
<group>
<field name="contract_document_id" invisible="1"/>
<field name="contract_file_name" invisible="1"/>
<field name="contract_file"
widget="adaptive_viewer"
filename="contract_file_name"/>
</group>
</group>
</page>
</xpath>
</field>
</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,2 +0,0 @@
from . import upload_file_wizard
from . import comment_wizard

View File

@@ -1,15 +0,0 @@
from odoo import models, fields
class CommentWizard(models.TransientModel):
_inherit = "comment.wizard"
def add_comment(self):
rec = self.env[self.res_model].browse(self.res_id)
self.review_ids = rec.review_ids
result = super(CommentWizard, self).add_comment()
return result

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, 'price': seller.price}) 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

@@ -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

@@ -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

@@ -8,7 +8,7 @@
'sequence': 120,
'summary': 'Control the quality of your products',
'website': 'https://www.odoo.com/app/quality',
'depends': ['quality', 'sf_manufacturing'],
'depends': ['quality'],
'description': """
Quality Control
===============

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([
@@ -159,34 +152,6 @@ class QualityCheck(models.Model):
is_lot_tested_fractionally = fields.Boolean(related='point_id.is_lot_tested_fractionally')
testing_percentage_within_lot = fields.Float(related="point_id.testing_percentage_within_lot")
product_tracking = fields.Selection(related='product_id.tracking')
quality_check_type = fields.Selection([
('采购入库检', '采购入库检'),
('客供料入库检', '客供料入库检'),
('退货入库检', '退货入库检'),
('生产入库检', '生产入库检'),
('外协入库检', '外协入库检'),
('成品发货检', '成品发货检'),
('工序外协发货检', '工序外协发货检'),
('委外坯料发货检', '委外坯料发货检')], string='类型', compute='_compute_quality_check_type', store=True)
@api.depends('picking_id')
def _compute_quality_check_type(self):
for check in self:
if check.picking_id:
picking_type = check.picking_id.picking_type_id.sequence_code
type_mapping = {
'IN': '采购入库检',
'DL': '客供料入库检',
'RET': '退货入库检',
'SFP': '生产入库检',
'OCIN': '外协入库检',
'OUT': '成品发货检',
'OCOUT': '工序外协发货检',
'RES': '委外坯料发货检',
}
check.quality_check_type = type_mapping.get(picking_type, False)
else:
check.quality_check_type = False
@api.depends('measure_success')
def _compute_warning_message(self):
@@ -329,19 +294,6 @@ class QualityAlert(models.Model):
_inherit = "quality.alert"
title = fields.Char('Title')
part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True)
part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
@api.depends('product_id', 'picking_id')
def _compute_part_info(self):
for alert in self:
if alert.product_tmpl_id.categ_id.name == '成品':
alert.part_number = alert.product_id.part_number
alert.part_name = alert.product_id.part_name
elif alert.product_id.categ_id.name == '坯料':
if alert.picking_id.move_ids_without_package:
alert.part_number = alert.picking_id.move_ids_without_package[0].part_number
alert.part_name = alert.picking_id.move_ids_without_package[0].part_name
def action_see_check(self):
return {

View File

@@ -90,8 +90,6 @@
<field name="lot_id" context="{'default_product_id': product_id}"
groups="stock.group_production_lot"/>
<field name="picking_id"/>
<field name="part_name"/>
<field name="part_number"/>
</group>
<group>
<field name="team_id"/>
@@ -152,10 +150,6 @@
<field name="date_assign" position="after">
<field name="company_id" groups="base.main_company"/>
</field>
<field name="product_tmpl_id" position="after">
<field name="part_name" optional="show"/>
<field name="part_number" optional="show"/>
</field>
</field>
</record>
@@ -209,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
@@ -266,8 +260,6 @@
<field name="company_id" invisible="1"/>
<field name="product_id" attrs="{'invisible' : [('measure_on', '=', 'operation')]}"/>
<field name="measure_on" attrs="{'readonly': [('point_id', '!=', False)]}"/>
<field name="part_name"/>
<field name="part_number"/>
<field name="show_lot_text" invisible="1"/>
<field name="move_line_id" invisible="1"/>
<field name="product_tracking" invisible="1"/>
@@ -397,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"/>
@@ -456,10 +446,6 @@
<filter string="Control Point" name="groupby_point_id" context="{'group_by': 'point_id'}"/>
<filter string="Team" name="groupby_team_id" context="{'group_by': 'team_id'}"/>
</group>
<searchpanel>
<field name="quality_check_type" icon="fa-filter" enable_counters="1"/>
<field name="quality_state" icon="fa-filter" enable_counters="1"/>
</searchpanel>
</search>
</field>
</record>
@@ -519,7 +505,6 @@
<field name="name">Quality Checks</field>
<field name="res_model">quality.check</field>
<field name="view_mode">tree,kanban,form,pivot,graph</field>
<field name="context">{'is_web_request': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No quality check found

View File

@@ -402,22 +402,3 @@ class MachiningAccuracy(models.Model):
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,9 +157,7 @@ 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))
result.append((parameter.id, name))
return result
# 获取表面工艺的获取方式

View File

@@ -1,6 +1,6 @@
<odoo>
<data>
<record id="group_quality" model="res.groups">
<record id="group_quality" model="res.groups">
<field name="name">质检岗</field>
<field name="category_id" ref="base.module_category_manufacturing_quality"/>
</record>
@@ -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

@@ -251,6 +251,3 @@ access_sf_cutting_tool_type_group_plan_dispatch,sf_cutting_tool_type_group_plan_
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
251
252
253

View File

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

View File

@@ -633,26 +633,4 @@
<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,18 @@
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"
sequence="1"
action="action_sf_machining_accuracy"/>
<menuitem
id="menu_sf_machine_control_system"
parent="menu_sf_base"
name="数控系统"
sequence="4"
sequence="2"
action="action_sf_machine_control_system"/>

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

@@ -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,8 @@
<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='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 +63,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)]}"/>
@@ -116,13 +110,15 @@
</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 string="2D加工图纸">
<field name='machining_drawings' attrs="{'invisible': [('categ_type', '!=', '成品')]}"
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 string="质检标准">
<field name='quality_standard' attrs="{'invisible': [('categ_type', '!=', '成品')]}"
widget="adaptive_viewer"/>
</page>
</xpath>

View File

@@ -23,16 +23,6 @@ class JkmPracticeEmployee(models.Model):
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:

View File

@@ -25,7 +25,7 @@
# 只有它被屏蔽了
# 'views/SfWorkOrderBarcodes.xml',
'views/WorkCenterBarcodes.xml',
# 'views/Stock_picking_Barcodes.xml',
'views/Stock_picking_Barcodes.xml',
'views/machine_monitor.xml',
'views/machine_info_present.xml',
'views/delivery_record.xml',

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

@@ -689,8 +689,6 @@ 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)
equipment.next_action_date = next_date
else:
@@ -737,8 +735,6 @@ 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)
equipment.overhaul_date = next_date
else:

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, _
@@ -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)

View File

@@ -139,7 +139,7 @@
<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_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

@@ -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','jikimo_attachment_viewer'],
'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,8 +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,21 +0,0 @@
#. module: sf_manufacturing
#. odoo-python
#: code:addons/sf_manufacturing/models/mrp_production.py:0
#, python-format
msgid "You must enter a serial number for %s"
msgstr "您必须为%s输入一个序列号。"
#. module: sf_manufacturing
#. odoo-python
#: code:addons/sf_manufacturing/models/mrp_production.py:0
#, python-format
msgid "You must enter a serial number for each line of %s"
msgstr "您必须为以下各%s行输入序列号"
#. module: sf_manufacturing
#. odoo-python
#: code:addons/sf_manufacturing/models/mrp_production.py:0
#, python-format
msgid "You have %s incomplete supplies: %s"
msgstr "您有%s补给未完成: %s"

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 = '配送中'
@@ -274,45 +264,7 @@ class AgvScheduling(models.Model):
'task_delivery_time': fields.Datetime.now()
})
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,29 +34,6 @@ 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:
@@ -82,95 +57,6 @@ class MrpProduction(models.Model):
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
@api.depends('workorder_ids.tool_state_remark')
def _compute_tool_state_remark(self):
for item in self:
@@ -212,7 +98,6 @@ class MrpProduction(models.Model):
# ])
state = fields.Selection([
('draft', '草稿'),
('technology_to_confirmed', '待工艺确认'),
('confirmed', '待排程'),
('pending_cam', '待加工'),
('progress', '加工中'),
@@ -263,8 +148,6 @@ class MrpProduction(models.Model):
quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True)
part_name = fields.Char(string='零件名称', related='product_id.part_name', readonly=True)
@api.depends('product_id.manual_quotation')
def _compute_manual_quotation(self):
for item in self:
@@ -275,8 +158,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 +184,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 +214,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:
production.state = 'confirmed'
# # 新添加的状态逻辑
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 +452,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,53 +489,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)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(
production, route, product_production_process.seller_ids[0].partner_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_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]
sorted_workorders = None
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'})
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
if not sorted_workorders:
return
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)
is_pick = False
consecutive_workorders = []
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):
@@ -875,87 +671,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 work.name == '人工线下加工':
td_ids = technology_design_ids.filtered(lambda item: (item.route_id.name in work.name))
if td_ids:
work.sequence = td_ids[0].sequence
cancel_work_ids = workorder_ids.filtered(lambda item: item.state in ('已取消', 'cancel'))
if cancel_work_ids:
sequence = max(workorder_ids.filtered(lambda item: item.state not in ('已取消', 'cancel')),
key=lambda w: w.sequence).sequence
for cw in cancel_work_ids:
cw.sequence = sequence + 1
def _reset_work_order_sequence_1(self):
"""
工单工序排序方法(旧)
"""
for rec in self:
workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework'))
# 产品模型类型
@@ -1051,32 +767,54 @@ 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_finished = datetime.datetime.today() + datetime.timedelta(days=100)
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):
@@ -1100,7 +838,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:
@@ -1201,16 +938,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',
@@ -1219,8 +946,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
@@ -1374,15 +1099,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':
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
elif mo.location_id.barcode == 'PL' and mo.rule_id.picking_type_id.barcode == 'INT':
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
if domain:
if mo.procure_method == 'make_to_order' and mo.name != productions.name:
if mo.name == '/':
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
elif mo.name == '':
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
picking_type = self.env['stock.picking.type'].search(domain)
mo.write({'picking_type_id': picking_type.id})
mo._assign_picking()
@@ -1400,6 +1123,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)])
@@ -1433,6 +1157,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 = '编程中'
@@ -1462,145 +1187,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', '=', first_order.procurement_group_id.id), ('group_id', '!=', False),
])
order.delivery_count = len(first_order.picking_ids)
else:
order.picking_ids = self.env['stock.picking'].search([
('group_id', '=', order.procurement_group_id.id), ('group_id', '!=', False),
])
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
def _subcontract_sanity_check(self):
for production in self:
if production.product_tracking != 'none' and not self.lot_producing_id:
raise UserError(_('You must enter a serial number for %s') % production.product_id.name)
for sml in production.move_raw_ids.move_line_ids:
if sml.tracking != 'none' and not sml.lot_id:
picking_ids = production.picking_ids.filtered(
lambda p: p.state not in ['done', 'cancel'])
picking_num = len(picking_ids)
picking_info = ', '.join(
['%s:%s' % (picking.picking_type_id.name, picking.name) for picking in picking_ids]
)
if picking_info:
raise UserError(_('You have %s incomplete supplies: %s') % (
picking_num, picking_info))
else:
raise UserError(
_('You must enter a serial number for each line of %s') % sml.product_id.display_name)
return True
class sf_detection_result(models.Model):
_name = 'sf.detection.result'
@@ -1630,6 +1216,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

@@ -17,35 +17,34 @@ from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
_order = 'sequence asc'
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([
@@ -130,7 +129,7 @@ class ResMrpWorkOrder(models.Model):
Y10_axis = fields.Float(default=0)
Z10_axis = fields.Float(default=0)
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
test_results = fields.Selection([("合格", "合格"), ("返工", "返工")], default='合格',
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
string="检测结果", tracking=True)
cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序")
cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序")
@@ -138,11 +137,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')
@@ -150,107 +146,21 @@ 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:
domain += [('surface_technics_parameters_id', '=', workorder.surface_technics_parameters_id.id)]
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)
workorder.picking_ids = picking_ids.ids
@@ -274,73 +184,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', 'like', '%' + 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)
# 保存名称
@@ -356,7 +228,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([
@@ -408,11 +279,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 = ''
tem_name = record.production_id.name.replace('/', '_')
record.save_name = tem_name + '_' + record.processing_panel
schedule_state = fields.Selection(related='production_id.schedule_state', store=True)
# 工件装夹信息
@@ -459,8 +327,7 @@ class ResMrpWorkOrder(models.Model):
@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(_("您不能创建周期性的依赖关系."))
@@ -471,9 +338,8 @@ 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)
workorder._plan_workorder(replan)
start_date = max(start_date, workorder.date_planned_finished)
# Plan only suitable workorders
if self.state not in ['pending', 'waiting', 'ready']:
return
@@ -803,14 +669,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
@@ -818,22 +705,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,
production.product_id),
'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
@@ -864,24 +755,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),
@@ -1033,104 +922,208 @@ 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()
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:
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
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']):
if (workorder.production_id.tool_state in ['1', '2']
and not workorder.production_id.workorder_ids.filtered(lambda a: a.sequence == 0)
and workorder.production_id.programming_state == '编程中' and workorder.name == '装夹预调'):
if 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.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'
# continue
if workorder.technology_design_id.routing_tag == 'special':
if workorder.is_subcontract is False:
workorder.state = 'ready'
else:
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'
# if workorder.routing_type == 'CNC加工' and workorder.state == 'progress':
# workorder.state = 'to be detected'
# for workorder in self:
# 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]):
# workorder.state = 'ready' if workorder.production_id.reservation_state == 'assigned' else 'waiting'
# continue
# if workorder.state not in ('waiting', 'ready'):
# continue
# if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
# workorder.state = 'pending'
# continue
# if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'):
# continue
# 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'
# 重写工单开始按钮方法
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:
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
boolean = True
else:
boolean = True
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
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程序请稍后再试'))
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程序请稍后再试'))
else:
if self.production_id.tool_state in ['1', '2']:
if self.production_id.tool_state == '1':
@@ -1147,33 +1140,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':
@@ -1243,8 +1218,8 @@ class ResMrpWorkOrder(models.Model):
if record.is_rework is False:
if not record.material_center_point:
raise UserError("坯料中心点为空,请检查")
# if record.X_deviation_angle <= 0:
# raise UserError("X偏差角度小于等于0请检查本次计算的X偏差角度为%s" % record.X_deviation_angle)
# if record.X_deviation_angle <= 0:
# raise UserError("X偏差角度小于等于0请检查本次计算的X偏差角度为%s" % record.X_deviation_angle)
record.process_state = '待加工'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待加工'
@@ -1274,17 +1249,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)
@@ -1303,46 +1284,41 @@ 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'
# 解绑托盘
def unbind_tray(self):
for item in self:
self.env['stock.lot'].sudo().search([('rfid', '=', item.rfid_code)]).write(
{'tool_material_status': '可用'})
self.production_id.workorder_ids.write({
'rfid_code': False,
'tray_serial_number': False,
@@ -1458,8 +1434,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'
@@ -1668,7 +1642,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))
@@ -1983,56 +1956,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

@@ -141,11 +141,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 +774,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:
@@ -849,23 +849,16 @@ class ResProductMo(models.Model):
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 +866,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,
@@ -891,7 +884,6 @@ class ResProductMo(models.Model):
'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')])
@@ -906,20 +898,6 @@ class ResProductMo(models.Model):
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 = []
@@ -937,8 +915,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 +932,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,107 +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'
production_count = fields.Integer(
"关联制造订单",
compute='_compute_workorder_count',
)
def action_view_production(self):
origins = [order.name for order in self.picking_ids]
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
if not production_id:
return
action = {
'res_model': 'mrp.production',
'type': 'ir.actions.act_window',
}
if len(production_id) == 1:
action.update({
'view_mode': 'form',
'res_id': production_id.id,
})
else:
action.update({
'name': _("制造订单列表"),
'domain': [('id', 'in', production_id.ids)],
'view_mode': 'tree,form',
})
return action
def _compute_workorder_count(self):
for purchase in self:
origins = [order.name for order in purchase.picking_ids]
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
purchase.production_count = len(production_id)
def button_confirm(self):
super().button_confirm()
workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id), ('state', '!=', 'cancel')])
for workorder in workorders:
if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True:
move_out = workorder.move_subcontract_workorder_ids[1]
for mo in move_out:
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids:
self.env['stock.move.line'].create(mo.get_move_line(workorder.production_id, workorder))
return True
origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id')
origin_sale_ids = fields.Many2many('sale.order', string='销售订单号(多个)', store=True,
compute='_compute_origin_sale_id')
@api.depends('origin')
def _compute_origin_sale_id(self):
for purchase in self:
if not purchase.origin:
continue
elif 'MO' in purchase.origin:
mp_name_list = [name.strip() for name in purchase['origin'].split(',')]
os_ids = list({mp_id.sale_order_id.id for mp_id in self.env['mrp.production'].sudo().search([
('name', 'in', mp_name_list)])})
if len(os_ids) == 1:
purchase.origin_sale_id = os_ids[0]
elif len(os_ids) >= 2:
purchase.origin_sale_ids = os_ids
elif 'S' in purchase.origin:
os_name_list = [name.strip() for name in purchase['origin'].split(',')]
os_ids = self.env['sale.order'].sudo().search([('name', 'in', os_name_list)])
if len(os_ids) == 1:
purchase.origin_sale_id = os_ids.id
elif len(os_ids) >= 2:
purchase.origin_sale_ids = os_ids.ids
elif 'IN' in purchase.origin:
sp_name_list = [name.strip() for name in purchase['origin'].split(',')]
os_ids = list({sp_id.sale_order_id.id for sp_id in self.env['stock.picking'].sudo().search([
('name', 'in', sp_name_list)])})
if len(os_ids) == 1:
purchase.origin_sale_id = os_ids[0]
elif len(os_ids) >= 2:
purchase.origin_sale_ids = os_ids
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,169 +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)
return super(SaleOrder, self).action_confirm()
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,109 +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):
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

@@ -208,15 +208,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,26 +272,18 @@ 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)])
else:
mrp_production = production
# 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,
'product_qty': production.product_qty,
'product_id': production.product_id.id,
'state': 'draft',
})
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': production.origin,
'product_qty': production.product_qty,
'product_id': production.product_id.id,
'state': 'draft',
})
all_production = productions
grouped_product_ids = {k: list(g) for k, g in groupby(all_production, key=lambda x: x.product_id.id)}
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
@@ -310,86 +293,78 @@ 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
return True
class ProductionLot(models.Model):
@@ -423,8 +398,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 +452,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,39 +548,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_order_id = fields.Many2one('sale.order', '销售单号', compute='_compute_move_ids', store=True)
picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code')
@api.depends('move_ids', 'move_ids.product_id')
def _compute_move_ids(self):
for item in self:
if item.move_ids:
product_id = item.move_ids[0].product_id
if product_id:
sale_info = None
if product_id.categ_id.type == '坯料' and product_id.name.startswith('R-S'):
parts = product_id.name.split('-')
if len(parts) >= 3:
sale_name = parts[1]
sale_info = self.env['sale.order'].sudo().search(
[('name', '=', sale_name)])
else:
sale_order_line = self.env['sale.order.line'].sudo().search(
[('product_id', '=', product_id.id)])
if sale_order_line:
sale_info = sale_order_line[0].order_id
if sale_info:
item.sale_order_id = sale_info.id
item.retrospect_ref = sale_info.order_code
if item.picking_type_id.sequence_code == 'DL':
item.person_of_delivery = sale_info.person_of_delivery
item.telephone_of_delivery = sale_info.telephone_of_delivery
item.address_of_delivery = sale_info.address_of_delivery
# 设置外协出入单的名称
def _get_name_Res(self, rescode):
@@ -629,103 +561,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 self.location_id.name == '成品存货区' and self.location_dest_id.name == '客户':
sale_id = self.env['sale.order'].sudo().search(
[('name', '=', self.origin)])
stock_picking_list = self.env['stock.picking'].sudo().search(
[('id', 'in', sale_id.picking_ids.ids)])
stock_picking = stock_picking_list.filtered(lambda p: p.state not in ("done", "cancel"))
if sale_id and not stock_picking:
sale_id.write({'state': 'delivered'})
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': 'cancel'})
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'cancel'})
# 创建一个新的补货组
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(sorted_workorders) 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
new_picking = True
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,
context = dict(self.env.context)
context.update({
'default_production_id': item.id, # 添加额外信息到 context 中
})
moves_in = self.env['stock.move'].sudo().with_context(context).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)
# self.env.context.get('default_production_id')
moves_out = self.env['stock.move'].sudo().with_context(context).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])]})
picking_out = self.create(
moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/'))
# pick_ids.append(picking_out.id)
moves_out.write(
{'picking_id': picking_out.id, 'state': 'waiting'})
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'
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_out = self.env['stock.move'].sudo().create(
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, sorted_workorders, 'WH/OCOUT/'))
# pick_ids.append(picking_out.id)
moves_out.write(
{'picking_id': picking_out.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
moves_out._assign_picking_post_process(new=new_picking)
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):
@@ -734,51 +647,18 @@ class ReStockMove(models.Model):
materiel_length = fields.Float(string='物料长度', digits=(16, 4))
materiel_width = fields.Float(string='物料宽度', digits=(16, 4))
materiel_height = fields.Float(string='物料高度', digits=(16, 4))
part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True)
part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
@api.depends('product_id')
def _compute_part_info(self):
for move in self:
if move.product_id.categ_id.type == '成品':
move.part_number = move.product_id.part_number
move.part_name = move.product_id.part_name
elif move.product_id.categ_id.type == '坯料':
if move.origin:
origin = move.origin.split(',')[0] if ',' in move.origin else move.origin
mrp_productio_info = self.env['mrp.production'].sudo().search(
[('name', '=', origin)])
if mrp_productio_info:
move.part_number = mrp_productio_info.part_number
move.part_name = mrp_productio_info.part_name
else:
purchase_order_info = self.env['purchase.order'].sudo().search(
[('name', '=', origin)])
if purchase_order_info:
mrp_production_ids = purchase_order_info._get_mrp_productions().ids
if mrp_production_ids:
mrp_productio_info = self.env['mrp.production'].sudo().search(
[('id', '=', mrp_production_ids[0])])
if mrp_productio_info:
move.part_number = mrp_productio_info.part_number
move.part_name = mrp_productio_info.part_name
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,
# 'production_id': item.id,
# 'route_ids': False if not route else [(4, route.id)],
'date_deadline': datetime.now(),
'picking_type_id': picking_type_id,
@@ -802,7 +682,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):
@@ -815,7 +695,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',
@@ -859,8 +739,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)
@@ -1013,42 +891,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

@@ -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,24 +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
access_mrp_workorder_group_purchase_director,mrp_workorder,model_mrp_workorder,sf_base.group_purchase_director,1,1,0,0
access_mrp_workorder_group_purchase,mrp_workorder,model_mrp_workorder,sf_base.group_purchase,1,1,0,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
access_mrp_workorder_group_purchase_director mrp_workorder model_mrp_workorder sf_base.group_purchase_director 1 1 0 0
access_mrp_workorder_group_purchase mrp_workorder model_mrp_workorder sf_base.group_purchase 1 1 0 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

@@ -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

@@ -33,20 +33,6 @@
class="btn-danger"
confirm="你确定要取消这条记录吗?"
/>
<button
name="button_cancel_confirm"
string="取消" type="object"
attrs="{'invisible': [('state', '!=', '配送中')]}"
icon="fa-times"
class="btn-danger"
/>
<button
name="button_resend_confirm"
string="重新下发" type="object"
attrs="{'invisible': [('state', '!=', '配送中')]}"
icon="fa-circle-o-notch"
class="btn-success "
/>
</tree>
</field>
</record>

View File

@@ -3,7 +3,7 @@
<data>
#------------------模型类型------------------
<record model="ir.ui.view" id="search_sf_model_name_view">
<record model="ir.ui.view" id="search_sf_model_type_view">
<field name="name">search.sf.model.type</field>
<field name="model">sf.model.type</field>
<field name="arch" type="xml">
@@ -14,7 +14,7 @@
</field>
</record>
<record model="ir.ui.view" id="tree_sf_model_name_view">
<record model="ir.ui.view" id="tree_sf_model_type_view">
<field name="name">tree.sf.model.type</field>
<field name="model">sf.model.type</field>
<field name="arch" type="xml">
@@ -24,31 +24,20 @@
</field>
</record>
<record model="ir.ui.view" id="form_sf_model_name">
<record model="ir.ui.view" id="form_sf_model_type">
<field name="name">form.sf.model.type</field>
<field name="model">sf.model.type</field>
<field name="arch" type="xml">
<form string="模型类型">
<group>
<field name="name" required="1"/>
<field name="embryo_tolerance_id" required="1"/>
<field name="embryo_tolerance" required="1" string="坯料容余(mm)"/>
</group>
<group>
<field name='product_routing_tmpl_ids'>
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.product.model.type.routing.sort'}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
</tree>
</field>
</group>
<group>
<field name='manual_product_routing_tmpl_ids'>
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.manual.product.model.type.routing.sort'}"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
@@ -59,7 +48,7 @@
<field name='embryo_routing_tmpl_ids'>
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.embryo.model.type.routing.sort'}"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
@@ -70,7 +59,7 @@
<field name='surface_technics_routing_tmpl_ids' style="white-space: pre-wrap;">
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.surface_technics.model.type.routing.sort'}"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
@@ -81,7 +70,7 @@
</field>
</record>
<record id="action_sf_model_name" model="ir.actions.act_window">
<record id="action_sf_model_type" model="ir.actions.act_window">
<field name="name">模型类型</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.model.type</field>
@@ -96,11 +85,11 @@
</record>
<menuitem
id="menu_sf_model_name"
id="menu_sf_model_type"
name="模型类型"
parent="mrp.menu_mrp_configuration"
sequence="9"
action="action_sf_model_name"
action="action_sf_model_type"
/>
</data>
</odoo>

View File

@@ -18,12 +18,9 @@
<xpath expr="//field[@name='date_deadline']" position="replace"/>
<xpath expr="//field[@name='name']" position="after">
<field name="product_id" readonly="1" optional="show"/>
<field name="part_name" readonly="1" optional="hide"/>
<field name="part_number" readonly="1" optional="show"/>
</xpath>
<xpath expr="//field[@name='product_id']" position="after">
<field name="product_qty" sum="Total Qty" string="数量" readonly="1" optional="show"/>
<field name="deadline_of_delivery" optional="show"/>
</xpath>
<xpath expr="//field[@name='product_qty']" position="after">
<field name="product_uom_id" string="计量单位" options="{'no_open':True,'no_create':True}"
@@ -38,15 +35,13 @@
<field name="reservation_state" optional="hide" decoration-danger="reservation_state == 'confirmed'"
decoration-success="reservation_state == 'assigned'"/>
</xpath>
<xpath expr="//field[@name='state']" position="before">
<field name="production_type" widget="badge" decoration-warning="production_type == '人工线下加工'"
decoration-success="production_type == '自动化产线加工'" optional="show"/>
</xpath>
<!-- <xpath expr="//field[@name='state']" position="before"> -->
<!-- <field name="schedule_state" optional="show"/> -->
<!-- </xpath> -->
<xpath expr="//field[@name='activity_ids']" position="replace">
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
</xpath>
<xpath expr="//field[@name='origin']" position="replace">
<field name="sale_order_id" optional="show"/>
<field name="origin" optional="hide"/>
</xpath>
<xpath expr="//field[@name='components_availability']" position="replace">
@@ -61,16 +56,6 @@
<!-- <button name="action_view_production_schedule" string="生产排程" type="object" attrs="{'invisible': [('state', 'in', ['draft', 'cancel','已排程','progress','done','to_close'])]}"/> -->
<!-- <button name="cancel_plan" string="取消排程" type="object" attrs="{'invisible': [('state', 'in', ['draft', 'cancel','progress','done','to_close','confirmed'])]}"/> -->
<!-- </xpath> -->
<xpath expr="//field[@name='production_real_duration']" position="before">
<field name="delivery_status" optional="show" widget="badge"
decoration-success="delivery_status == '正常'"
decoration-warning="delivery_status == '预警'"
decoration-danger="delivery_status == '已逾期'"/>
</xpath>
<xpath expr="//field[@name='production_real_duration']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
</field>
</record>
@@ -85,15 +70,9 @@
<!-- <attribute name="statusbar_visible">draft,confirmed,progress,pending_processing,completed,done -->
<!-- </attribute> -->
<attribute name="statusbar_visible">
technology_to_confirmed,confirmed,pending_cam,progress,rework,scrap,done
confirmed,pending_cam,progress,rework,scrap,done
</attribute>
</xpath>
<xpath expr="//div[@class='d-flex flex-row align-items-start']//span[1]" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//button[@name='action_product_forecast_report']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//sheet//group//group[2]//label" position="before">
<!-- <field name="process_state"/> -->
<field name="state" readonly="1"/>
@@ -101,13 +80,9 @@
</xpath>
<xpath expr="//sheet//group//group//div[3]" position="after">
<field name="production_type" readonly="1"/>
<field name="manual_quotation" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
<field name="programming_no" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
<field name="manual_quotation" readonly="1"/>
<field name="programming_no" readonly="1"/>
<field name="programming_state" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"
decoration-success="programming_state == '已编程'"
decoration-warning="programming_state =='编程中'"
decoration-danger="programming_state =='已编程未下发'"/>
@@ -122,15 +97,17 @@
<xpath expr="//field[@name='user_id']" position="after">
<field name="production_line_id" readonly="1"/>
<!-- <field name="production_line_state" readonly="1"/>-->
<field name="part_name"/>
<field name="part_number" string="零件图号"/>
<field name="tool_state"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
<field name="part_number" string="成品的零件图号"/>
<field name="tool_state"/>
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
<field name="sale_order_id" readonly="1"/>
<field name="deadline_of_delivery" readonly="1"/>
<field name="tool_state_remark2" invisible="1"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', True)]}"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="(//header//button[@name='button_mark_done'])[1]" position="replace">
<button name="button_mark_done"
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '!=', [])]}"
@@ -145,10 +122,6 @@
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="(//header//button[@name='button_scrap'])" position="replace">
<button name="technology_confirm" string="工艺确认" type="object" class="oe_highlight"
attrs="{'invisible': [('state', '!=', 'technology_to_confirmed')]}"></button>
<button name="technology_back_adjust" string="退回调整" type="object" class="oe_highlight"
attrs="{'invisible': [('state', '!=', 'confirmed')]}"></button>
<button name="button_scrap" invisible="1"/>
<button name="do_update_program" string="更新程序" type="object" groups="sf_base.group_sf_mrp_user"
confirm="是否确认更新程序"
@@ -255,6 +228,13 @@
type="object" groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="Cancel" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', False)]}"
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
<button name="button_unbuild" type="object" string="拆单"
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
@@ -298,14 +278,12 @@
type="object" groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel'][2]" position="replace">
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'rework', 'cancel')), ('confirm_cancel', '=', False)]}"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', False)]}"
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel'][1]" position="replace"></xpath>
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
<button name="button_unbuild" type="object" string="拆单"
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
@@ -313,7 +291,8 @@
</xpath>
<xpath expr="//sheet//notebook//page[@name='operations']" position="attributes">
<attribute name="attrs">{'invisible': [('workorder_ids', '=', [])]}
<attribute name="attrs">{'invisible': ['|',('schedule_state', '=', '未排'),('workorder_ids', '=',
[])]}
</attribute>
</xpath>
@@ -354,48 +333,10 @@
<field name="part_drawing" widget="adaptive_viewer"/>
</page>
</xpath>
<xpath expr="//sheet//notebook//page[@name='components']" position="before">
<page string="工艺设计" attrs="{'invisible': [('state', '!=', 'technology_to_confirmed')]}">
<field name="technology_design_ids" widget="one2many">
<tree editable="bottom">
<field name="sequence" widget="handle"/>
<field name="route_id" context="{'production_id': production_id}"
attrs="{'readonly': [('id', '!=', False)]}" options="{'no_create': True}"/>
<field name="process_parameters_id"
attrs="{'readonly': [('id', '!=', False),('routing_tag', '=', 'standard')]}"
string="参数" context="{'route_id':route_id,'production_id': production_id}"
options="{'no_create': True}"/>
<field name="panel" readonly="1"/>
<field name="routing_tag" readonly="1" widget="badge"
decoration-success="routing_tag == 'standard'"
decoration-warning="routing_tag == 'special'"/>
<field name="time_cycle_manual" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="is_auto" invisible="1"/>
<field name="production_id" invisible="1"/>
<button name="unlink_technology_design" confirm="是否确认删除?" class="oe_highlight"
attrs="{'invisible': [('is_auto', '=', True)]}" type="object"
string="删除"></button>
</tree>
</field>
</page>
</xpath>
<xpath expr="//sheet//notebook" position="inside">
<page string="质检标准">
<field name="quality_standard" widget="adaptive_viewer"/>
</page>
</xpath>
<xpath expr="//sheet/group/group/div[@class='d-flex flex-row align-items-start']/span[last()]"
position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//sheet/group/group/div[@class='d-flex flex-row align-items-start']/button[@name='action_product_forecast_report']"
position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//sheet/div[@class='oe_button_box']/button[@name='action_view_mrp_production_childs']/div/span[last()]"
position="replace">
<span class="o_stat_text">子MO</span>
</xpath>
</field>
</record>
@@ -434,7 +375,9 @@
<xpath expr="//tree//button[@name='button_start']" position="replace">
<field name="routing_type" invisible="True"/>
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始?"
attrs="{'invisible': [('state', '!=', 'ready')]}"
attrs="{'invisible': ['|', '|', '|','|', ('production_state','in', ('draft', 'done', 'cancel')),
('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=',
False), ('routing_type', '=', 'CNC加工')]}"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//tree//button[@name='button_pending']" position="replace">
@@ -574,10 +517,8 @@
<separator/>
</xpath>
<xpath expr="//search" position="inside">
<searchpanel>
<searchpanel class="account_root">
<field name="state" icon="fa-filter" enable_counters="1"/>
<field name="delivery_status" icon="fa-filter" enable_counters="1"/>
<field name="production_type" icon="fa-filter" enable_counters="1"/>
</searchpanel>
</xpath>
<filter name='todo' position="replace"/>
@@ -732,19 +673,5 @@
<!-- parent="mrp.menu_mrp_manufacturing"-->
<!-- sequence="1"/>-->
<menuitem id="mrp.menu_mrp_production_action"
name="制造订单"
parent="mrp.menu_mrp_manufacturing"
action="mrp.mrp_production_action"
groups="sf_base.group_plan_dispatch,sf_base.group_sf_mrp_manager"
sequence="1"/>
<menuitem id="stock_picking_type_menu"
name="驾驶舱"
parent="stock.menu_stock_root"
action="stock.stock_picking_type_action"
groups="sf_base.group_sf_stock_user"
sequence="0"/>
</data>
</odoo>

View File

@@ -16,7 +16,6 @@
</field>
<field name="bom_product_template_attribute_value_ids" position="after">
<field name="routing_type" required="1"/>
<field name="routing_tag" required="1" string="工序标签"/>
<field name="is_repeat"/>
<field name="reserved_duration"/>
</field>

View File

@@ -1,18 +0,0 @@
<odoo>
<data>
<record id="mrp_production_workorder_tree_editable_view_inherit" model="ir.ui.view">
<field name="name">>mrp.workorder.tree.editable.inherit</field>
<field name="model">mrp.workorder</field>
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
<field name="arch" type="xml">
<xpath expr="//tree/field[1]" position="before">
<header>
<button string="重新安排" name="%(sf_manufacturing.mrp_workorder_batch_replan_wizard)d" type="action"
class="treeHeaderBtn"
invisible="context.get('workorder_type') not in ('工件装夹中心','1#自动生产线','工件拆卸中心')"/>
</header>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -29,16 +29,11 @@
</field>
<field name="product_id" position="after">
<field name="equipment_id" optional="hide"/>
<field name="part_name" optional="hide"/>
<field name="part_number" optional="show"/>
</field>
<xpath expr="//field[@name='qty_remaining']" position="after">
<field name="manual_quotation" optional="show"/>
<field name="tag_type" optional="show"/>
<field name="construction_period_status" optional="show" widget="badge"
decoration-success="construction_period_status == '正常'"
decoration-warning="construction_period_status == '预警'"
decoration-danger="construction_period_status == '已逾期'"/>
<field name='tag_type' widget="badge"
decoration-danger="tag_type == '重新加工'"/>
</xpath>
<xpath expr="//field[@name='date_planned_start']" position="replace">
<field name="date_planned_start" string="计划开始日期" optional="show"/>
@@ -55,7 +50,10 @@
<!-- 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),-->
<!-- ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}-->
<!-- </attribute>-->
<attribute name="attrs">{'invisible': [('state', '!=', 'ready')]}
<attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
'done',
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
('is_user_working', '!=', False),("user_permissions","=",False),("name","in",("CNC加工","解除装夹"))]}
</attribute>
</xpath>
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
@@ -103,7 +101,8 @@
<!-- <field name="target">fullscreen</field>-->
<field name="target">current</field>
<field name="domain">[('state', '!=', 'cancel'),('schedule_state', '=', '已排')]</field>
<field name="context">{'search_default_product': 1, 'search_default_workcenter_id': active_id}
<field name="context">{'search_default_product': 1, 'search_default_workcenter_id':
active_id,'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1,'search_default_filter_order_normal':1}
</field>
<field name="help" type="html">
<p class="o_view_nocontent_workorder">
@@ -137,7 +136,7 @@
<button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button"
icon="fa-credit-card"
groups="base.group_user,sf_base.group_sf_order_user"
attrs="{'invisible': [('surface_technics_purchase_count', '=', 0)]}">
attrs="{'invisible': [('surface_technics_purchase_count', '=', 0),('routing_type', '!=', '表面工艺')]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="surface_technics_purchase_count"/>
@@ -173,7 +172,9 @@
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
attrs="{'invisible': [('state', '!=', 'ready')]}"/>
attrs="{'invisible': ['|', '|', '|', '|', '|', ('routing_type', '=', '装夹预调'), ('routing_type', '=', '解除装夹'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
<button name="button_start" type="object" string="开始" class="btn-success"
attrs="{'invisible': ['|', '|', '|', '|', ('routing_type', '!=', '装夹预调'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
<button name="button_pending" type="object" string="暂停" class="btn-warning"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
@@ -196,7 +197,7 @@
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
<button name="button_rework_pre" type="object" string="异常反馈"
<button name="button_rework_pre" type="object" string="返工"
class="btn-primary"
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
<button name="unbind_tray" type="object" string="解绑托盘"
@@ -206,7 +207,7 @@
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
</xpath>
<xpath expr="//page[1]" position="before">
<page string="开料要求" attrs='{"invisible": [("routing_type","not in",("切割", "线切割", "人工线下加工"))]}'>
<page string="开料要求" attrs='{"invisible": [("routing_type","!=","切割")]}'>
<group>
<group>
<field name="product_tmpl_id_materials_id" widget="many2one"/>
@@ -247,8 +248,8 @@
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
decoration-danger="tag_type == '重新加工'"/>
<field name="is_test_env" invisible="1"/>
<field name="rfid_code" force_save="1" readonly="1" cache="True"
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget="qrcode_widget"/>
<field name="rfid_code" force_save="1" readonly="1" cache="True"
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget='qrcode_widget'/>
<field name="rfid_code" string="RFID码(手动输入框)" force_save="1" readonly="0" cache="True"
attrs="{'invisible': ['|',('rfid_code_old', '!=', False), ('is_test_env', '=', False)]}"/>
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
@@ -256,12 +257,11 @@
</xpath>
<xpath expr="//header" position="inside">
<div class="o_statusbar_buttons">
<button name="button_change_env"
type="object"
string="演示模式"
class="btn-primary"
groups="sf_manufacturing.group_show_button"/>
<div class="o_statusbar_buttons"><button name="button_change_env"
type="object"
string="演示模式"
class="btn-primary"
groups="sf_manufacturing.group_show_button"/>
</div>
</xpath>
@@ -281,8 +281,7 @@
<label for="material_height" string="高"/>
<field name="material_height" class="o_address_zip"/>
</div>
<field name="part_name"/>
<field name="part_number" string="零件图号"/>
<field name="part_number" string="成品的零件图号"/>
</xpath>
<xpath expr="//label[1]" position="attributes">
<attribute name="string">计划加工时间</attribute>
@@ -482,11 +481,11 @@
</group>
</page>
<page string="2D加工图纸" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
<page string="2D加工图纸" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
<field name="machining_drawings" widget="adaptive_viewer"/>
</page>
<page string="质检标准" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
<page string="质检标准" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
<field name="quality_standard" widget="adaptive_viewer"/>
</page>
@@ -543,11 +542,11 @@
<!-- attrs='{"invisible": ["|","|",("state","!=","progress"),("user_permissions","=",False),("results","=","合格")]}'/>-->
<!-- </div>-->
</page>
<page string="2D加工图纸" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<page string="2D加工图纸" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<field name="machining_drawings" widget="adaptive_viewer"/>
</page>
<page string="质检标准" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<page string="质检标准" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<field name="quality_standard" widget="adaptive_viewer"/>
</page>
</xpath>

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record model="ir.ui.view" id="view_purchase_order_line_form_inherit_sf1">
<field name="name">purchase.order.form.inherit.sf</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="after">
<field name="related_product" optional="show"/>
<field name="part_number" optional="show"/>
</xpath>
<xpath expr="//sheet//div[@class='oe_button_box']" position="inside">
<button class="oe_stat_button" name="action_view_production" type="object" icon="fa-wrench"
attrs="{'invisible': [('production_count', '=', 0)]}"
>
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="production_count"/>
</span>
<span class="o_stat_text">制造订单</span>
</div>
</button>
</xpath>
<!-- 添加销售订单号字段-->
<xpath expr="//sheet/group/group[2]/div[@name='date_approve']" position="after">
<field name="origin_sale_id" readonly="1" string="参考销售订单"
attrs="{'invisible': [('origin_sale_id' , '=', False)]}"/>
<field name="origin_sale_ids" readonly="1" string="参考销售订单" widget="many2many_tags"
attrs="{'invisible': [('origin_sale_ids' , '=', [])]}"/>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -1,98 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_order_form_inherit_supply_method" model="ir.ui.view">
<field name="name">view.sale.order.form.inherit.supply.method</field>
<field name="inherit_id" ref="sf_sale.view_sale_order_form_inherit_sf"/>
<field name="model">sale.order</field>
<field name="arch" type="xml">
<xpath expr="//header/button[@name='action_confirm'][last()]" position="attributes">
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
<attribute name="name">confirm_to_supply_method</attribute>
</xpath>
<xpath expr="//header/button[@name='confirm_to_supply_method']" position="before">
<button name="action_confirm" string="供货方式确认" type="object" attrs="{'invisible': [('state', '!=', 'supply method')]}" confirm="确认供货方式"/>
</xpath>
<xpath expr="//header/field[@name='state']" position="attributes">
<attribute name="statusbar_visible">supply method,sale,processing,physical_distribution,delivered</attribute>
</xpath>
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='model_glb_file']" position="before">
<field name="part_number" optional="show"/>
</xpath>
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
</xpath>
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
</xpath>
<xpath expr="//header/button[@name='action_quotation_send'][5]" position="attributes">
<attribute name="attrs">{'invisible': ['|','&amp;',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel','supply method']),'&amp;',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel','supply method'])]}</attribute>
</xpath>
</field>
</record>
<record id="jikimo_sale_order_view_search_inherit_quotation_supply_method" model="ir.ui.view">
<field name="name">jikimo.sale.order.search.inherit.quotation.supply.method</field>
<field name="model">sale.order</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="sale.sale_order_view_search_inherit_quotation"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='my_quotation']" position="attributes">
<attribute name="domain">[('state', 'in', ('draft', 'sent')), ('user_id', '=', uid)]</attribute>
</xpath>
<xpath expr="//filter[@name='draft']" position="after">
<filter string="供货方式待确认" name="supply_method" domain="[('state', '=', 'supply method')]"/>
</xpath>
</field>
</record>
<record id="sale.action_quotations_with_onboarding" model="ir.actions.act_window">
<field name="search_view_id" ref="jikimo_sale_order_view_search_inherit_quotation_supply_method"/>
<field name="context">{'search_default_draft': 1}</field>
</record>
<record id="action_quotations_supply_method" model="ir.actions.act_window">
<field name="name">报价单</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
<field name="view_mode">tree,kanban,form,calendar,pivot,graph,activity</field>
<field name="view_id" ref="sale.view_quotation_tree_with_onboarding"/>
<field name="search_view_id" ref="jikimo_sale_order_view_search_inherit_quotation_supply_method"/>
<field name="context">{'search_default_supply_method': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new quotation, the first step of a new sale!
</p><p>
Once the quotation is confirmed by the customer, it becomes a sales order.<br/> You will be able to create an invoice and collect the payment.
</p>
</field>
</record>
<menuitem
id="sale.sale_menu_root"
groups="sf_base.group_production_engineer,sf_base.group_sale_director,sf_base.group_sale_salemanager"
/>
<!--供货路线专员菜单-->
<menuitem
id="sale_order_menu_quotations_supply_method"
name="报价"
action="action_quotations_supply_method"
parent="sale.sale_order_menu"
groups="sf_base.group_production_engineer"
sequence="2"/>
<record id="sale.menu_sale_order" model="ir.ui.menu">
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
</record>
<record id="sale.report_sales_team" model="ir.ui.menu">
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
</record>
<record id="sale.res_partner_menu" model="ir.ui.menu">
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
</record>
</odoo>

View File

@@ -1,78 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<!-- <record id="view_stock_move_operations_inherit_sf" model="ir.ui.view">-->
<!-- <field name="name">stock.move.operations.form.inherit.sf</field>-->
<!-- <field name="model">stock.move</field>-->
<!-- <field name="inherit_id" ref="stock.view_stock_move_operations"/>-->
<!-- <field name="arch" type="xml">-->
<!-- <xpath expr="//button[@name='action_assign_serial_show_details']" position="after">-->
<!-- <button name="print_serial_numbers" string="序列号打印" type="object"/>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
<record id="view_stock_move_operations_inherit_sf" model="ir.ui.view">
<field name="name">stock.move.operations.form.inherit.sf</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_stock_move_operations"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='action_assign_serial_show_details']" position="after">
<button name="print_serial_numbers" string="序列号打印" type="object"/>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="view_picking_form_inherit_sf_1">
<record model="ir.ui.view" id="view_picking_form_inherit_sf">
<field name="name">stock.picking.form.inherit.sf</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='backorder_id']" position="after">
<field name="retrospect_ref"/>
</xpath>
<xpath expr="//field[@name='origin']" position="after">
<field name="sale_order_id"/>
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<field name="picking_type_sequence_code" invisible="1"/>
<field name="retrospect_ref"
attrs="{'invisible':[('picking_type_sequence_code','not in',['DL', 'INT', 'PC'])]}"/>
<field name="person_of_delivery" attrs="{'invisible':[('picking_type_sequence_code','!=','DL')]}"/>
<field name="telephone_of_delivery"
attrs="{'invisible':[('picking_type_sequence_code','!=','DL')]}"/>
<field name="address_of_delivery" attrs="{'invisible':[('picking_type_sequence_code','!=','DL')]}"/>
</xpath>
<xpath expr="//field[@name='product_id']" position="after">
<field name="part_number" optional="show"/>
<field name="part_name" optional="show"/>
</xpath>
</field>
</record>
<record id="sf_vpicktree_1" model="ir.ui.view">
<field name="name">sf.vpicktree.1</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.vpicktree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='origin']" position="before">
<field name="sale_order_id" string="销售单号"/>
</xpath>
<xpath expr="//field[@name='origin']" position="after">
<field name="retrospect_ref"/>
</xpath>
</field>
</record>
<record id="view_picking_internal_search_sf" model="ir.ui.view">
<field name="name">stock.picking.search</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_internal_search"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="replace">
<field name="name"/>
<field name="retrospect_ref"/>
<field name="sale_order_id" string="销售单号"/>
</xpath>
<xpath expr="//filter[@name='picking_type']" position="after">
<filter string="追溯参考" name="retrospect" domain="[]"
context="{'group_by': 'retrospect_ref'}"/>
</xpath>
</field>
</record>
<record id="stock.action_picking_tree_all" model="ir.actions.act_window">
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('stock.vpicktree')})]"/>
</record>
</data>
</odoo>

View File

@@ -1,6 +1,3 @@
from . import workpiece_delivery_wizard
from . import rework_wizard
from . import production_wizard
from . import production_technology_wizard
from . import production_technology_re_adjust_wizard
from . import mrp_workorder_batch_replan_wizard

View File

@@ -1,70 +0,0 @@
# -*- coding: utf-8 -*-
import logging
from datetime import datetime, timedelta
from odoo import models, api, fields, _
class MrpWorkorderBatchReplanWizard(models.TransientModel):
_name = 'mrp.workorder.batch.replan.wizard'
_description = '制造订单批量重新安排向导'
def _get_date_planned_start(self):
planned_start_date = datetime.now() + timedelta(hours=2)
logging.info('计划开始加工时间: %s', planned_start_date)
return planned_start_date
def _get_default_workorder_count(self):
active_ids = self.env.context.get('active_ids', [])
return len(active_ids)
def _get_default_workorder_type(self):
active_ids = self.env.context.get('active_ids', [])
if active_ids:
workorders = self.env['mrp.workorder'].browse(active_ids)
if workorders:
routing_type = set(workorders.mapped('routing_type'))
return '/'.join(sorted(routing_type)) if routing_type else None
return None
workorder_type = fields.Char(string='工单类型', default=_get_default_workorder_type, readonly=True)
workorder_count = fields.Integer(string='工单数量',
default=_get_default_workorder_count,
readonly=True)
workorder_id = fields.Many2many('mrp.workorder', string=u'工单')
def confirm(self):
routing_type = set(self.workorder_id.mapped('routing_type'))
if len(routing_type) > 1:
raise models.ValidationError("批量重新安排工单类型必须一致。")
show_json_popover = self.workorder_id.mapped('show_json_popover')
if any(not value for value in show_json_popover):
raise models.ValidationError("所选工单必须都为逾期状态")
failed_workorders = {}
for workorder_info in self.workorder_id:
try:
workorder_info.action_replan()
except Exception as e:
reason = str(e)
if reason in failed_workorders:
failed_workorders[reason].append(
workorder_info.production_id.name)
else:
failed_workorders[reason] = [workorder_info.production_id.name]
if failed_workorders:
error_messages = "\n".join(
[f"制造订单: {', '.join(workorder_names)}, 原因: {reason}" for reason, workorder_names in
failed_workorders.items()])
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'message': f"以下工单重新安排失败:\n{error_messages}",
'sticky': False,
'color': 'red',
'next': {
'type': 'ir.actions.act_window_close'
}
},
}

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="mrp_workorder_batch_replan_wizard_form" model="ir.ui.view">
<field name="name">mrp.workorder.batch.replan.wizard.form.view</field>
<field name="model">mrp.workorder.batch.replan.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<field name="workorder_type"/>
<field name="workorder_count"/>
</group>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn-primary" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="mrp_workorder_batch_replan_wizard" model="ir.actions.act_window">
<field name="name">重新安排工单</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.workorder.batch.replan.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="mrp_workorder_batch_replan_wizard_form"/>
<field name="target">new</field>
<field name="context">{'default_workorder_id': active_ids}</field>
</record>
</odoo>

View File

@@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
import logging
from itertools import groupby
from odoo import models, api, fields, _
class ProductionTechnologyReAdjustWizard(models.TransientModel):
_name = 'sf.production.technology.re_adjust.wizard'
_description = '制造订单工艺调整'
production_id = fields.Many2one('mrp.production', string='制造订单号')
origin = fields.Char(string='源单据')
is_technology_re_adjust = fields.Boolean(default=True)
def confirm(self):
if self.is_technology_re_adjust is True:
domain = [('origin', '=', self.origin), ('state', '=', 'confirmed'),
('product_id', '=', self.production_id.product_id.id)]
else:
domain = [('id', '=', self.production_id.id)]
technology_designs = self.env['sf.technology.design'].sudo().search(
[('production_id', '=', self.production_id.id), ('active', 'in', [True, False])])
productions = self.env['mrp.production'].search(domain)
for production_item in productions:
# 该制造订单的其他同一销售订单的制造订单的工艺设计处理
if production_item != self.production_id:
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs, production_item)
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production_item.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
for special in special_design:
workorders_values = []
if special.active is False:
is_cancel = False
# 工单采购单外协出入库单皆需取消
domain = [('production_id', '=', special.production_id.id)]
if special.process_parameters_id:
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')]
else:
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
workorder = self.env['mrp.workorder'].search(domain)
else:
workorder = self.env['mrp.workorder'].search(
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')])
if not workorder:
if special.route_id.routing_type == '表面工艺':
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special,
product_production_process.seller_ids[
0].partner_id.id))
else:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
special.production_id.write({'workorder_ids': workorders_values})
else:
logging.info(workorder.blocked_by_workorder_ids)
if len(workorder.blocked_by_workorder_ids) > 1:
if workorder.sequence == 1:
workorder.blocked_by_workorder_ids = None
else:
if workorder.blocked_by_workorder_ids:
workorder.blocked_by_workorder_ids = workorder.blocked_by_workorder_ids[0]
productions._reset_work_order_sequence()
# 退回时不对外协出入库单和采购单做处理
# if self.production_id.product_id.categ_id.type == '成品':
# productions._reset_subcontract_pick_purchase()
# productions.get_subcontract_pick_purchase()
productions.is_adjust = True
for item in productions:
workorders = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
key=lambda a: a.sequence)
if workorders[0].state in ['pending']:
if workorders[
0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorders[0].state = 'waiting'

View File

@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="sf_production_technology_re_adjust_wizard_form_view">
<field name="name">sf.production.technology.re_adjust.wizard.form.view</field>
<field name="model">sf.production.technology.re_adjust.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="1"/>
<field name="origin" invisible="1"/>
<div>
<field name="is_technology_re_adjust" force_save="1"/>
当前制造订单,同一销售订单相同产品所生成的制造订单是否统一进行退回调整操作
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight" confirm="是否确认退回调整"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="sf_production_technology_re_adjust_wizard_confirm_form_view">
<field name="name">sf.production.technology.re_adjust.wizard.form.view</field>
<field name="model">sf.production.technology.re_adjust.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="1"/>
<field name="origin" invisible="1"/>
<div>
是否确认退回调整
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<record id="action_sf_production_technology_re_adjust_wizard" model="ir.actions.act_window">
<field name="name">工艺退回调整</field>
<field name="res_model">sf.production.technology.re_adjust.wizard</field>
<field name="view_mode">form</field>
<!-- <field name="context">{-->
<!-- 'default_production_id': active_id}-->
<!-- </field>-->
<field name="target">new</field>
</record>
</odoo>

View File

@@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
import logging
from itertools import groupby
from odoo import models, api, fields, _
class ProductionTechnologyWizard(models.TransientModel):
_name = 'sf.production.technology.wizard'
_description = '制造订单工艺确认向导'
production_id = fields.Many2one('mrp.production', string='制造订单号')
origin = fields.Char(string='源单据')
is_technology_confirm = fields.Boolean(default=True)
def confirm(self):
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']:
domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'),
('product_id', '=', self.production_id.product_id.id)]
else:
domain = [('id', '=', self.production_id.id)]
technology_designs = self.env['sf.technology.design'].sudo().search(
[('production_id', '=', self.production_id.id), ('active', 'in', [True, False])])
# technology_designs = self.production_id.technology_design_ids
productions = self.env['mrp.production'].search(domain)
for production in productions:
if production != self.production_id:
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
production)
# 特殊表面工艺
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
for special in special_design:
workorders_values = []
if special.active is False:
# is_cancel = False
# 工单采购单外协出入库单皆需取消
domain = [('production_id', '=', special.production_id.id)]
if special.process_parameters_id:
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')]
else:
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
workorder = self.env['mrp.workorder'].search(domain)
# previous_workorder = self.env['mrp.workorder'].search(
# [('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
# ('production_id', '=', workorder.production_id.id)])
# if previous_workorder:
# if previous_workorder.supplier_id != workorder.supplier_id:
# is_cancel = True
# if workorder.state != 'cancel' and is_cancel is True:
workorder.write({'state': 'cancel'})
workorder.picking_ids.write({'state': 'cancel'})
workorder.picking_ids.move_ids.write({'state': 'cancel'})
purchase_order = self.env['purchase.order'].search(
[('origin', '=', workorder.production_id.name), ('purchase_type', '=', 'consignment')])
for line in purchase_order.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id:
purchase_order.write({'state': 'cancel'})
else:
if special.production_id.workorder_ids:
workorder = self.env['mrp.workorder'].search(
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')])
if not workorder:
if special.route_id.routing_type == '表面工艺':
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special,
product_production_process.seller_ids[
0].partner_id.id))
else:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
special.production_id.write({'workorder_ids': workorders_values})
else:
if len(workorder.blocked_by_workorder_ids) > 1:
if workorder.sequence == 1:
workorder.blocked_by_workorder_ids = None
else:
if workorder.blocked_by_workorder_ids:
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
productions._create_workorder(False)
if self.production_id.product_id.categ_id.type == '成品':
productions.get_subcontract_pick_purchase()
productions.is_adjust = False
for item in productions:
workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
key=lambda a: a.sequence)
if workorder[0].state in ['pending']:
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorder[0].state = 'waiting'
return productions

View File

@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="sf_production_technology_wizard_form_view">
<field name="name">sf.production.technology.wizard.form.view</field>
<field name="model">sf.production.technology.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="1"/>
<field name="origin" invisible="1"/>
<div>
<field name="is_technology_confirm" force_save="1"/>
对当前制造订单,同一销售订单相同产品所生成的制造订单统一进行工艺调整与确认
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"
confirm="是否确认工艺调整"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="sf_production_technology_wizard_confirm_form_view">
<field name="name">sf.production.technology.wizard.form.view</field>
<field name="model">sf.production.technology.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="1"/>
<field name="origin" invisible="1"/>
<div>
是否确认工艺调整
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<record id="action_sf_production_technology_wizard" model="ir.actions.act_window">
<field name="name">工艺确认</field>
<field name="res_model">sf.production.technology.wizard</field>
<field name="view_mode">form</field>
<!-- <field name="context">{-->
<!-- 'default_production_id': active_id}-->
<!-- </field>-->
<field name="target">new</field>
</record>
</odoo>

View File

@@ -46,5 +46,58 @@ class ProductionWizard(models.TransientModel):
ret = {'programming_list': [], 'is_reprogramming': self.is_reprogramming}
if self.is_reprogramming is True:
self.production_id.update_programming_state()
else:
scrap_cnc = self.production_id.workorder_ids.filtered(lambda crw: crw.routing_type == 'CNC加工').cnc_ids
scrap_cmm = self.production_id.workorder_ids.filtered(lambda cm: cm.routing_type == 'CNC加工').cmm_ids
for item_line in scrap_cnc:
vals = {
'sequence_number': item_line.sequence_number,
'program_name': item_line.program_name,
'cutting_tool_name': item_line.cutting_tool_name,
'cutting_tool_no': item_line.cutting_tool_no,
'processing_type': item_line.processing_type,
'margin_x_y': item_line.margin_x_y,
'margin_z': item_line.margin_z,
'depth_of_processing_z': item_line.depth_of_processing_z,
'cutting_tool_extension_length': item_line.cutting_tool_extension_length,
'estimated_processing_time': item_line.estimated_processing_time,
'cutting_tool_handle_type': item_line.cutting_tool_handle_type,
'ftp_path': item_line.program_path,
'processing_panel': item_line.workorder_id.processing_panel,
'program_create_date': datetime.strftime(item_line.program_create_date,
'%Y-%m-%d %H:%M:%S'),
'remark': item_line.remark
}
ret['programming_list'].append(vals)
for cmm_line in scrap_cmm:
vals = {
'sequence_number': cmm_line.sequence_number,
'program_name': cmm_line.program_name,
'ftp_path': cmm_line.program_path,
'processing_panel': item_line.workorder_id.processing_panel,
'program_create_date': datetime.strftime(
cmm_line.program_create_date,
'%Y-%m-%d %H:%M:%S')
}
ret['programming_list'].append(vals)
new_production = self.production_id.recreateManufacturing(ret)
self.production_id.write({'remanufacture_production_id': new_production.id})
if self.is_reprogramming is False:
for panel in new_production.product_id.model_processing_panel.split(','):
scrap_cnc_workorder = max(
self.production_id.workorder_ids.filtered(
lambda
scn: scn.processing_panel == panel and scn.routing_type == 'CNC加工'),
key=lambda w: w.create_date)
scrap_pre_workorder = max(self.production_id.workorder_ids.filtered(
lambda
pr: pr.processing_panel == panel and pr.routing_type == '装夹预调'),
key=lambda w1: w1.create_date)
new_cnc_workorder = new_production.workorder_ids.filtered(
lambda
nc: nc.processing_panel == panel and nc.routing_type == 'CNC加工')
new_cnc_workorder.write({'cnc_worksheet': scrap_cnc_workorder.cnc_worksheet})
new_pre_workorder = new_production.workorder_ids.filtered(lambda
p: p.routing_type == '装夹预调' and p.processing_panel == panel)
new_pre_workorder.write({'processing_drawing': scrap_pre_workorder.processing_drawing})

View File

@@ -4,7 +4,6 @@ import logging
from odoo.exceptions import UserError, ValidationError
from datetime import datetime
from odoo import models, api, fields, _
from odoo.tools import groupby
class ReworkWizard(models.TransientModel):
@@ -14,9 +13,6 @@ class ReworkWizard(models.TransientModel):
workorder_id = fields.Many2one('mrp.workorder', string='工单')
product_id = fields.Many2one('product.product')
production_id = fields.Many2one('mrp.production', string='制造订单号')
workorder_ids = fields.Many2many('mrp.workorder', 'rework_wizard_to_work_order', string='所有工单',
domain="[('production_id', '=', production_id),('state','=','done')]")
hidden_workorder_ids = fields.Char('')
rework_reason = fields.Selection(
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
("operate computer", "操机"),
@@ -38,61 +34,6 @@ class ReworkWizard(models.TransientModel):
tool_state = fields.Selection(string='功能刀具状态', related='production_id.tool_state')
@api.onchange('hidden_workorder_ids')
def _onchange_hidden_workorder_ids(self):
for item in self:
if item.hidden_workorder_ids not in ['', None, False]:
hidden_workorder_list = item.hidden_workorder_ids.split(',')
# 获取加工面对应需要返工的工单
rw_ids = item.workorder_ids.filtered(
lambda w: str(w.ids[0]) in hidden_workorder_list and w.processing_panel not in ['', None, False])
grouped_rw_ids = {key: list(group) for key, group in groupby(rw_ids, key=lambda w: w.processing_panel)}
for panel, panel_rw_ids in grouped_rw_ids.items():
work_ids = item.workorder_ids.filtered(lambda w: w.state == 'done' and w.processing_panel == panel)
if len(work_ids) == 3 and len(panel_rw_ids) != 3:
for work_id in work_ids:
if work_id not in panel_rw_ids:
hidden_workorder_list.append(str(work_id.ids[0]))
elif len(work_ids) == 2 and len(panel_rw_ids) < 2 and panel_rw_ids[0].name == 'CNC加工':
if rw_ids.filtered(lambda w: (w.sequence < panel_rw_ids[0].sequence
and w.processing_panel != panel_rw_ids[0].processing_panel)):
hidden_workorder_list.append(str(work_ids.filtered(
lambda w: (w.processing_panel == panel_rw_ids[0].processing_panel
and w.name == '装夹预调')).ids[0]))
hidden_workorder_list.sort()
item.hidden_workorder_ids = ','.join(hidden_workorder_list)
def efficacy_rework_wo(self, wk_ids):
"""限制判断 """
# 判断检测结果待处理所对应的工单是否勾选
result_ids = self.production_id.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]
if len(wk_ids.filtered(lambda wk: wk.id in work_id_list)) != len(work_id_list):
raise ValidationError('存在【检测结果】为【待处理】所对应的工单未进行勾选!!!')
# 获取已完成的标准工单
grouped_rw_ids = {key: list(group) for key, group in groupby(wk_ids, key=lambda w: w.processing_panel)}
for panel, panel_rw_ids in grouped_rw_ids.items():
# 1、当制造订单内ZM面的工单都已完成时返工勾选工序时只能勾选上ZM面的所有工序进行返工
work_ids = self.workorder_ids.filtered(lambda w: w.state == 'done' and w.processing_panel == panel)
if len(work_ids) == 3 and len(panel_rw_ids) != 3:
raise ValidationError(
'因为[%s]面的工单已全部完成,如果要对[%s]面的工单进行返工,请勾选这个面的所有工单。' % (panel, panel))
# 2、当FM工单在CNC工单进行选择返工并将已全部完成的ZM面工序全部勾选上时FM工单上所有的已完成的工单装夹预调工单也必须进行勾选
if not wk_ids.filtered(lambda wk: wk.name == '装夹预调' and wk.processing_panel == panel):
if wk_ids.filtered(lambda wk: wk.name == 'CNC加工' and wk.processing_panel == panel):
sequence_max = wk_ids.filtered(
lambda wk: wk.name == 'CNC加工' and wk.processing_panel == panel).sequence
for wk_id in wk_ids.filtered(lambda wk: wk.sequence < sequence_max):
if len(wk_ids.filtered(lambda wk: wk.processing_panel == wk_id.processing_panel)) == 3:
raise ValidationError(
'由于在[%s]面之前存在整个面进行了勾选的情况,所以在勾选了[%s]面的【CNC加工】工单的时请勾选[%s]面的装夹预调工单!' % (
panel, panel, panel))
def confirm(self):
if self.routing_type in ['装夹预调', 'CNC加工']:
self.is_clamp_measure = False
@@ -107,113 +48,100 @@ class ReworkWizard(models.TransientModel):
'test_report': self.workorder_id.detection_report})]})
self.workorder_id.button_finish()
else:
if self.hidden_workorder_ids:
hidden_workorder_list = self.hidden_workorder_ids.split(',')
rework_workorder_ids = self.workorder_ids.filtered(lambda w: str(w.id) in hidden_workorder_list)
# 调用效验方法
self.efficacy_rework_wo(rework_workorder_ids)
else:
raise ValidationError('请选择返工工单!!!')
if rework_workorder_ids:
# 限制
# 1、单独返工CNC工单则不解绑托盘RFID如单独返工装夹预调工单则自动解绑托盘RFID
# 2、返工CNC工单和装夹预调工单则自动解绑RFID
clamp_workorder_ids = rework_workorder_ids.filtered(lambda rp: rp.routing_type == '装夹预调')
if clamp_workorder_ids:
for clamp_workorder_id in clamp_workorder_ids:
self.production_id.workorder_ids.filtered(
lambda wk: wk.processing_panel == clamp_workorder_id.processing_panel).write(
{'rfid_code': None})
# 返工工单状态设置为【返工】
rework_workorder_ids.write({'state': 'rework'})
# 查询返工工单对应的工艺设计记录,并调用方法拼接数据,用于创建新的工单
workorders_values = []
for work in rework_workorder_ids:
route = self.production_id.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 route:
work_list = self.env['mrp.workorder'].json_workorder_str(self.production_id, route[0])
work_list[2].update({'tag_type': '重新加工', 'sequence': 0})
workorders_values.append(work_list)
# 创建新工单,并进行返工配置的相关操作
if workorders_values:
# 创建新工单、工序排序、完成检测结果单据
self.production_id.write({'workorder_ids': workorders_values, 'is_rework': True})
new_work_ids = self.production_id.workorder_ids.filtered(lambda kw: kw.sequence == 0)
new_pre_workorder_ids = self.production_id.workorder_ids.filtered(
lambda kw: kw.routing_type == '装夹预调' and kw.sequence == 0)
self.production_id._reset_work_order_sequence()
# ====新工单绑定rfid===
for new_work_id in new_work_ids:
if new_work_id.routing_type in ['CNC加工', '解除装夹']:
new_work_id.write({'rfid_code': self.production_id.workorder_ids.filtered(
lambda wk: wk.sequence == new_work_id.sequence - 1).rfid_code})
self.production_id.detection_result_ids.filtered(
lambda ap1: ap1.handle_result == '待处理').write({'handle_result': '已处理'})
panels = [] # 返工的加工面
# ===================保留装夹测量数据=====================
if self.is_clamp_measure and clamp_workorder_ids and new_pre_workorder_ids:
for new_pre_workorder in new_pre_workorder_ids:
if new_pre_workorder.processing_panel and new_pre_workorder.processing_panel not in panels:
panels.append(new_pre_workorder.processing_panel)
for rework_clamp_workorder in clamp_workorder_ids:
if new_pre_workorder.sequence == rework_clamp_workorder.sequence + 1:
new_pre_workorder.write(
{'X1_axis': rework_clamp_workorder.X1_axis,
'Y1_axis': rework_clamp_workorder.Y1_axis
, 'Z1_axis': rework_clamp_workorder.Z1_axis,
'X2_axis': rework_clamp_workorder.X2_axis
, 'Y2_axis': rework_clamp_workorder.Y2_axis,
'Z2_axis': rework_clamp_workorder.Z2_axis
, 'X3_axis': rework_clamp_workorder.X3_axis,
'Y3_axis': rework_clamp_workorder.Y3_axis
, 'Z3_axis': rework_clamp_workorder.Z3_axis,
'X4_axis': rework_clamp_workorder.X4_axis
, 'Y4_axis': rework_clamp_workorder.Y4_axis,
'Z4_axis': rework_clamp_workorder.Z4_axis
, 'X5_axis': rework_clamp_workorder.X5_axis,
'Y5_axis': rework_clamp_workorder.Y5_axis
, 'Z5_axis': rework_clamp_workorder.Z5_axis,
'X6_axis': rework_clamp_workorder.X6_axis
, 'Y6_axis': rework_clamp_workorder.Y6_axis,
'Z6_axis': rework_clamp_workorder.Z6_axis
, 'X7_axis': rework_clamp_workorder.X7_axis,
'Y7_axis': rework_clamp_workorder.Y7_axis
, 'Z7_axis': rework_clamp_workorder.Z7_axis,
'X8_axis': rework_clamp_workorder.X8_axis
, 'Y8_axis': rework_clamp_workorder.Y8_axis,
'Z8_axis': rework_clamp_workorder.Z8_axis
, 'X9_axis': rework_clamp_workorder.X9_axis,
'Y9_axis': rework_clamp_workorder.Y9_axis
, 'Z9_axis': rework_clamp_workorder.Z9_axis,
'X10_axis': rework_clamp_workorder.X10_axis
, 'Y10_axis': rework_clamp_workorder.Y10_axis,
'Z10_axis': rework_clamp_workorder.Z10_axis
, 'X_deviation_angle': rework_clamp_workorder.X_deviation_angle,
'material_center_point': rework_clamp_workorder.material_center_point
})
break
# =====================不申请重新编程====================
if self.is_reprogramming is False:
if self.programming_state in ['已编程', '已下发']:
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
for panel_name in panels:
self.production_id.get_new_program(panel_name)
if self.reprogramming_num >= 0 and self.programming_state == '已下发':
# ============= 处理CNC加工加工工单的 CNC程序和cmm程序 信息=============
for cnc_work in new_work_ids.filtered(lambda wk: wk.name == 'CNC加工'):
if self.production_id.workorder_ids:
handle_result = self.production_id.detection_result_ids.filtered(
lambda dr: dr.handle_result == '待处理')
if handle_result:
processing_panels_to_handle = set(handle_item.processing_panel for handle_item in handle_result)
processing_panels_choice = set(dr_panel.name for dr_panel in self.processing_panel_id)
# 使用集合的差集操作找出那些待处理结果中有但实际可用加工面中没有的加工面
processing_panels_missing = processing_panels_to_handle - processing_panels_choice
# 存在不一致的加工面
if processing_panels_missing:
processing_panels_str = ','.join(processing_panels_missing)
raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str)
for panel in self.processing_panel_id:
panel_workorder = self.production_id.workorder_ids.filtered(
lambda ap: ap.processing_panel == panel.name and ap.state != 'rework')
if panel_workorder:
panel_workorder.write({'state': 'rework'})
rework_clamp_workorder = max(panel_workorder.filtered(
lambda
rp: rp.processing_panel == panel.name and rp.routing_type == '装夹预调' and rp.state in [
'done', 'rework']))
# panel_workorder.filtered(
# lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered(
# lambda wd: wd.status == '待下发').write({'status': '已取消'})
# workpiece = self.env['sf.workpiece.delivery'].search([('status', '=', '待下发'), (
# 'workorder_id', '=',
# panel_workorder.filtered(lambda wd: wd.routing_type == '装夹预调').id)])
product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
[('product_model_type_id', '=', self.production_id.product_id.product_model_type_id.id)],
order='sequence asc'
)
workorders_values = []
for route in product_routing_workcenter:
if route.is_repeat is True:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(panel.name,
self.production_id, route, False))
if workorders_values:
self.production_id.write({'workorder_ids': workorders_values, 'is_rework': True})
self.production_id._reset_work_order_sequence()
self.production_id.detection_result_ids.filtered(
lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write(
{'handle_result': '已处理'})
new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
'rework', 'done'))
if new_pre_workorder and rework_clamp_workorder and self.is_clamp_measure is True:
new_pre_workorder.write(
{'X1_axis': rework_clamp_workorder.X1_axis, 'Y1_axis': rework_clamp_workorder.Y1_axis
, 'Z1_axis': rework_clamp_workorder.Z1_axis,
'X2_axis': rework_clamp_workorder.X2_axis
, 'Y2_axis': rework_clamp_workorder.Y2_axis,
'Z2_axis': rework_clamp_workorder.Z2_axis
, 'X3_axis': rework_clamp_workorder.X3_axis,
'Y3_axis': rework_clamp_workorder.Y3_axis
, 'Z3_axis': rework_clamp_workorder.Z3_axis,
'X4_axis': rework_clamp_workorder.X4_axis
, 'Y4_axis': rework_clamp_workorder.Y4_axis,
'Z4_axis': rework_clamp_workorder.Z4_axis
, 'X5_axis': rework_clamp_workorder.X5_axis,
'Y5_axis': rework_clamp_workorder.Y5_axis
, 'Z5_axis': rework_clamp_workorder.Z5_axis,
'X6_axis': rework_clamp_workorder.X6_axis
, 'Y6_axis': rework_clamp_workorder.Y6_axis,
'Z6_axis': rework_clamp_workorder.Z6_axis
, 'X7_axis': rework_clamp_workorder.X7_axis,
'Y7_axis': rework_clamp_workorder.Y7_axis
, 'Z7_axis': rework_clamp_workorder.Z7_axis,
'X8_axis': rework_clamp_workorder.X8_axis
, 'Y8_axis': rework_clamp_workorder.Y8_axis,
'Z8_axis': rework_clamp_workorder.Z8_axis
, 'X9_axis': rework_clamp_workorder.X9_axis,
'Y9_axis': rework_clamp_workorder.Y9_axis
, 'Z9_axis': rework_clamp_workorder.Z9_axis,
'X10_axis': rework_clamp_workorder.X10_axis
, 'Y10_axis': rework_clamp_workorder.Y10_axis,
'Z10_axis': rework_clamp_workorder.Z10_axis
, 'X_deviation_angle': rework_clamp_workorder.X_deviation_angle,
'material_center_point': rework_clamp_workorder.material_center_point
})
if self.is_reprogramming is False:
if self.programming_state in ['已编程', '已下发']:
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
self.production_id.get_new_program(panel.name)
if self.reprogramming_num >= 0 and self.programming_state == '已下发':
ret = {'programming_list': []}
old_cnc_rework = max(self.production_id.workorder_ids.filtered(
lambda crw: crw.processing_panel == cnc_work.processing_panel
and crw.state == 'rework' and crw.routing_type == 'CNC加工'),
cnc_rework = max(
self.production_id.workorder_ids.filtered(
lambda
crw: crw.processing_panel == panel.name and crw.state == 'rework' and crw.routing_type == 'CNC加工'),
key=lambda w: w.create_date
)
# 获取当前工单的CNC程序和cmm程序
if old_cnc_rework.cnc_ids:
for item_line in old_cnc_rework.cnc_ids:
if cnc_rework.cnc_ids:
for item_line in cnc_rework.cnc_ids:
vals = {
'sequence_number': item_line.sequence_number,
'program_name': item_line.program_name,
@@ -228,50 +156,50 @@ class ReworkWizard(models.TransientModel):
'cutting_tool_handle_type': item_line.cutting_tool_handle_type,
'program_path': item_line.program_path,
'ftp_path': item_line.program_path,
'processing_panel': cnc_work.processing_panel,
'processing_panel': panel.name,
'program_create_date': datetime.strftime(item_line.program_create_date,
'%Y-%m-%d %H:%M:%S'),
'remark': item_line.remark
}
ret['programming_list'].append(vals)
for cmm_line in old_cnc_rework.cmm_ids:
for cmm_line in cnc_rework.cmm_ids:
vals = {
'sequence_number': cmm_line.sequence_number,
'program_name': cmm_line.program_name,
'program_path': cmm_line.program_path,
'ftp_path': cmm_line.program_path,
'processing_panel': cnc_work.processing_panel,
'processing_panel': panel.name,
'program_create_date': datetime.strftime(
cmm_line.program_create_date,
'%Y-%m-%d %H:%M:%S')
}
ret['programming_list'].append(vals)
# 获取新的
new_cnc_workorder = self.production_id.workorder_ids.filtered(
lambda ap1: ap1.processing_panel == cnc_work.processing_panel
and ap1.state not in (
'rework', 'done') and ap1.routing_type == 'CNC加工'
)
lambda ap1: ap1.processing_panel == panel.name and ap1.state not in (
'rework', 'done') and ap1.routing_type == 'CNC加工')
if not new_cnc_workorder.cnc_ids:
new_cnc_workorder.write({
'cnc_ids': new_cnc_workorder.cnc_ids.sudo()._json_cnc_processing(
cnc_work.processing_panel, ret),
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(
cnc_work.processing_panel, ret),
'cnc_worksheet': old_cnc_rework.cnc_worksheet})
# ========== 处理装夹预调 【装夹图纸】 数据 ================
for new_pre_work in new_pre_workorder_ids:
pre_rework = max(self.production_id.workorder_ids.filtered(
lambda pr: (pr.processing_panel == new_pre_work.processing_panel
and pr.state in ['rework'] and pr.routing_type == '装夹预调')),
key=lambda w1: w1.create_date)
new_pre_work.write(
{'processing_drawing': pre_rework.processing_drawing})
self.production_id.write({'state': 'progress', 'is_rework': False})
elif self.programming_state in ['待编程', '编程中']:
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True})
# =================================================
'cnc_ids': new_cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel.name,
ret),
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name,
ret),
'cnc_worksheet': cnc_rework.cnc_worksheet})
if new_pre_workorder:
pre_rework = max(self.production_id.workorder_ids.filtered(
lambda pr: pr.processing_panel == panel.name and pr.state in (
'rework') and pr.routing_type == '装夹预调'),
key=lambda w1: w1.create_date)
new_pre_workorder.write(
{'processing_drawing': pre_rework.processing_drawing})
self.production_id.write({'state': 'progress', 'is_rework': False})
elif self.programming_state in ['待编程', '编程中']:
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True})
if self.is_reprogramming is True:
self.production_id.update_programming_state()
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中'})
if self.production_id.state == 'progress':
self.production_id.write({'programming_state': '已编程', 'work_state': '已编程'})
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
@@ -282,13 +210,6 @@ class ReworkWizard(models.TransientModel):
productions_not_delivered.write(
{'state': 'progress', 'programming_state': '已编程', 'work_state': '已编程',
'is_rework': False})
# ==================申请重新编程=======================
if self.is_reprogramming is True:
self.production_id.update_programming_state()
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中', 'state': 'progress'})
# ================= 返工完成,制造订单状态置为加工中 ==============
self.production_id.write({'state': 'progress', 'is_rework': False})
@api.onchange('production_id')
def onchange_processing_panel_id(self):

View File

@@ -11,20 +11,10 @@
<field name="product_id" invisible="True"/>
<field name="tool_state" invisible="True"/>
<field name="routing_type" invisible="True"/>
<field name="processing_panel_id" invisible="1"/>
<field name="hidden_workorder_ids" class="css_not_available_msg"/>
<group>
<field name="hidden_workorder_ids" invisible="1"/>
<field options="{'no_create': True,'no_open': True}" readonly="1" name="workorder_ids"
widget="jikimo_subtree_selector_field"
jikimo_selector="True" replace_context="hidden_workorder_ids" string="工序"
attrs='{"invisible": [("routing_type","=","装夹预调")]}'>
<tree create="0" editable='bottom' delete="0">
<field name="sequence" readonly="1" string="工序"/>
<field name="processing_panel" readonly="1"/>
<field name="name" readonly="1"/>
</tree>
</field>
<field name="processing_panel_id" options="{'no_create': True}"
attrs='{"invisible": [("routing_type","=","装夹预调")]}' widget="many2many_tags"/>
</group>
<div attrs='{"invisible": [("routing_type","=","装夹预调")]}'>
<span style='font-weight:bold;'>保留装夹测量数据

View File

@@ -207,9 +207,8 @@ class WorkpieceDeliveryWizard(models.TransientModel):
workorder.production_line_id.id != self.production_ids[0].production_line_id.id):
raise UserError(f'该rfid对应的制造订单号为{workorder.production_id.name}的目的生产线不一致')
if workorder.routing_type == '解除装夹':
# 调用打印成品条码方法
workorder.print_method()
# 调用打印成品条码方法
workorder.print_method()
# 将对象添加到对应的同模型且是多对多类型里
self.production_ids |= workorder.production_id

View File

@@ -11,16 +11,13 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality', 'mrp',
'sf_manufacturing', 'product', 'quality'],
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality', 'mrp'],
'data': [
'data/bussiness_node.xml',
'data/cron_data.xml',
'data/template_data.xml',
'security/ir.model.access.csv',
'views/mrp_workorder_views.xml',
'views/purchase_order_view.xml',
'views/stock_picking_view.xml',
],
'test': [
],

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