Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化
This commit is contained in:
143
jikimo_sale_multiple_supply_methods/__init__.py
Normal file
143
jikimo_sale_multiple_supply_methods/__init__.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# -*- 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})
|
||||||
|
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': '坯料'})
|
||||||
|
|
||||||
|
# 为三步制造增加规则
|
||||||
|
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
|
||||||
27
jikimo_sale_multiple_supply_methods/__manifest__.py
Normal file
27
jikimo_sale_multiple_supply_methods/__manifest__.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
'name': '机企猫 多供货方式',
|
||||||
|
'version': '16.0.1.0.0',
|
||||||
|
'summary': """ 报价单提供(自动化产线加工/人工线下加工/外购/委外加工)多种供货方式选择 """,
|
||||||
|
'author': 'fox',
|
||||||
|
'website': '',
|
||||||
|
'category': '',
|
||||||
|
'depends': ['sf_dlm', 'sale_stock', 'sf_sale'],
|
||||||
|
"data": [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'data/stock_routes.xml',
|
||||||
|
'data/product_data.xml',
|
||||||
|
'views/sale_order_views.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',
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import main
|
||||||
50
jikimo_sale_multiple_supply_methods/controllers/main.py
Normal file
50
jikimo_sale_multiple_supply_methods/controllers/main.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
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'], 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)
|
||||||
|
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:
|
||||||
|
_logger.info('get_bfm_process_order_list error:%s' % e)
|
||||||
|
res['status'] = -1
|
||||||
|
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
||||||
|
request.cr.rollback()
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
98
jikimo_sale_multiple_supply_methods/data/product_data.xml
Normal file
98
jikimo_sale_multiple_supply_methods/data/product_data.xml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?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>
|
||||||
32
jikimo_sale_multiple_supply_methods/data/stock_routes.xml
Normal file
32
jikimo_sale_multiple_supply_methods/data/stock_routes.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?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>
|
||||||
4
jikimo_sale_multiple_supply_methods/models/__init__.py
Normal file
4
jikimo_sale_multiple_supply_methods/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import product_template
|
||||||
|
from . import sale_order
|
||||||
|
from . import mrp_bom
|
||||||
13
jikimo_sale_multiple_supply_methods/models/mrp_bom.py
Normal file
13
jikimo_sale_multiple_supply_methods/models/mrp_bom.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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 product_create(self, product_id, item, order_id, order_number, i):
|
||||||
|
product_id = super(ProductTemplate, self).product_create(product_id, item, order_id, order_number, i)
|
||||||
|
product_id.product_tmpl_id.is_customer_provided = True if item['embryo_redundancy_id'] else False
|
||||||
|
return product_id
|
||||||
|
|
||||||
|
|
||||||
|
def copy_template(self, product_template_id):
|
||||||
|
if not isinstance(product_template_id, ProductTemplate):
|
||||||
|
raise ValueError('%s必须是ProductTemplate类型' % product_template_id)
|
||||||
|
|
||||||
|
self.route_ids = product_template_id.route_ids
|
||||||
|
self.categ_id = product_template_id.categ_id
|
||||||
|
self.invoice_policy = product_template_id.invoice_policy
|
||||||
|
self.detailed_type = product_template_id.detailed_type
|
||||||
|
self.purchase_ok = product_template_id.purchase_ok
|
||||||
|
self.uom_id = product_template_id.uom_id
|
||||||
|
self.uom_po_id = product_template_id.uom_po_id
|
||||||
|
self.company_id = product_template_id.company_id
|
||||||
|
self.single_manufacturing = product_template_id.single_manufacturing
|
||||||
|
self.tracking = product_template_id.tracking
|
||||||
|
self.is_bfm = product_template_id.is_bfm
|
||||||
|
self.is_manual_processing = product_template_id.is_manual_processing
|
||||||
|
# 复制 seller_ids
|
||||||
|
self.seller_ids = [(0, 0, {'partner_id': seller.partner_id.id, 'delay': 1.0}) for seller in product_template_id.seller_ids]
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
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('工厂创建销售订单和产品失败,请联系管理员')
|
||||||
157
jikimo_sale_multiple_supply_methods/models/sale_order.py
Normal file
157
jikimo_sale_multiple_supply_methods/models/sale_order.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
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', "销售订单"),
|
||||||
|
('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)
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
# 供货方式
|
||||||
|
supply_method = fields.Selection([
|
||||||
|
('automation', "自动化产线加工"),
|
||||||
|
('manual', "人工线下加工"),
|
||||||
|
('purchase', "外购"),
|
||||||
|
('outsourcing', "委外加工"),
|
||||||
|
], string='供货方式')
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
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
|
||||||
|
@@ -0,0 +1,16 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
|
||||||
108
jikimo_sale_multiple_supply_methods/views/sale_order_views.xml
Normal file
108
jikimo_sale_multiple_supply_methods/views/sale_order_views.xml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_order_form_inherit_sf" model="ir.ui.view">
|
||||||
|
<field name="name">view.sale.order.form.inherit.sf</field>
|
||||||
|
<field name="inherit_id" ref="sale_stock.view_order_form_inherit_sale_stock_qty"/>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page/field[@name='order_line']/form/group/group/div[@name='ordered_qty']/widget[@name='qty_at_date_widget']" position="replace">
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page/field[@name='order_line']/tree/widget[@name='qty_at_date_widget']" position="replace">
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<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">draft,sent,supply method,sale</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="//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': ['|','&',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel','supply method']),'&',('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>
|
||||||
@@ -203,7 +203,7 @@
|
|||||||
<record id="quality_alert_action_check" model="ir.actions.act_window">
|
<record id="quality_alert_action_check" model="ir.actions.act_window">
|
||||||
<field name="name">Quality Alerts</field>
|
<field name="name">Quality Alerts</field>
|
||||||
<field name="res_model">quality.alert</field>
|
<field name="res_model">quality.alert</field>
|
||||||
<field name="view_mode">kanban,tree,form,pivot,graph,calendar</field>
|
<field name="view_mode">tree,kanban,form,pivot,graph,calendar</field>
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="o_view_nocontent_smiling_face">
|
<p class="o_view_nocontent_smiling_face">
|
||||||
Create a new quality alert
|
Create a new quality alert
|
||||||
|
|||||||
@@ -410,3 +410,14 @@ class ReSaleOrder(models.Model):
|
|||||||
person_of_delivery = fields.Char('收货人')
|
person_of_delivery = fields.Char('收货人')
|
||||||
telephone_of_delivery = fields.Char('电话号码')
|
telephone_of_delivery = fields.Char('电话号码')
|
||||||
address_of_delivery = fields.Char('联系地址')
|
address_of_delivery = fields.Char('联系地址')
|
||||||
|
|
||||||
|
|
||||||
|
class EmbryoRedundancy(models.Model):
|
||||||
|
_name = "sf.embryo.redundancy"
|
||||||
|
|
||||||
|
name = fields.Char('名称', required=True)
|
||||||
|
long = fields.Float('长度(mm)', required=True)
|
||||||
|
width = fields.Float('宽度(mm)', required=True)
|
||||||
|
height = fields.Float('高度(mm)', required=True)
|
||||||
|
code = fields.Char('编码', required=True)
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class MrsMaterialModel(models.Model):
|
|||||||
finish_machining = fields.Float("精加工Vc(m/min)")
|
finish_machining = fields.Float("精加工Vc(m/min)")
|
||||||
remark = fields.Text("备注")
|
remark = fields.Text("备注")
|
||||||
gain_way = fields.Selection(
|
gain_way = fields.Selection(
|
||||||
[("自加工", "自加工"), ("外协", "外协"), ("采购", "采购")],
|
[("自加工", "自加工"), ("外协", "委外加工"), ("采购", "采购")],
|
||||||
default="", string="获取方式")
|
default="", string="获取方式")
|
||||||
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
|
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
|
||||||
active = fields.Boolean('有效', default=True)
|
active = fields.Boolean('有效', default=True)
|
||||||
@@ -100,6 +100,7 @@ class MrsProductionProcess(models.Model):
|
|||||||
travel_day = fields.Float('路途天数/d')
|
travel_day = fields.Float('路途天数/d')
|
||||||
sequence = fields.Integer('排序')
|
sequence = fields.Integer('排序')
|
||||||
|
|
||||||
|
|
||||||
# class MrsProcessingTechnology(models.Model):
|
# class MrsProcessingTechnology(models.Model):
|
||||||
# _name = 'sf.processing.technology'
|
# _name = 'sf.processing.technology'
|
||||||
# _description = '加工工艺'
|
# _description = '加工工艺'
|
||||||
@@ -157,7 +158,9 @@ class MrsProductionProcessParameter(models.Model):
|
|||||||
for parameter in self:
|
for parameter in self:
|
||||||
if parameter.process_id:
|
if parameter.process_id:
|
||||||
name = parameter.process_id.name + '-' + parameter.name
|
name = parameter.process_id.name + '-' + parameter.name
|
||||||
result.append((parameter.id, name))
|
else:
|
||||||
|
name = parameter.name
|
||||||
|
result.append((parameter.id, name))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# 获取表面工艺的获取方式
|
# 获取表面工艺的获取方式
|
||||||
|
|||||||
@@ -251,3 +251,6 @@ 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,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_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
|
||||||
|
|||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
.img-fluid {
|
.img-fluid {
|
||||||
max-width: unset !important;
|
max-width: unset !important;
|
||||||
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.o_inner_group .img-fluid {
|
.o_inner_group .img-fluid {
|
||||||
|
|||||||
@@ -633,4 +633,26 @@
|
|||||||
<field name="res_model">sf.machining.accuracy</field>
|
<field name="res_model">sf.machining.accuracy</field>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
</record>
|
</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>
|
</odoo>
|
||||||
@@ -141,18 +141,25 @@
|
|||||||
sequence="1"
|
sequence="1"
|
||||||
action="action_sf_machine_brand"/>
|
action="action_sf_machine_brand"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_embryo_redundancy"
|
||||||
|
parent="menu_sf_base"
|
||||||
|
name="坯料冗余"
|
||||||
|
sequence="2"
|
||||||
|
action="action_sf_embryo_redundancy"/>
|
||||||
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="menu_sf_machining_accuracy"
|
id="menu_sf_machining_accuracy"
|
||||||
parent="menu_sf_base"
|
parent="menu_sf_base"
|
||||||
name="加工精度"
|
name="加工精度"
|
||||||
sequence="1"
|
sequence="3"
|
||||||
action="action_sf_machining_accuracy"/>
|
action="action_sf_machining_accuracy"/>
|
||||||
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="menu_sf_machine_control_system"
|
id="menu_sf_machine_control_system"
|
||||||
parent="menu_sf_base"
|
parent="menu_sf_base"
|
||||||
name="数控系统"
|
name="数控系统"
|
||||||
sequence="2"
|
sequence="4"
|
||||||
action="action_sf_machine_control_system"/>
|
action="action_sf_machine_control_system"/>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
<record id="mrp.product_template_action" model="ir.actions.act_window">
|
<record id="mrp.product_template_action" model="ir.actions.act_window">
|
||||||
|
<field name="view_mode">tree,kanban,form,activity</field>
|
||||||
<field name="context">
|
<field name="context">
|
||||||
{"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
|
{"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
|
||||||
</field>
|
</field>
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
|
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name='is_bfm' invisible="1"/>
|
<field name='is_bfm' invisible="1"/>
|
||||||
<field name='categ_type' 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='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
||||||
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/>
|
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/>
|
||||||
<field name="upload_model_file"
|
<field name="upload_model_file"
|
||||||
@@ -63,6 +65,9 @@
|
|||||||
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
|
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
|
||||||
domain="[('fixture_model_id','=',fixture_model_id)]"/>
|
domain="[('fixture_model_id','=',fixture_model_id)]"/>
|
||||||
</field>
|
</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">
|
<xpath expr="//label[@for='volume']" position="before">
|
||||||
<label for="length" string="尺寸"
|
<label for="length" string="尺寸"
|
||||||
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}"/>
|
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}"/>
|
||||||
@@ -110,15 +115,13 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet//notebook" position="inside">
|
<xpath expr="//sheet//notebook" position="inside">
|
||||||
<page string="2D加工图纸">
|
<page string="2D加工图纸" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])]}">
|
||||||
<field name='machining_drawings' attrs="{'invisible': [('categ_type', '!=', '成品')]}"
|
<field name='machining_drawings' widget="adaptive_viewer"/>
|
||||||
widget="adaptive_viewer"/>
|
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet//notebook" position="inside">
|
<xpath expr="//sheet//notebook" position="inside">
|
||||||
<page string="质检标准">
|
<page string="质检标准" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])]}">
|
||||||
<field name='quality_standard' attrs="{'invisible': [('categ_type', '!=', '成品')]}"
|
<field name='quality_standard' widget="adaptive_viewer"/>
|
||||||
widget="adaptive_viewer"/>
|
|
||||||
|
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<searchpanel>
|
<searchpanel>
|
||||||
<field name="routing_type" select="multi" string="工序类型" icon="fa-building" enable_counters="1"/>
|
<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="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="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
|
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
|
||||||
</searchpanel>
|
</searchpanel>
|
||||||
|
|
||||||
|
|||||||
@@ -689,6 +689,8 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
if next_date < date_now:
|
if next_date < date_now:
|
||||||
next_date = date_now
|
next_date = date_now
|
||||||
else:
|
else:
|
||||||
|
if not equipment.initial_action_date:
|
||||||
|
raise ValidationError('重置保养日期不能为空!!!')
|
||||||
next_date = equipment.initial_action_date + timedelta(days=equipment.period)
|
next_date = equipment.initial_action_date + timedelta(days=equipment.period)
|
||||||
equipment.next_action_date = next_date
|
equipment.next_action_date = next_date
|
||||||
else:
|
else:
|
||||||
@@ -735,6 +737,8 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
if next_date < date_now:
|
if next_date < date_now:
|
||||||
next_date = date_now
|
next_date = date_now
|
||||||
else:
|
else:
|
||||||
|
if not equipment.initial_overhaul_date:
|
||||||
|
raise ValidationError('重置维修日期不能为空')
|
||||||
next_date = equipment.initial_overhaul_date + timedelta(days=equipment.overhaul_period)
|
next_date = equipment.initial_overhaul_date + timedelta(days=equipment.overhaul_period)
|
||||||
equipment.overhaul_date = next_date
|
equipment.overhaul_date = next_date
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -10,8 +10,9 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse','jikimo_attachment_viewer'],
|
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse','jikimo_attachment_viewer', 'jikimo_sale_multiple_supply_methods'],
|
||||||
'data': [
|
'data': [
|
||||||
|
'data/cron_data.xml',
|
||||||
'data/stock_data.xml',
|
'data/stock_data.xml',
|
||||||
'data/empty_racks_data.xml',
|
'data/empty_racks_data.xml',
|
||||||
'data/panel_data.xml',
|
'data/panel_data.xml',
|
||||||
@@ -21,6 +22,8 @@
|
|||||||
'wizard/workpiece_delivery_views.xml',
|
'wizard/workpiece_delivery_views.xml',
|
||||||
'wizard/rework_wizard_views.xml',
|
'wizard/rework_wizard_views.xml',
|
||||||
'wizard/production_wizard_views.xml',
|
'wizard/production_wizard_views.xml',
|
||||||
|
'wizard/production_technology_wizard_views.xml',
|
||||||
|
'wizard/production_technology_re_adjust_wizard_views.xml',
|
||||||
'views/mrp_views_menus.xml',
|
'views/mrp_views_menus.xml',
|
||||||
'views/agv_scheduling_views.xml',
|
'views/agv_scheduling_views.xml',
|
||||||
'views/stock_lot_views.xml',
|
'views/stock_lot_views.xml',
|
||||||
|
|||||||
29
sf_manufacturing/data/cron_data.xml
Normal file
29
sf_manufacturing/data/cron_data.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<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>
|
||||||
@@ -11,3 +11,5 @@ from . import production_line_base
|
|||||||
from . import agv_setting
|
from . import agv_setting
|
||||||
from . import agv_scheduling
|
from . import agv_scheduling
|
||||||
from . import res_config_setting
|
from . import res_config_setting
|
||||||
|
from . import sf_technology_design
|
||||||
|
from . import sf_production_common
|
||||||
|
|||||||
@@ -6,42 +6,29 @@ class ModelType(models.Model):
|
|||||||
_description = '模型类型'
|
_description = '模型类型'
|
||||||
|
|
||||||
name = fields.Char('名称')
|
name = fields.Char('名称')
|
||||||
embryo_tolerance = fields.Integer('坯料容余')
|
# embryo_tolerance = fields.Char('坯料容余')
|
||||||
|
embryo_tolerance_id = fields.Many2one('sf.embryo.redundancy', string='坯料容余')
|
||||||
product_routing_tmpl_ids = fields.One2many('sf.product.model.type.routing.sort', 'product_model_type_id',
|
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',
|
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_routing_tmpl_ids = fields.One2many('sf.surface_technics.model.type.routing.sort',
|
||||||
'surface_technics_model_type_id',
|
'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):
|
class ProductModelTypeRoutingSort(models.Model):
|
||||||
_name = 'sf.product.model.type.routing.sort'
|
_name = 'sf.product.model.type.routing.sort'
|
||||||
_description = '成品工序排序'
|
_description = '成品工序排序(自动化产线加工)'
|
||||||
|
|
||||||
sequence = fields.Integer('Sequence')
|
sequence = fields.Integer('Sequence')
|
||||||
route_workcenter_id = fields.Many2one('mrp.routing.workcenter',
|
route_workcenter_id = fields.Many2one('mrp.routing.workcenter',
|
||||||
domain=[('routing_type', 'in', ['装夹预调', 'CNC加工', '解除装夹'])])
|
domain=[('routing_type', 'in', ['装夹预调', 'CNC加工', '解除装夹'])])
|
||||||
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
|
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')
|
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')
|
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
|
||||||
product_model_type_id = fields.Many2one('sf.model.type')
|
product_model_type_id = fields.Many2one('sf.model.type')
|
||||||
|
|
||||||
@@ -57,24 +44,7 @@ class EmbryoModelTypeRoutingSort(models.Model):
|
|||||||
sequence = fields.Integer('Sequence')
|
sequence = fields.Integer('Sequence')
|
||||||
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['切割'])])
|
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['切割'])])
|
||||||
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
|
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')
|
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')
|
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
|
||||||
embryo_model_type_id = fields.Many2one('sf.model.type')
|
embryo_model_type_id = fields.Many2one('sf.model.type')
|
||||||
|
|
||||||
@@ -90,24 +60,7 @@ class SurfaceTechnicsModelTypeRoutingSort(models.Model):
|
|||||||
sequence = fields.Integer('Sequence')
|
sequence = fields.Integer('Sequence')
|
||||||
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['表面工艺'])])
|
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['表面工艺'])])
|
||||||
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
|
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')
|
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')
|
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
|
||||||
surface_technics_model_type_id = fields.Many2one('sf.model.type')
|
surface_technics_model_type_id = fields.Many2one('sf.model.type')
|
||||||
|
|
||||||
@@ -116,3 +69,18 @@ class SurfaceTechnicsModelTypeRoutingSort(models.Model):
|
|||||||
'route_model_type_uniq', 'unique (route_workcenter_id,surface_technics_model_type_id)',
|
'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')
|
||||||
|
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)', '成品工序不能重复!')
|
||||||
|
]
|
||||||
@@ -34,6 +34,95 @@ class MrpProduction(models.Model):
|
|||||||
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
|
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
|
||||||
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
|
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
|
||||||
|
|
||||||
|
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('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
||||||
def _compute_deadline_of_delivery(self):
|
def _compute_deadline_of_delivery(self):
|
||||||
for production in self:
|
for production in self:
|
||||||
@@ -98,6 +187,7 @@ class MrpProduction(models.Model):
|
|||||||
# ])
|
# ])
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('draft', '草稿'),
|
('draft', '草稿'),
|
||||||
|
('technology_to_confirmed', '待工艺确认'),
|
||||||
('confirmed', '待排程'),
|
('confirmed', '待排程'),
|
||||||
('pending_cam', '待加工'),
|
('pending_cam', '待加工'),
|
||||||
('progress', '加工中'),
|
('progress', '加工中'),
|
||||||
@@ -148,6 +238,8 @@ class MrpProduction(models.Model):
|
|||||||
|
|
||||||
quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True)
|
quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True)
|
||||||
|
|
||||||
|
part_name = fields.Char(string='零件名称', related='product_id.part_name', readonly=True)
|
||||||
|
|
||||||
@api.depends('product_id.manual_quotation')
|
@api.depends('product_id.manual_quotation')
|
||||||
def _compute_manual_quotation(self):
|
def _compute_manual_quotation(self):
|
||||||
for item in self:
|
for item in self:
|
||||||
@@ -158,6 +250,7 @@ class MrpProduction(models.Model):
|
|||||||
is_remanufacture = fields.Boolean('是否重新制造', default=False)
|
is_remanufacture = fields.Boolean('是否重新制造', default=False)
|
||||||
remanufacture_count = fields.Integer("重新制造订单数量", compute='_compute_remanufacture_production_ids')
|
remanufacture_count = fields.Integer("重新制造订单数量", compute='_compute_remanufacture_production_ids')
|
||||||
remanufacture_production_id = fields.Many2one('mrp.production', string='')
|
remanufacture_production_id = fields.Many2one('mrp.production', string='')
|
||||||
|
technology_design_ids = fields.One2many('sf.technology.design', 'production_id', string='工艺设计')
|
||||||
|
|
||||||
@api.depends('remanufacture_production_id')
|
@api.depends('remanufacture_production_id')
|
||||||
def _compute_remanufacture_production_ids(self):
|
def _compute_remanufacture_production_ids(self):
|
||||||
@@ -214,16 +307,17 @@ class MrpProduction(models.Model):
|
|||||||
precision_rounding=move.product_uom.rounding or move.product_id.uom_id.rounding)
|
precision_rounding=move.product_uom.rounding or move.product_id.uom_id.rounding)
|
||||||
for move in production.move_raw_ids if move.product_id):
|
for move in production.move_raw_ids if move.product_id):
|
||||||
production.state = 'progress'
|
production.state = 'progress'
|
||||||
|
# 新添加的状态逻辑
|
||||||
# # 新添加的状态逻辑
|
|
||||||
if (
|
if (
|
||||||
production.state == 'to_close' or production.state == 'progress') and production.schedule_state == '未排':
|
production.state == 'to_close' or production.state == 'progress') and production.schedule_state == '未排':
|
||||||
production.state = 'confirmed'
|
if not production.workorder_ids:
|
||||||
|
production.state = 'technology_to_confirmed'
|
||||||
|
else:
|
||||||
|
production.state = 'confirmed'
|
||||||
elif production.state == 'pending_cam' and production.schedule_state == '未排':
|
elif production.state == 'pending_cam' and production.schedule_state == '未排':
|
||||||
production.state = 'confirmed'
|
production.state = 'confirmed'
|
||||||
elif production.state == 'to_close' and production.schedule_state == '已排':
|
elif production.state == 'to_close' and production.schedule_state == '已排':
|
||||||
production.state = 'pending_cam'
|
production.state = 'pending_cam'
|
||||||
|
|
||||||
if production.state == 'progress':
|
if production.state == 'progress':
|
||||||
if all(wo_state not in ('progress', 'done', 'rework', 'scrap') for wo_state in
|
if all(wo_state not in ('progress', 'done', 'rework', 'scrap') for wo_state in
|
||||||
production.workorder_ids.mapped('state')):
|
production.workorder_ids.mapped('state')):
|
||||||
@@ -252,6 +346,99 @@ class MrpProduction(models.Model):
|
|||||||
if production.tool_state == '2':
|
if production.tool_state == '2':
|
||||||
production.state = 'rework'
|
production.state = 'rework'
|
||||||
|
|
||||||
|
# 退回调整
|
||||||
|
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:
|
||||||
|
return {
|
||||||
|
'name': _('退回调整'),
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'view_mode': '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 = []
|
||||||
|
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.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)
|
||||||
|
purchase = self.env['purchase.order'].search([('origin', '=', special.production_id.name)])
|
||||||
|
account = self.env['account.move'].search([('id', 'in', purchase.invoice_ids)])
|
||||||
|
if account.state not in ['cancel', False]:
|
||||||
|
if purchase.name not in account_moves:
|
||||||
|
account_moves.append(purchase.name)
|
||||||
|
if account_moves:
|
||||||
|
raise UserError(_("请联系工厂生产经理对采购订单为%s生成的账单进行取消", ", ".join(account_moves)))
|
||||||
|
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:
|
||||||
|
return {
|
||||||
|
'name': _('工艺确认'),
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'view_mode': '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
|
||||||
|
|
||||||
def action_check(self):
|
def action_check(self):
|
||||||
"""
|
"""
|
||||||
审核启用
|
审核启用
|
||||||
@@ -490,121 +677,81 @@ class MrpProduction(models.Model):
|
|||||||
'state': 'pending',
|
'state': 'pending',
|
||||||
}]
|
}]
|
||||||
if production.product_id.categ_id.type == '成品':
|
if production.product_id.categ_id.type == '成品':
|
||||||
# # 根据加工面板的面数及对应的工序模板生成工单
|
# # 根据工序设计生成工单
|
||||||
i = 0
|
for route in production.technology_design_ids:
|
||||||
processing_panel_len = len(production.product_id.model_processing_panel.split(','))
|
if route.route_id.routing_type not in ['表面工艺']:
|
||||||
for k in (production.product_id.model_processing_panel.split(',')):
|
workorders_values.append(
|
||||||
product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
|
self.env['mrp.workorder'].json_workorder_str(production, route))
|
||||||
[('product_model_type_id', '=', production.product_id.product_model_type_id.id)],
|
else:
|
||||||
order='sequence asc'
|
product_production_process = self.env['product.template'].search(
|
||||||
)
|
[('server_product_process_parameters_id', '=', route.process_parameters_id.id)])
|
||||||
i += 1
|
workorders_values.append(
|
||||||
for route in product_routing_workcenter:
|
self.env[
|
||||||
if route.is_repeat is True:
|
'mrp.workorder']._json_workorder_surface_process_str(
|
||||||
workorders_values.append(
|
production, route, product_production_process.seller_ids[0].partner_id.id))
|
||||||
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', '=', 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 == '坯料':
|
elif production.product_id.categ_id.type == '坯料':
|
||||||
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
|
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
|
||||||
[('embryo_model_type_id', '=', production.product_id.embryo_model_type_id.id)],
|
[('embryo_model_type_id', '=', production.product_id.embryo_model_type_id.id)],
|
||||||
order='sequence asc'
|
order='sequence asc'
|
||||||
)
|
)
|
||||||
for route in embryo_routing_workcenter:
|
for route_embryo in embryo_routing_workcenter:
|
||||||
workorders_values.append(
|
workorders_values.append(
|
||||||
self.env['mrp.workorder'].json_workorder_str('', production, route))
|
self.env['mrp.workorder'].json_workorder_str('', production, route_embryo))
|
||||||
production.workorder_ids = workorders_values
|
production.workorder_ids = workorders_values
|
||||||
# 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)])
|
|
||||||
if process_parameter_workorder:
|
|
||||||
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:
|
for workorder in production.workorder_ids:
|
||||||
workorder.duration_expected = workorder._get_duration_expected()
|
workorder.duration_expected = workorder._get_duration_expected()
|
||||||
|
|
||||||
|
# 外协出入库单处理
|
||||||
|
def get_subcontract_pick_purchase(self):
|
||||||
|
production_all = self.sorted(lambda x: x.id)
|
||||||
|
product_id_to_production_names = {}
|
||||||
|
grouped_product_ids = {k: list(g) for k, g in
|
||||||
|
groupby(production_all, key=lambda x: x.product_id.id)}
|
||||||
|
for product_id, pd in grouped_product_ids.items():
|
||||||
|
product_id_to_production_names[product_id] = [p.name for p in pd]
|
||||||
|
for production in production_all:
|
||||||
|
process_parameter_workorder = self.env['mrp.workorder'].search(
|
||||||
|
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
|
||||||
|
('is_subcontract', '=', True)], order='sequence asc')
|
||||||
|
if process_parameter_workorder:
|
||||||
|
consecutive_workorders = []
|
||||||
|
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
|
||||||
|
for i, workorder in enumerate(sorted_workorders):
|
||||||
|
# 检查当前工作订单和下一个工作订单是否连续,并且供应商相同
|
||||||
|
if workorder.sequence == 1:
|
||||||
|
consecutive_workorders.append(workorder)
|
||||||
|
elif workorder.sequence == sorted_workorders[
|
||||||
|
i - 1].sequence + 1 and workorder.supplier_id.id == sorted_workorders[i - 1].supplier_id.id:
|
||||||
|
consecutive_workorders.append(workorder)
|
||||||
|
else:
|
||||||
|
# 处理连续组,如果它不为空
|
||||||
|
if consecutive_workorders:
|
||||||
|
# 创建出库拣货单和采购订单
|
||||||
|
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
||||||
|
self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
|
||||||
|
product_id_to_production_names)
|
||||||
|
if i < len(sorted_workorders) - 1:
|
||||||
|
# 重置连续组,并添加当前工作订单
|
||||||
|
consecutive_workorders = [workorder]
|
||||||
|
else:
|
||||||
|
# 判断最后一笔:
|
||||||
|
if workorder.sequence == sorted_workorders[
|
||||||
|
i - 1].sequence and workorder.supplier_id.id == sorted_workorders[
|
||||||
|
i - 1].supplier_id.id:
|
||||||
|
consecutive_workorders = [workorder]
|
||||||
|
else:
|
||||||
|
# 立即创建出库拣货单和采购订单
|
||||||
|
self.env['stock.picking'].create_outcontract_picking(workorder, production)
|
||||||
|
self.env['purchase.order'].get_purchase_order(workorder, production,
|
||||||
|
product_id_to_production_names)
|
||||||
|
consecutive_workorders = []
|
||||||
|
|
||||||
|
# 处理最后一个组,即使它可能只有一个工作订单
|
||||||
|
if consecutive_workorders:
|
||||||
|
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
||||||
|
self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
|
||||||
|
product_id_to_production_names)
|
||||||
|
|
||||||
# 工单排序
|
# 工单排序
|
||||||
def _reset_work_order_sequence1(self, k):
|
def _reset_work_order_sequence1(self, k):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
@@ -672,6 +819,41 @@ class MrpProduction(models.Model):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _reset_work_order_sequence(self):
|
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:
|
||||||
|
for order_id in rec.workorder_ids.filtered(lambda item: item.sequence > 0):
|
||||||
|
if work_id.name == order_id.name:
|
||||||
|
work_id.sequence = order_id.sequence + 1
|
||||||
|
break
|
||||||
|
# 对该工单之后的工单工序进行加一
|
||||||
|
work_order_ids = rec.workorder_ids.filtered(lambda item: item.sequence >= work_id.sequence)
|
||||||
|
for work in work_order_ids:
|
||||||
|
work.sequence = work.sequence + 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
# 将工艺设计生成的工单序号赋值给工单的序号
|
||||||
|
for work in workorder_ids:
|
||||||
|
td_ids = technology_design_ids.filtered(
|
||||||
|
lambda item: (item.route_id.name in work.name and item.process_parameters_id
|
||||||
|
and item.process_parameters_id == work.surface_technics_parameters_id) or
|
||||||
|
(item.route_id.name == work.name and item.panel
|
||||||
|
and item.panel == work.processing_panel))
|
||||||
|
if td_ids:
|
||||||
|
work.sequence = td_ids[0].sequence
|
||||||
|
|
||||||
|
def _reset_work_order_sequence_1(self):
|
||||||
|
"""
|
||||||
|
工单工序排序方法(旧)
|
||||||
|
"""
|
||||||
for rec in self:
|
for rec in self:
|
||||||
workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework'))
|
workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework'))
|
||||||
# 产品模型类型
|
# 产品模型类型
|
||||||
@@ -838,6 +1020,7 @@ class MrpProduction(models.Model):
|
|||||||
backorders = backorders - productions_to_backorder
|
backorders = backorders - productions_to_backorder
|
||||||
|
|
||||||
productions_not_to_backorder._post_inventory(cancel_backorder=True)
|
productions_not_to_backorder._post_inventory(cancel_backorder=True)
|
||||||
|
# 查出最后一张工单完成入库操作
|
||||||
# if self.workorder_ids.filtered(lambda w: w.routing_type in ['表面工艺']):
|
# if self.workorder_ids.filtered(lambda w: w.routing_type in ['表面工艺']):
|
||||||
# move_finish = self.env['stock.move'].search([('created_production_id', '=', self.id)])
|
# move_finish = self.env['stock.move'].search([('created_production_id', '=', self.id)])
|
||||||
# if move_finish:
|
# if move_finish:
|
||||||
@@ -1187,6 +1370,19 @@ class MrpProduction(models.Model):
|
|||||||
'user_id': production.user_id.id}
|
'user_id': production.user_id.id}
|
||||||
return production_values_str
|
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 '人工线下加工'
|
||||||
|
|
||||||
|
|
||||||
class sf_detection_result(models.Model):
|
class sf_detection_result(models.Model):
|
||||||
_name = 'sf.detection.result'
|
_name = 'sf.detection.result'
|
||||||
@@ -1216,10 +1412,6 @@ class sf_detection_result(models.Model):
|
|||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
'res_id': self.id,
|
'res_id': self.id,
|
||||||
'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')],
|
'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')],
|
||||||
# 'view_mode': 'form',
|
|
||||||
# 'context': {
|
|
||||||
# 'default_id': self.id
|
|
||||||
# },
|
|
||||||
'target': 'new'
|
'target': 'new'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,21 +7,23 @@ class ResMrpRoutingWorkcenter(models.Model):
|
|||||||
_inherit = 'mrp.routing.workcenter'
|
_inherit = 'mrp.routing.workcenter'
|
||||||
|
|
||||||
routing_type = fields.Selection([
|
routing_type = fields.Selection([
|
||||||
# ('获取CNC加工程序', '获取CNC加工程序'),
|
|
||||||
('装夹预调', '装夹预调'),
|
('装夹预调', '装夹预调'),
|
||||||
# ('前置三元定位检测', '前置三元定位检测'),
|
|
||||||
('CNC加工', 'CNC加工'),
|
('CNC加工', 'CNC加工'),
|
||||||
# ('后置三元质量检测', '后置三元质量检测'),
|
|
||||||
('解除装夹', '解除装夹'),
|
('解除装夹', '解除装夹'),
|
||||||
('切割', '切割'),
|
('切割', '切割'),
|
||||||
('表面工艺', '表面工艺')
|
('表面工艺', '表面工艺')
|
||||||
], string="工序类型")
|
], string="工序类型")
|
||||||
|
routing_tag = fields.Selection([
|
||||||
|
('standard', '标准'),
|
||||||
|
('special', '特殊')
|
||||||
|
], string="标签")
|
||||||
is_repeat = fields.Boolean('重复', default=False)
|
is_repeat = fields.Boolean('重复', default=False)
|
||||||
workcenter_id = fields.Many2one('mrp.workcenter', required=False)
|
workcenter_id = fields.Many2one('mrp.workcenter', required=False)
|
||||||
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
|
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
|
||||||
bom_id = fields.Many2one('mrp.bom', required=False)
|
bom_id = fields.Many2one('mrp.bom', required=False)
|
||||||
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
|
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
|
||||||
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
|
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
|
||||||
|
|
||||||
def get_no(self):
|
def get_no(self):
|
||||||
international_standards = self.search(
|
international_standards = self.search(
|
||||||
[('code', '!=', ''), ('active', 'in', [True, False])],
|
[('code', '!=', ''), ('active', 'in', [True, False])],
|
||||||
@@ -78,3 +80,13 @@ class ResMrpRoutingWorkcenter(models.Model):
|
|||||||
else:
|
else:
|
||||||
workcenter_id = workcenter_ids[0]
|
workcenter_id = workcenter_ids[0]
|
||||||
return workcenter_id
|
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'):
|
||||||
|
technology_design = self.env['sf.technology.design'].search(
|
||||||
|
[('production_id', '=', self._context.get('production_id'))])
|
||||||
|
route_ids = [t.route_id.id for t in technology_design]
|
||||||
|
domain = [('id', 'not in', route_ids)]
|
||||||
|
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
||||||
|
return super()._name_search(name, args, operator, limit, name_get_uid)
|
||||||
|
|||||||
@@ -38,13 +38,10 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
processing_panel = fields.Char('加工面')
|
processing_panel = fields.Char('加工面')
|
||||||
sequence = fields.Integer(string='工序')
|
sequence = fields.Integer(string='工序')
|
||||||
routing_type = fields.Selection([
|
routing_type = fields.Selection([
|
||||||
# ('获取CNC加工程序', '获取CNC加工程序'),
|
|
||||||
('装夹预调', '装夹预调'),
|
('装夹预调', '装夹预调'),
|
||||||
# ('前置三元定位检测', '前置三元定位检测'),
|
|
||||||
('CNC加工', 'CNC加工'),
|
('CNC加工', 'CNC加工'),
|
||||||
# ('后置三元质量检测', '后置三元质量检测'),
|
|
||||||
('解除装夹', '解除装夹'),
|
('解除装夹', '解除装夹'),
|
||||||
('切割', '切割'), ('表面工艺', '表面工艺')
|
('切割', '切割'), ('表面工艺', '表面工艺'), ('线切割', '线切割')
|
||||||
], string="工序类型")
|
], string="工序类型")
|
||||||
results = fields.Char('结果')
|
results = fields.Char('结果')
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
@@ -147,11 +144,93 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
|
|
||||||
tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True)
|
tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True)
|
||||||
|
|
||||||
|
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')
|
@api.depends('name', 'production_id.name')
|
||||||
def _compute_surface_technics_picking_ids(self):
|
def _compute_surface_technics_picking_ids(self):
|
||||||
for workorder in self:
|
for workorder in self:
|
||||||
if workorder.routing_type == '表面工艺':
|
if workorder.routing_type == '表面工艺':
|
||||||
domain = [('origin', '=', workorder.production_id.name)]
|
domain = [('origin', '=', workorder.production_id.name), ('state', 'not in', ['cancel'])]
|
||||||
previous_workorder = self.env['mrp.workorder'].search(
|
previous_workorder = self.env['mrp.workorder'].search(
|
||||||
[('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
|
[('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
|
||||||
('production_id', '=', workorder.production_id.id)])
|
('production_id', '=', workorder.production_id.id)])
|
||||||
@@ -185,24 +264,47 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
def _compute_surface_technics_purchase_ids(self):
|
def _compute_surface_technics_purchase_ids(self):
|
||||||
for order in self:
|
for order in self:
|
||||||
if order.routing_type == '表面工艺':
|
if order.routing_type == '表面工艺':
|
||||||
production_programming = self.env['mrp.production'].search(
|
if order.production_id.production_type == '自动化产线加工':
|
||||||
[('programming_no', '=', order.production_id.programming_no)], order='name asc')
|
domain = [('programming_no', '=', order.production_id.programming_no)]
|
||||||
production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
|
else:
|
||||||
|
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_list = [production.name for production in production_programming]
|
||||||
purchase = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
|
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 = [('origin', '=', ','.join(production_list))]
|
||||||
|
purchase = self.env['purchase.order'].search(domain)
|
||||||
|
if not purchase:
|
||||||
|
order.surface_technics_purchase_count = 0
|
||||||
for line in purchase.order_line:
|
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(
|
if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id:
|
||||||
production_no_remanufacture):
|
if (line.product_qty == len(production_no_remanufacture)) or technology_design.is_auto is False:
|
||||||
order.surface_technics_purchase_count = len(purchase)
|
order.surface_technics_purchase_count = len(purchase)
|
||||||
else:
|
else:
|
||||||
order.surface_technics_purchase_count = 0
|
order.surface_technics_purchase_count = 0
|
||||||
|
|
||||||
def action_view_surface_technics_purchase(self):
|
def action_view_surface_technics_purchase(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
production_programming = self.env['mrp.production'].search(
|
if self.routing_type == '表面工艺':
|
||||||
[('programming_no', '=', self.production_id.programming_no)], order='name asc')
|
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_list = [production.name for production in production_programming]
|
||||||
purchase_orders = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
|
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:
|
||||||
|
domain = [('origin', '=', ','.join(production_list))]
|
||||||
|
purchase_orders = self.env['purchase.order'].search(domain)
|
||||||
result = {
|
result = {
|
||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"res_model": "purchase.order",
|
"res_model": "purchase.order",
|
||||||
@@ -228,6 +330,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
part_number = fields.Char(related='production_id.part_number', string='零件图号')
|
part_number = fields.Char(related='production_id.part_number', string='零件图号')
|
||||||
machining_drawings = fields.Binary('2D加工图纸', related='production_id.part_drawing', readonly=True)
|
machining_drawings = fields.Binary('2D加工图纸', related='production_id.part_drawing', readonly=True)
|
||||||
quality_standard = fields.Binary('质检标准', related='production_id.quality_standard', 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([
|
process_state = fields.Selection([
|
||||||
@@ -327,7 +430,8 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
|
|
||||||
@api.constrains('blocked_by_workorder_ids')
|
@api.constrains('blocked_by_workorder_ids')
|
||||||
def _check_no_cyclic_dependencies(self):
|
def _check_no_cyclic_dependencies(self):
|
||||||
if self.production_id.state not in ['rework'] and self.state not in ['rework']:
|
if self.production_id.state not in ['rework', 'technology_to_confirmed', 'confirmed'] and self.state not in [
|
||||||
|
'rework']:
|
||||||
if not self._check_m2m_recursion('blocked_by_workorder_ids'):
|
if not self._check_m2m_recursion('blocked_by_workorder_ids'):
|
||||||
raise ValidationError(_("您不能创建周期性的依赖关系."))
|
raise ValidationError(_("您不能创建周期性的依赖关系."))
|
||||||
|
|
||||||
@@ -338,8 +442,9 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
for workorder in self.blocked_by_workorder_ids:
|
for workorder in self.blocked_by_workorder_ids:
|
||||||
if workorder.state in ['done', 'cancel', 'rework']:
|
if workorder.state in ['done', 'cancel', 'rework']:
|
||||||
continue
|
continue
|
||||||
workorder._plan_workorder(replan)
|
if workorder.production_id.state not in ['technology_to_confirmed', 'confirmed']:
|
||||||
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
|
# Plan only suitable workorders
|
||||||
if self.state not in ['pending', 'waiting', 'ready']:
|
if self.state not in ['pending', 'waiting', 'ready']:
|
||||||
return
|
return
|
||||||
@@ -669,35 +774,14 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
# 拼接工单对象属性值
|
# 拼接工单对象属性值
|
||||||
def json_workorder_str(self, k, production, route, item):
|
def json_workorder_str(self, production, route):
|
||||||
# 计算预计时长duration_expected
|
# 计算预计时长duration_expected
|
||||||
routing_types = ['切割', '装夹预调', 'CNC加工', '解除装夹']
|
routing_types = ['切割', '装夹预调', 'CNC加工', '解除装夹']
|
||||||
if route.routing_type in routing_types:
|
if route.route_id.routing_type in routing_types:
|
||||||
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
|
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
|
||||||
[('name', '=', route.routing_type)])
|
[('name', '=', route.route_id.routing_type)])
|
||||||
duration_expected = routing_workcenter.time_cycle
|
duration_expected = routing_workcenter.time_cycle
|
||||||
reserved_duration = routing_workcenter.reserved_duration
|
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:
|
else:
|
||||||
duration_expected = 60
|
duration_expected = 60
|
||||||
reserved_duration = 30
|
reserved_duration = 30
|
||||||
@@ -705,26 +789,20 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
'product_uom_id': production.product_uom_id.id,
|
'product_uom_id': production.product_uom_id.id,
|
||||||
'qty_producing': 0,
|
'qty_producing': 0,
|
||||||
'operation_id': False,
|
'operation_id': False,
|
||||||
'name': route.route_workcenter_id.name,
|
'name': route.route_id.name,
|
||||||
'processing_panel': k,
|
'processing_panel': route.panel,
|
||||||
'quality_point_ids': route.route_workcenter_id.quality_point_ids,
|
'sequence': route.sequence,
|
||||||
'routing_type': route.routing_type,
|
'quality_point_ids': route.route_id.quality_point_ids,
|
||||||
# 'work_state': '待发起',
|
'routing_type': route.route_id.routing_type,
|
||||||
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
|
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.route_id.workcenter_ids.ids,
|
||||||
route.routing_type,
|
route.route_id.routing_type,
|
||||||
production.product_id),
|
production.product_id),
|
||||||
# 设定初始化值,避免出现变成bool问题
|
# 设定初始化值,避免出现变成bool问题
|
||||||
'date_planned_start': datetime.now(),
|
'date_planned_start': datetime.now(),
|
||||||
'date_planned_finished': datetime.now() + timedelta(days=1),
|
'date_planned_finished': datetime.now() + timedelta(days=1),
|
||||||
'duration_expected': duration_expected,
|
'duration_expected': duration_expected,
|
||||||
'duration': 0,
|
'duration': 0,
|
||||||
'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,
|
'reserved_duration': reserved_duration,
|
||||||
}]
|
}]
|
||||||
return workorders_values_str
|
return workorders_values_str
|
||||||
@@ -755,22 +833,23 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
]
|
]
|
||||||
|
|
||||||
# 拼接工单对象属性值(表面工艺)
|
# 拼接工单对象属性值(表面工艺)
|
||||||
def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id):
|
def _json_workorder_surface_process_str(self, production, route, supplier_id):
|
||||||
workorders_values_str = [0, '', {
|
workorders_values_str = [0, '', {
|
||||||
'product_uom_id': production.product_uom_id.id,
|
'product_uom_id': production.product_uom_id.id,
|
||||||
'qty_producing': 0,
|
'qty_producing': 0,
|
||||||
'operation_id': False,
|
'operation_id': False,
|
||||||
'name': '%s-%s' % (route.name, process_parameter.name),
|
'name': route.process_parameters_id.display_name,
|
||||||
'processing_panel': '',
|
'processing_panel': '',
|
||||||
|
'sequence': route.sequence,
|
||||||
'routing_type': '表面工艺',
|
'routing_type': '表面工艺',
|
||||||
'surface_technics_parameters_id': process_parameter.id,
|
'surface_technics_parameters_id': route.process_parameters_id.id,
|
||||||
'work_state': '',
|
'work_state': '',
|
||||||
'supplier_id': supplier_id,
|
'supplier_id': supplier_id,
|
||||||
'is_subcontract': True if process_parameter.gain_way == '外协' else False,
|
'is_subcontract': True if route.process_parameters_id.gain_way == '外协' else False,
|
||||||
'workcenter_id': self.env[
|
'workcenter_id': self.env[
|
||||||
'mrp.workcenter'].get_process_outsourcing_workcenter() if process_parameter.gain_way == '外协' else
|
'mrp.workcenter'].get_process_outsourcing_workcenter() if route.process_parameters_id.gain_way == '外协' else
|
||||||
self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
|
self.env['mrp.routing.workcenter'].get_workcenter(route.route_id.workcenter_ids.ids,
|
||||||
route.routing_type,
|
route.route_id.routing_type,
|
||||||
production.product_id),
|
production.product_id),
|
||||||
'date_planned_start': datetime.now(),
|
'date_planned_start': datetime.now(),
|
||||||
'date_planned_finished': datetime.now() + timedelta(days=1),
|
'date_planned_finished': datetime.now() + timedelta(days=1),
|
||||||
@@ -922,10 +1001,47 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
return workorders_values_str
|
return workorders_values_str
|
||||||
|
|
||||||
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
|
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
|
||||||
'production_id.tool_state')
|
'production_id.tool_state', 'production_id.schedule_state')
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
super()._compute_state()
|
# super()._compute_state()
|
||||||
for workorder in self:
|
for workorder in self:
|
||||||
|
if workorder.sequence != 1:
|
||||||
|
previous_workorder = self.env['mrp.workorder'].search(
|
||||||
|
[('production_id', '=', workorder.production_id.id),
|
||||||
|
('sequence', '=', workorder.sequence - 1)])
|
||||||
|
if workorder.state == 'pending':
|
||||||
|
if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
|
||||||
|
if workorder.production_id.reservation_state == 'assigned' and workorder.production_id.schedule_state == '已排':
|
||||||
|
if (workorder.sequence == 1 and not workorder.blocked_by_workorder_ids) or (
|
||||||
|
workorder.blocked_by_workorder_ids.state in ('done', 'cancel')) or (
|
||||||
|
previous_workorder.state in (
|
||||||
|
'done', 'cancel') and not workorder.blocked_by_workorder_ids):
|
||||||
|
workorder.state = 'ready'
|
||||||
|
continue
|
||||||
|
if workorder.production_id.schedule_state == '未排' and workorder.state in ('waiting', 'ready'):
|
||||||
|
if workorder.sequence != 1:
|
||||||
|
workorder.state = 'pending'
|
||||||
|
continue
|
||||||
|
if workorder.state not in ('waiting', 'ready'):
|
||||||
|
continue
|
||||||
|
if workorder.state in (
|
||||||
|
'waiting') and workorder.sequence == 1 and workorder.production_id.schedule_state == '已排':
|
||||||
|
workorder.state = 'ready'
|
||||||
|
continue
|
||||||
|
if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
|
||||||
|
workorder.state = 'pending'
|
||||||
|
if workorder.state in ['waiting']:
|
||||||
|
if previous_workorder.state == 'waiting':
|
||||||
|
workorder.state = 'pending'
|
||||||
|
if workorder.sequence == 1 and workorder.state == 'pending':
|
||||||
|
workorder.state = 'waiting'
|
||||||
|
continue
|
||||||
|
if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'):
|
||||||
|
continue
|
||||||
|
if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting' and workorder.production_id.schedule_state == '已排':
|
||||||
|
workorder.state = 'ready'
|
||||||
|
elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready':
|
||||||
|
workorder.state = 'waiting'
|
||||||
re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
|
re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
|
||||||
('processing_panel', '=', workorder.processing_panel),
|
('processing_panel', '=', workorder.processing_panel),
|
||||||
('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
|
('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
|
||||||
@@ -989,7 +1105,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
workorder.state = 'ready'
|
workorder.state = 'ready'
|
||||||
else:
|
else:
|
||||||
production_programming = self.env['mrp.production'].search(
|
production_programming = self.env['mrp.production'].search(
|
||||||
[('programming_no', '=', self.production_id.programming_no)], order='name asc')
|
[('origin', '=', self.production_id.origin)], order='name asc')
|
||||||
production_no_remanufacture = production_programming.filtered(
|
production_no_remanufacture = production_programming.filtered(
|
||||||
lambda a: a.is_remanufacture is False)
|
lambda a: a.is_remanufacture is False)
|
||||||
production_list = [production.name for production in production_programming]
|
production_list = [production.name for production in production_programming]
|
||||||
@@ -1005,99 +1121,68 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
elif workorder.production_id.state == 'scrap':
|
elif workorder.production_id.state == 'scrap':
|
||||||
if workorder.routing_type == '解除装夹' and unclamp_workorder.test_results == '报废':
|
if workorder.routing_type == '解除装夹' and unclamp_workorder.test_results == '报废':
|
||||||
workorder.state = 'waiting'
|
workorder.state = 'waiting'
|
||||||
if workorder.routing_type == '装夹预调' and workorder.state in ['waiting', 'ready', 'pending']:
|
# if workorder.routing_type == '装夹预调' and workorder.state in ['waiting', 'ready', 'pending']:
|
||||||
workorder_ids = workorder.production_id.workorder_ids
|
# workorder_ids = workorder.production_id.workorder_ids
|
||||||
work_bo = True
|
# work_bo = True
|
||||||
for wo in workorder_ids.filtered(lambda a: a.routing_type == '装夹预调' and a.state == 'rework'):
|
# for wo in workorder_ids.filtered(lambda a: a.routing_type == '装夹预调' and a.state == 'rework'):
|
||||||
if not workorder_ids.filtered(
|
# if not workorder_ids.filtered(
|
||||||
lambda a: (a.routing_type == '装夹预调' and a.state not in ['rework', 'cancel']
|
# lambda a: (a.routing_type == '装夹预调' and a.state not in ['rework', 'cancel']
|
||||||
and a.processing_panel == wo.processing_panel)):
|
# and a.processing_panel == wo.processing_panel)):
|
||||||
work_bo = False
|
# work_bo = False
|
||||||
break
|
# break
|
||||||
if (workorder.production_id.programming_state == '已编程' and work_bo
|
# if (workorder.production_id.programming_state == '已编程' and work_bo
|
||||||
and not workorder_ids.filtered(lambda a: a.sequence == 0)):
|
# and not workorder_ids.filtered(lambda a: a.sequence == 0)):
|
||||||
# 当工单对应制造订单的功能刀具状态为 【无效刀】时,先对的第一个装夹预调工单状态设置为 【等待组件】
|
# # 当工单对应制造订单的功能刀具状态为 【无效刀】时,先对的第一个装夹预调工单状态设置为 【等待组件】
|
||||||
if workorder.production_id.tool_state in ['1', '2']:
|
# if workorder.production_id.tool_state in ['1', '2']:
|
||||||
if workorder.state in ['ready']:
|
# if workorder.state in ['ready']:
|
||||||
workorder.state = 'waiting'
|
# workorder.state = 'waiting'
|
||||||
continue
|
# continue
|
||||||
elif workorder.state in ['waiting']:
|
# elif workorder.state in ['waiting']:
|
||||||
continue
|
# continue
|
||||||
elif workorder.state == 'pending' and workorder == self.search(
|
# elif workorder.state == 'pending' and workorder == self.search(
|
||||||
[('production_id', '=', workorder.production_id.id),
|
# [('production_id', '=', workorder.production_id.id),
|
||||||
('routing_type', '=', '装夹预调'),
|
# ('routing_type', '=', '装夹预调'),
|
||||||
('state', 'not in', ['rework', 'done', 'cancel'])],
|
# ('state', 'not in', ['rework', 'done', 'cancel'])],
|
||||||
limit=1,
|
# limit=1,
|
||||||
order="sequence"):
|
# order="sequence"):
|
||||||
workorder.state = 'waiting'
|
# workorder.state = 'waiting'
|
||||||
continue
|
# continue
|
||||||
elif workorder.production_id.tool_state in ['0']:
|
# elif workorder.production_id.tool_state in ['0']:
|
||||||
if workorder_ids.filtered(lambda a: a.state == 'rework'):
|
# if workorder_ids.filtered(lambda a: a.state == 'rework'):
|
||||||
if not workorder_ids.filtered(
|
# if not workorder_ids.filtered(
|
||||||
lambda a: (a.routing_type not in ['装夹预调'] and
|
# lambda a: (a.routing_type not in ['装夹预调'] and
|
||||||
a.state not in ['pending', 'done', 'rework', 'cancel'])):
|
# a.state not in ['pending', 'done', 'rework', 'cancel'])):
|
||||||
# 查询工序最小的非完工、非返工的装夹预调工单
|
# # 查询工序最小的非完工、非返工的装夹预调工单
|
||||||
work_id = self.search(
|
# work_id = self.search(
|
||||||
[('production_id', '=', workorder.production_id.id),
|
# [('production_id', '=', workorder.production_id.id),
|
||||||
('state', 'not in', ['rework', 'done', 'cancel'])],
|
# ('state', 'not in', ['rework', 'done', 'cancel'])],
|
||||||
limit=1,
|
# limit=1,
|
||||||
order="sequence")
|
# order="sequence")
|
||||||
if work_id.routing_type == '装夹预调':
|
# if work_id.routing_type == '装夹预调':
|
||||||
if workorder == work_id:
|
# if workorder == work_id:
|
||||||
if workorder.production_id.reservation_state == 'assigned':
|
# if workorder.production_id.reservation_state == 'assigned':
|
||||||
workorder.state = 'ready'
|
# workorder.state = 'ready'
|
||||||
elif workorder.production_id.reservation_state != 'assigned':
|
# elif workorder.production_id.reservation_state != 'assigned':
|
||||||
workorder.state = 'waiting'
|
# workorder.state = 'waiting'
|
||||||
continue
|
# continue
|
||||||
elif (workorder.name == '装夹预调' and
|
# elif (workorder.name == '装夹预调' and
|
||||||
workorder.state not in ['rework', 'done', 'cancel']):
|
# workorder.state not in ['rework', 'done', 'cancel']):
|
||||||
if workorder.state != 'pending':
|
# if workorder.state != 'pending':
|
||||||
workorder.state = 'pending'
|
# workorder.state = 'pending'
|
||||||
if workorder.production_id.tool_state in ['1', '2'] and workorder.state == 'ready':
|
# if workorder.production_id.tool_state in ['1', '2'] and workorder.state == 'ready':
|
||||||
workorder.state = 'waiting'
|
# workorder.state = 'waiting'
|
||||||
continue
|
# continue
|
||||||
if (workorder.production_id.tool_state in ['1', '2']
|
# if (workorder.production_id.tool_state in ['1', '2']
|
||||||
and not workorder.production_id.workorder_ids.filtered(lambda a: a.sequence == 0)
|
# and not workorder.production_id.workorder_ids.filtered(lambda a: a.sequence == 0)
|
||||||
and workorder.production_id.programming_state == '编程中' and workorder.name == '装夹预调'):
|
# and workorder.production_id.programming_state == '编程中' and workorder.name == '装夹预调'):
|
||||||
if workorder.state == 'pending' and workorder == self.search(
|
# if workorder.state == 'pending' and workorder == self.search(
|
||||||
[('production_id', '=', workorder.production_id.id),
|
# [('production_id', '=', workorder.production_id.id),
|
||||||
('routing_type', '=', '装夹预调'),
|
# ('routing_type', '=', '装夹预调'),
|
||||||
('state', 'not in', ['rework', 'done', 'cancel'])],
|
# ('state', 'not in', ['rework', 'done', 'cancel'])],
|
||||||
limit=1,
|
# limit=1,
|
||||||
order="sequence"):
|
# order="sequence"):
|
||||||
workorder.state = 'waiting'
|
# workorder.state = 'waiting'
|
||||||
continue
|
# continue
|
||||||
|
|
||||||
# elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'cancel', 'progress',
|
|
||||||
# 'rework']:
|
|
||||||
# per_work = self.env['mrp.workorder'].search(
|
|
||||||
# [('routing_type', '=', '装夹预调'), ('production_id', '=', workorder.production_id.id),
|
|
||||||
# ('processing_panel', '=', workorder.processing_panel), ('is_rework', '=', True)])
|
|
||||||
# if per_work:
|
|
||||||
# workorder.state = 'waiting'
|
|
||||||
# if workorder.routing_type == 'CNC加工' and workorder.state == 'progress':
|
|
||||||
# workorder.state = 'to be detected'
|
|
||||||
|
|
||||||
# for workorder in self:
|
|
||||||
# if workorder.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):
|
def button_start(self):
|
||||||
@@ -1107,7 +1192,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
'actual_start_time': datetime.now()
|
'actual_start_time': datetime.now()
|
||||||
})
|
})
|
||||||
|
|
||||||
if self.routing_type == '装夹预调':
|
if self.sequence == 1:
|
||||||
# 判断是否有坯料的序列号信息
|
# 判断是否有坯料的序列号信息
|
||||||
boolean = False
|
boolean = False
|
||||||
if self.production_id.move_raw_ids:
|
if self.production_id.move_raw_ids:
|
||||||
@@ -1218,8 +1303,8 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
if record.is_rework is False:
|
if record.is_rework is False:
|
||||||
if not record.material_center_point:
|
if not record.material_center_point:
|
||||||
raise UserError("坯料中心点为空,请检查")
|
raise UserError("坯料中心点为空,请检查")
|
||||||
# if record.X_deviation_angle <= 0:
|
# if record.X_deviation_angle <= 0:
|
||||||
# raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
|
# raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
|
||||||
record.process_state = '待加工'
|
record.process_state = '待加工'
|
||||||
# record.write({'process_state': '待加工'})
|
# record.write({'process_state': '待加工'})
|
||||||
record.production_id.process_state = '待加工'
|
record.production_id.process_state = '待加工'
|
||||||
@@ -1249,23 +1334,10 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
'''
|
'''
|
||||||
record.date_finished = datetime.now()
|
record.date_finished = datetime.now()
|
||||||
if record.routing_type == '表面工艺':
|
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:
|
if record.picking_ids:
|
||||||
for pick_item in record.picking_ids:
|
picks = record.picking_ids.filtered(lambda p: p.state not in ('done'))
|
||||||
if pick_item.state not in ['done']:
|
if picks:
|
||||||
raise UserError(
|
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_planned_finished = record.date_planned_finished
|
||||||
tem_date_finished = record.date_finished
|
tem_date_finished = record.date_finished
|
||||||
logging.info('routing_type:%s' % record.routing_type)
|
logging.info('routing_type:%s' % record.routing_type)
|
||||||
|
|||||||
@@ -777,6 +777,7 @@ class ResProductMo(models.Model):
|
|||||||
part_number = fields.Char(string='零件图号', readonly=True)
|
part_number = fields.Char(string='零件图号', readonly=True)
|
||||||
machining_drawings = fields.Binary('2D加工图纸', readonly=True)
|
machining_drawings = fields.Binary('2D加工图纸', readonly=True)
|
||||||
quality_standard = fields.Binary('质检标准', readonly=True)
|
quality_standard = fields.Binary('质检标准', readonly=True)
|
||||||
|
part_name = fields.Char(string='零件名称', readonly=True)
|
||||||
|
|
||||||
@api.constrains('tool_length')
|
@api.constrains('tool_length')
|
||||||
def _check_tool_length_size(self):
|
def _check_tool_length_size(self):
|
||||||
@@ -849,16 +850,23 @@ class ResProductMo(models.Model):
|
|||||||
copy_product_id.product_tmpl_id.active = True
|
copy_product_id.product_tmpl_id.active = True
|
||||||
model_type = self.env['sf.model.type'].search([], limit=1)
|
model_type = self.env['sf.model.type'].search([], limit=1)
|
||||||
attachment = self.attachment_create(item['model_name'], item['model_data'])
|
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 = {
|
vals = {
|
||||||
'name': '%s-%s-%s' % ('P', order_id.name, i),
|
'name': '%s-%s-%s' % ('P', order_id.name, i),
|
||||||
'model_long': item['model_long'] + model_type.embryo_tolerance,
|
'model_long': item['model_long'] + embryo_redundancy_id.long,
|
||||||
'model_width': item['model_width'] + model_type.embryo_tolerance,
|
'model_width': item['model_width'] + embryo_redundancy_id.width,
|
||||||
'model_height': item['model_height'] + model_type.embryo_tolerance,
|
'model_height': item['model_height'] + embryo_redundancy_id.height,
|
||||||
'model_volume': (item['model_long'] + model_type.embryo_tolerance) * (
|
'model_volume': (item['model_long'] + embryo_redundancy_id.long) * (
|
||||||
item['model_width'] + model_type.embryo_tolerance) * (
|
item['model_width'] + embryo_redundancy_id.width) * (
|
||||||
item['model_height'] + model_type.embryo_tolerance),
|
item['model_height'] + embryo_redundancy_id.height),
|
||||||
'product_model_type_id': model_type.id,
|
'product_model_type_id': model_type.id,
|
||||||
# 'model_processing_panel': 'R',
|
'model_processing_panel': item['processing_panel_detail'],
|
||||||
'model_machining_precision': item['model_machining_precision'],
|
'model_machining_precision': item['model_machining_precision'],
|
||||||
'model_code': item['barcode'],
|
'model_code': item['barcode'],
|
||||||
'length': item['model_long'],
|
'length': item['model_long'],
|
||||||
@@ -884,6 +892,7 @@ class ResProductMo(models.Model):
|
|||||||
'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(
|
'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(
|
||||||
item['machining_drawings']),
|
item['machining_drawings']),
|
||||||
'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
|
'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
|
||||||
|
'part_name': item['part_name'],
|
||||||
}
|
}
|
||||||
tax_id = self.env['account.tax'].sudo().search(
|
tax_id = self.env['account.tax'].sudo().search(
|
||||||
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
|
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
|
||||||
@@ -932,31 +941,40 @@ class ResProductMo(models.Model):
|
|||||||
# if surface_technology:
|
# if surface_technology:
|
||||||
# no_bom_copy_product_id.route_ids |= surface_technology
|
# no_bom_copy_product_id.route_ids |= surface_technology
|
||||||
no_bom_copy_product_id.product_tmpl_id.active = True
|
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_id = self.env['sf.production.materials'].search(
|
||||||
[('materials_no', '=', item['texture_code'])])
|
[('materials_no', '=', item['texture_code'])])
|
||||||
materials_type_id = self.env['sf.materials.model'].search(
|
materials_type_id = self.env['sf.materials.model'].search(
|
||||||
[('materials_no', '=', item['texture_type_code'])])
|
[('materials_no', '=', item['texture_type_code'])])
|
||||||
model_type = self.env['sf.model.type'].search([], limit=1)
|
model_type = self.env['sf.model.type'].search([], limit=1)
|
||||||
supplier = self.env['mrp.bom'].get_supplier(materials_type_id)
|
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)
|
logging.info('no_bom_copy_product_supplier-vals:%s' % supplier)
|
||||||
vals = {
|
vals = {
|
||||||
'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R',
|
'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R',
|
||||||
order_id.name, i, materials_id.name, materials_type_id.name,
|
order_id.name, i, materials_id.name, materials_type_id.name,
|
||||||
item['model_long'] + model_type.embryo_tolerance,
|
item['model_long'] + embryo_redundancy_id.long,
|
||||||
item['model_width'] + model_type.embryo_tolerance,
|
item['model_width'] + embryo_redundancy_id.width,
|
||||||
item['model_height'] + model_type.embryo_tolerance),
|
item['model_height'] + embryo_redundancy_id.height),
|
||||||
'length': item['model_long'] + model_type.embryo_tolerance,
|
'length': item['model_long'] + embryo_redundancy_id.long,
|
||||||
'width': item['model_width'] + model_type.embryo_tolerance,
|
'width': item['model_width'] + embryo_redundancy_id.width,
|
||||||
'height': item['model_height'] + model_type.embryo_tolerance,
|
'height': item['model_height'] + embryo_redundancy_id.height,
|
||||||
'volume': (item['model_long'] + model_type.embryo_tolerance) * (
|
'volume': (item['model_long'] + embryo_redundancy_id.long) * (
|
||||||
item['model_width'] + model_type.embryo_tolerance) * (
|
item['model_width'] + embryo_redundancy_id.width) * (
|
||||||
item['model_height'] + model_type.embryo_tolerance),
|
item['model_height'] + embryo_redundancy_id.height),
|
||||||
'embryo_model_type_id': model_type.id,
|
'embryo_model_type_id': model_type.id,
|
||||||
'list_price': item['price'],
|
'list_price': item['price'],
|
||||||
'materials_id': materials_id.id,
|
'materials_id': materials_id.id,
|
||||||
'materials_type_id': materials_type_id.id,
|
'materials_type_id': materials_type_id.id,
|
||||||
|
'single_manufacturing': product_id.single_manufacturing,
|
||||||
'is_bfm': True,
|
'is_bfm': True,
|
||||||
'active': True
|
'active': True,
|
||||||
}
|
}
|
||||||
# 外协和采购生成的坯料需要根据材料型号绑定供应商
|
# 外协和采购生成的坯料需要根据材料型号绑定供应商
|
||||||
if route_type == 'subcontract' or route_type == 'purchase':
|
if route_type == 'subcontract' or route_type == 'purchase':
|
||||||
|
|||||||
17
sf_manufacturing/models/sf_production_common.py
Normal file
17
sf_manufacturing/models/sf_production_common.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# -*- 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'):
|
||||||
|
routing = self.env['mrp.routing.workcenter'].search([('id', '=', self._context.get('route_id'))])
|
||||||
|
domain = [('process_id', '=', routing.surface_technics_id.id)]
|
||||||
|
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
||||||
|
return super()._name_search(name, args, operator, limit, name_get_uid)
|
||||||
|
|
||||||
31
sf_manufacturing/models/sf_technology_design.py
Normal file
31
sf_manufacturing/models/sf_technology_design.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class sf_technology_design(models.Model):
|
||||||
|
_name = 'sf.technology.design'
|
||||||
|
_description = "工艺设计"
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def json_technology_design_str(self, k, route, i, process_parameter):
|
||||||
|
workorders_values_str = [0, '', {
|
||||||
|
'route_id': route.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 unlink_technology_design(self):
|
||||||
|
self.active = False
|
||||||
@@ -284,6 +284,7 @@ class StockRule(models.Model):
|
|||||||
'product_id': production.product_id.id,
|
'product_id': production.product_id.id,
|
||||||
'state': 'draft',
|
'state': 'draft',
|
||||||
})
|
})
|
||||||
|
technology_design_values = []
|
||||||
all_production = productions
|
all_production = productions
|
||||||
grouped_product_ids = {k: list(g) for k, g in groupby(all_production, key=lambda x: x.product_id.id)}
|
grouped_product_ids = {k: list(g) for k, g in groupby(all_production, key=lambda x: x.product_id.id)}
|
||||||
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
|
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
|
||||||
@@ -293,78 +294,83 @@ class StockRule(models.Model):
|
|||||||
# 为同一个product_id创建一个生产订单名称列表
|
# 为同一个product_id创建一个生产订单名称列表
|
||||||
product_id_to_production_names[product_id] = [production.name for production in all_production]
|
product_id_to_production_names[product_id] = [production.name for production in all_production]
|
||||||
for production_item in productions:
|
for production_item in productions:
|
||||||
|
|
||||||
production_programming = self.env['mrp.production'].search(
|
production_programming = self.env['mrp.production'].search(
|
||||||
[('product_id.id', '=', production_item.product_id.id),
|
[('product_id.id', '=', production_item.product_id.id),
|
||||||
('origin', '=', production_item.origin)],
|
('origin', '=', production_item.origin)],
|
||||||
limit=1, order='id asc')
|
limit=1, order='id asc')
|
||||||
if production_item.product_id.id in product_id_to_production_names:
|
if production_item.product_id.id in product_id_to_production_names:
|
||||||
if not production_programming.programming_no:
|
# 同一个产品多个制造订单对应一个编程单和模型库
|
||||||
if production_item.product_id.model_process_parameters_ids:
|
# 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递
|
||||||
is_purchase = False
|
if not production_item.programming_no and production.production_type == '自动化产线加工':
|
||||||
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:
|
if not production_programming.programming_no:
|
||||||
production_item.fetchCNC(
|
production_item.fetchCNC(
|
||||||
', '.join(product_id_to_production_names[production_item.product_id.id]))
|
', '.join(product_id_to_production_names[production_item.product_id.id]))
|
||||||
else:
|
else:
|
||||||
production_item.write({'programming_no': production_programming.programming_no,
|
production_item.write({'programming_no': production_programming.programming_no,
|
||||||
'programming_state': '编程中'})
|
'programming_state': '编程中'})
|
||||||
return True
|
if not technology_design_values:
|
||||||
|
i = 0
|
||||||
|
if production_item.product_id.categ_id.type == '成品':
|
||||||
|
# 根据加工面板的面数及成品工序模板生成工序设计
|
||||||
|
|
||||||
|
for k in (production_item.product_id.model_processing_panel.split(',')):
|
||||||
|
if production_item.production_type == '自动化产线加工':
|
||||||
|
product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
|
||||||
|
[('product_model_type_id', '=', production_item.product_id.product_model_type_id.id)],
|
||||||
|
order='sequence asc'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
product_routing_workcenter = self.env['sf.manual.product.model.type.routing.sort'].search(
|
||||||
|
[('manual_product_model_type_id', '=', production_item.product_id.product_model_type_id.id)],
|
||||||
|
order='sequence asc'
|
||||||
|
)
|
||||||
|
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))
|
||||||
|
elif production.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))
|
||||||
|
productions.technology_design_ids = technology_design_values
|
||||||
|
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ProductionLot(models.Model):
|
class ProductionLot(models.Model):
|
||||||
@@ -452,6 +458,12 @@ class ProductionLot(models.Model):
|
|||||||
if product.categ_id.name == '刀具':
|
if product.categ_id.name == '刀具':
|
||||||
return self.env['stock.lot'].get_tool_generate_lot_names1(company, product)
|
return self.env['stock.lot'].get_tool_generate_lot_names1(company, product)
|
||||||
else:
|
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')
|
||||||
return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name, 2)[1]
|
return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name, 2)[1]
|
||||||
now = datetime.now().strftime("%Y%m%d")
|
now = datetime.now().strftime("%Y%m%d")
|
||||||
if product.cutting_tool_model_id:
|
if product.cutting_tool_model_id:
|
||||||
@@ -554,7 +566,9 @@ class StockPicking(models.Model):
|
|||||||
|
|
||||||
retrospect_ref = fields.Char('追溯参考', compute='_compute_move_ids', store=True)
|
retrospect_ref = fields.Char('追溯参考', compute='_compute_move_ids', store=True)
|
||||||
|
|
||||||
@api.depends('move_ids')
|
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):
|
def _compute_move_ids(self):
|
||||||
for item in self:
|
for item in self:
|
||||||
if item.move_ids:
|
if item.move_ids:
|
||||||
@@ -574,13 +588,14 @@ class StockPicking(models.Model):
|
|||||||
default_codes = ''
|
default_codes = ''
|
||||||
if boms:
|
if boms:
|
||||||
for bom in boms:
|
for bom in boms:
|
||||||
code = bom.product_tmpl_id.default_code.split('-')[-1]
|
if bom.product_tmpl_id.default_code:
|
||||||
default_code = bom.product_tmpl_id.default_code.split(f'-{code}')[0]
|
code = bom.product_tmpl_id.default_code.split('-')[-1]
|
||||||
if default_code not in default_codes:
|
default_code = bom.product_tmpl_id.default_code.split(f'-{code}')[0]
|
||||||
if default_codes == '':
|
if default_code not in default_codes:
|
||||||
default_codes = default_code
|
if default_codes == '':
|
||||||
else:
|
default_codes = default_code
|
||||||
default_codes = default_codes + ',' + default_code
|
else:
|
||||||
|
default_codes = default_codes + ',' + default_code
|
||||||
item.retrospect_ref = default_codes
|
item.retrospect_ref = default_codes
|
||||||
elif item.picking_type_id.sequence_code in ['INT', 'PC']:
|
elif item.picking_type_id.sequence_code in ['INT', 'PC']:
|
||||||
pass
|
pass
|
||||||
@@ -597,31 +612,27 @@ class StockPicking(models.Model):
|
|||||||
return '%s%s' % (rescode, num)
|
return '%s%s' % (rescode, num)
|
||||||
|
|
||||||
def button_validate(self):
|
def button_validate(self):
|
||||||
move_out = self.env['stock.move'].search(
|
if self.picking_type_id.barcode == 'OCOUT':
|
||||||
[('location_id', '=', self.env['stock.location'].search(
|
move_out = self.env['stock.move'].search(
|
||||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
[('location_id', '=', self.env['stock.location'].search(
|
||||||
('location_dest_id', '=', self.env['stock.location'].search(
|
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
||||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
('location_dest_id', '=', self.env['stock.location'].search(
|
||||||
('origin', '=', self.origin)])
|
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
||||||
# if self.id == move_out.picking_id.id:
|
('origin', '=', self.origin)])
|
||||||
# if move_out.move_line_ids.workorder_id.state not in ['progress']:
|
move_in = self.env['stock.move'].search(
|
||||||
# raise UserError(
|
[('location_dest_id', '=', self.env['stock.location'].search(
|
||||||
# _('该出库单里源单据内的单号为%s的工单还未开始,不能进行验证操作!' % move_out.move_line_ids.workorder_id.name))
|
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
||||||
# 入库单验证
|
('location_id', '=', self.env['stock.location'].search(
|
||||||
move_in = self.env['stock.move'].search(
|
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
||||||
[('location_dest_id', '=', self.env['stock.location'].search(
|
('origin', '=', self.origin), ('picking_id', '=', self.id)])
|
||||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id:
|
||||||
('location_id', '=', self.env['stock.location'].search(
|
if move_out.origin == move_in.origin:
|
||||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
move_in.write({'production_id': False})
|
||||||
('origin', '=', self.origin), ('picking_id', '=', self.id)])
|
if move_out.picking_id.state != 'done':
|
||||||
if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id:
|
raise UserError(
|
||||||
if move_out.origin == move_in.origin:
|
_('该入库单对应的单号为%s的出库单还未完成,不能进行验证操作!' % move_out.picking_id.name))
|
||||||
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()
|
res = super().button_validate()
|
||||||
if res is True:
|
if res is True and self.picking_type_id.barcode == 'OCIN':
|
||||||
if self.id == move_out.picking_id.id:
|
if self.id == move_out.picking_id.id:
|
||||||
# if move_out.move_line_ids.workorder_id.state == 'progress':
|
# if move_out.move_line_ids.workorder_id.state == 'progress':
|
||||||
move_in = self.env['stock.move'].search(
|
move_in = self.env['stock.move'].search(
|
||||||
@@ -639,42 +650,43 @@ class StockPicking(models.Model):
|
|||||||
|
|
||||||
# 创建 外协出库入单
|
# 创建 外协出库入单
|
||||||
def create_outcontract_picking(self, sorted_workorders_arr, item):
|
def create_outcontract_picking(self, sorted_workorders_arr, item):
|
||||||
m = 0
|
if len(sorted_workorders_arr) > 1:
|
||||||
for sorted_workorders in sorted_workorders_arr:
|
sorted_workorders_arr = sorted_workorders_arr[0]
|
||||||
# pick_ids = []
|
stock_picking = self.env['stock.picking'].search(
|
||||||
if m == 0:
|
[('origin', '=', sorted_workorders_arr.production_id.name), ('name', 'ilike', 'OCOUT')])
|
||||||
outcontract_stock_move = self.env['stock.move'].search(
|
if not stock_picking:
|
||||||
[('workorder_id', '=', sorted_workorders.id), ('production_id', '=', item.id)])
|
for sorted_workorders in sorted_workorders_arr:
|
||||||
if not outcontract_stock_move:
|
# pick_ids = []
|
||||||
new_picking = True
|
if not sorted_workorders.picking_ids:
|
||||||
location_id = self.env['stock.location'].search(
|
outcontract_stock_move = self.env['stock.move'].search([('production_id', '=', item.id)])
|
||||||
[('barcode', 'ilike', 'VL-SPOC')]).id,
|
if not outcontract_stock_move:
|
||||||
location_dest_id = self.env['stock.location'].search(
|
new_picking = True
|
||||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id,
|
location_id = self.env['stock.location'].search(
|
||||||
outcontract_picking_type_in = self.env.ref(
|
[('barcode', 'ilike', 'VL-SPOC')]).id,
|
||||||
'sf_manufacturing.outcontract_picking_in').id,
|
location_dest_id = self.env['stock.location'].search(
|
||||||
outcontract_picking_type_out = self.env.ref(
|
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id,
|
||||||
'sf_manufacturing.outcontract_picking_out').id,
|
outcontract_picking_type_in = self.env.ref(
|
||||||
moves_out = self.env['stock.move'].sudo().create(
|
'sf_manufacturing.outcontract_picking_in').id,
|
||||||
self.env['stock.move']._get_stock_move_values_Res(item, location_dest_id, location_id,
|
outcontract_picking_type_out = self.env.ref(
|
||||||
outcontract_picking_type_out))
|
'sf_manufacturing.outcontract_picking_out').id,
|
||||||
picking_out = self.create(
|
moves_out = self.env['stock.move'].sudo().create(
|
||||||
moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/'))
|
self.env['stock.move']._get_stock_move_values_Res(item, location_dest_id, location_id,
|
||||||
# pick_ids.append(picking_out.id)
|
outcontract_picking_type_out))
|
||||||
moves_out.write(
|
picking_out = self.create(
|
||||||
{'picking_id': picking_out.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
|
moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/'))
|
||||||
moves_out._assign_picking_post_process(new=new_picking)
|
# pick_ids.append(picking_out.id)
|
||||||
moves_in = self.env['stock.move'].sudo().create(
|
moves_out.write(
|
||||||
self.env['stock.move']._get_stock_move_values_Res(item, location_id, location_dest_id,
|
{'picking_id': picking_out.id, 'state': 'waiting'})
|
||||||
outcontract_picking_type_in))
|
moves_out._assign_picking_post_process(new=new_picking)
|
||||||
picking_in = self.create(
|
moves_in = self.env['stock.move'].sudo().create(
|
||||||
moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/'))
|
self.env['stock.move']._get_stock_move_values_Res(item, location_id, location_dest_id,
|
||||||
# pick_ids.append(picking_in.id)
|
outcontract_picking_type_in))
|
||||||
moves_in.write(
|
picking_in = self.create(
|
||||||
{'picking_id': picking_in.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
|
moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/'))
|
||||||
moves_in._assign_picking_post_process(new=new_picking)
|
# pick_ids.append(picking_in.id)
|
||||||
m += 1
|
moves_in.write(
|
||||||
# sorted_workorders.write({'picking_ids': [(6, 0, pick_ids)]})
|
{'picking_id': picking_in.id, 'state': 'waiting'})
|
||||||
|
moves_in._assign_picking_post_process(new=new_picking)
|
||||||
|
|
||||||
|
|
||||||
class ReStockMove(models.Model):
|
class ReStockMove(models.Model):
|
||||||
@@ -710,7 +722,7 @@ class ReStockMove(models.Model):
|
|||||||
return {
|
return {
|
||||||
'name': self.env['stock.picking']._get_name_Res(rescode),
|
'name': self.env['stock.picking']._get_name_Res(rescode),
|
||||||
'origin': item.name,
|
'origin': item.name,
|
||||||
'surface_technics_parameters_id': sorted_workorders.surface_technics_parameters_id.id,
|
# 'surface_technics_parameters_id': sorted_workorders.surface_technics_parameters_id.id,
|
||||||
'company_id': self.mapped('company_id').id,
|
'company_id': self.mapped('company_id').id,
|
||||||
'user_id': False,
|
'user_id': False,
|
||||||
'move_type': self.mapped('group_id').move_type or 'direct',
|
'move_type': self.mapped('group_id').move_type or 'direct',
|
||||||
@@ -731,7 +743,7 @@ class ReStockMove(models.Model):
|
|||||||
'picking_id': self.picking_id.id,
|
'picking_id': self.picking_id.id,
|
||||||
'reserved_uom_qty': 1.0,
|
'reserved_uom_qty': 1.0,
|
||||||
'lot_id': production_id.move_line_raw_ids.lot_id.id,
|
'lot_id': production_id.move_line_raw_ids.lot_id.id,
|
||||||
'company_id': self.company_id.id,
|
'company_id': self.env.company.id,
|
||||||
# 'workorder_id': '' if not sorted_workorders else sorted_workorders.id,
|
# 'workorder_id': '' if not sorted_workorders else sorted_workorders.id,
|
||||||
# 'production_id': '' if not sorted_workorders else sorted_workorders.production_id.id,
|
# 'production_id': '' if not sorted_workorders else sorted_workorders.production_id.id,
|
||||||
'state': 'assigned',
|
'state': 'assigned',
|
||||||
@@ -775,10 +787,10 @@ class ReStockMove(models.Model):
|
|||||||
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
|
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
|
||||||
else:
|
else:
|
||||||
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
|
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":
|
elif self.product_id.tracking == "lot":
|
||||||
self._put_tool_lot(self.company_id, self.product_id, self.origin)
|
self._put_tool_lot(self.company_id, self.product_id, self.origin)
|
||||||
if not self.move_line_nosuggest_ids:
|
|
||||||
self._generate_serial_numbers()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'name': _('Detailed Operations'),
|
'name': _('Detailed Operations'),
|
||||||
@@ -928,6 +940,11 @@ class ReStockMove(models.Model):
|
|||||||
move_lines_commands.append((0, 0, move_line_cmd))
|
move_lines_commands.append((0, 0, move_line_cmd))
|
||||||
qty_by_location[loc.id] += 1
|
qty_by_location[loc.id] += 1
|
||||||
return move_lines_commands
|
return move_lines_commands
|
||||||
|
|
||||||
|
# def _prepare_procurement_origin(self):
|
||||||
|
# """修改采购来源"""
|
||||||
|
# self.ensure_one()
|
||||||
|
# return self.group_id and self.group_id.name or (self.origin or self.picking_id.name or "/")
|
||||||
|
|
||||||
|
|
||||||
class ReStockQuant(models.Model):
|
class ReStockQuant(models.Model):
|
||||||
|
|||||||
@@ -165,6 +165,16 @@ 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_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_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,1,1,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_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,1,1,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,0
|
||||||
|
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
|
||||||
|
|||||||
|
@@ -31,7 +31,7 @@
|
|||||||
<form string="模型类型">
|
<form string="模型类型">
|
||||||
<group>
|
<group>
|
||||||
<field name="name" required="1"/>
|
<field name="name" required="1"/>
|
||||||
<field name="embryo_tolerance" required="1" string="坯料容余(mm)"/>
|
<field name="embryo_tolerance_id" required="1" string="坯料容余(mm)"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name='product_routing_tmpl_ids'>
|
<field name='product_routing_tmpl_ids'>
|
||||||
@@ -44,6 +44,17 @@
|
|||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</group>
|
</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}"/>
|
||||||
|
<field name="routing_type" string="类型"/>
|
||||||
|
<field name="is_repeat" string="重复"/>
|
||||||
|
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name='embryo_routing_tmpl_ids'>
|
<field name='embryo_routing_tmpl_ids'>
|
||||||
<tree editable='bottom'>
|
<tree editable='bottom'>
|
||||||
|
|||||||
@@ -35,9 +35,10 @@
|
|||||||
<field name="reservation_state" optional="hide" decoration-danger="reservation_state == 'confirmed'"
|
<field name="reservation_state" optional="hide" decoration-danger="reservation_state == 'confirmed'"
|
||||||
decoration-success="reservation_state == 'assigned'"/>
|
decoration-success="reservation_state == 'assigned'"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<!-- <xpath expr="//field[@name='state']" position="before"> -->
|
<xpath expr="//field[@name='state']" position="before">
|
||||||
<!-- <field name="schedule_state" optional="show"/> -->
|
<field name="production_type" widget="badge" decoration-warning="production_type == '人工线下加工'"
|
||||||
<!-- </xpath> -->
|
decoration-success="production_type == '自动化产线加工'" optional="show"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//field[@name='activity_ids']" position="replace">
|
<xpath expr="//field[@name='activity_ids']" position="replace">
|
||||||
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
|
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -56,6 +57,16 @@
|
|||||||
<!-- <button name="action_view_production_schedule" string="生产排程" type="object" attrs="{'invisible': [('state', 'in', ['draft', 'cancel','已排程','progress','done','to_close'])]}"/> -->
|
<!-- <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'])]}"/> -->
|
<!-- <button name="cancel_plan" string="取消排程" type="object" attrs="{'invisible': [('state', 'in', ['draft', 'cancel','progress','done','to_close','confirmed'])]}"/> -->
|
||||||
<!-- </xpath> -->
|
<!-- </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>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -70,7 +81,7 @@
|
|||||||
<!-- <attribute name="statusbar_visible">draft,confirmed,progress,pending_processing,completed,done -->
|
<!-- <attribute name="statusbar_visible">draft,confirmed,progress,pending_processing,completed,done -->
|
||||||
<!-- </attribute> -->
|
<!-- </attribute> -->
|
||||||
<attribute name="statusbar_visible">
|
<attribute name="statusbar_visible">
|
||||||
confirmed,pending_cam,progress,rework,scrap,done
|
technology_to_confirmed,confirmed,pending_cam,progress,rework,scrap,done
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet//group//group[2]//label" position="before">
|
<xpath expr="//sheet//group//group[2]//label" position="before">
|
||||||
@@ -80,9 +91,13 @@
|
|||||||
|
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet//group//group//div[3]" position="after">
|
<xpath expr="//sheet//group//group//div[3]" position="after">
|
||||||
<field name="manual_quotation" readonly="1"/>
|
<field name="production_type" readonly="1"/>
|
||||||
<field name="programming_no" 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="programming_state" readonly="1"
|
<field name="programming_state" readonly="1"
|
||||||
|
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"
|
||||||
decoration-success="programming_state == '已编程'"
|
decoration-success="programming_state == '已编程'"
|
||||||
decoration-warning="programming_state =='编程中'"
|
decoration-warning="programming_state =='编程中'"
|
||||||
decoration-danger="programming_state =='已编程未下发'"/>
|
decoration-danger="programming_state =='已编程未下发'"/>
|
||||||
@@ -97,7 +112,8 @@
|
|||||||
<xpath expr="//field[@name='user_id']" position="after">
|
<xpath expr="//field[@name='user_id']" position="after">
|
||||||
<field name="production_line_id" readonly="1"/>
|
<field name="production_line_id" readonly="1"/>
|
||||||
<!-- <field name="production_line_state" readonly="1"/>-->
|
<!-- <field name="production_line_state" readonly="1"/>-->
|
||||||
<field name="part_number" string="成品的零件图号"/>
|
<field name="part_name"/>
|
||||||
|
<field name="part_number" string="零件图号"/>
|
||||||
<field name="tool_state"/>
|
<field name="tool_state"/>
|
||||||
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
|
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
|
||||||
<field name="deadline_of_delivery" readonly="1"/>
|
<field name="deadline_of_delivery" readonly="1"/>
|
||||||
@@ -122,6 +138,10 @@
|
|||||||
groups="sf_base.group_sf_mrp_user"/>
|
groups="sf_base.group_sf_mrp_user"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="(//header//button[@name='button_scrap'])" position="replace">
|
<xpath expr="(//header//button[@name='button_scrap'])" position="replace">
|
||||||
|
<button name="technology_confirm" string="工艺确认" type="object" class="oe_highlight"
|
||||||
|
attrs="{'invisible': [('workorder_ids', '!=', [])]}"></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="button_scrap" invisible="1"/>
|
||||||
<button name="do_update_program" string="更新程序" type="object" groups="sf_base.group_sf_mrp_user"
|
<button name="do_update_program" string="更新程序" type="object" groups="sf_base.group_sf_mrp_user"
|
||||||
confirm="是否确认更新程序"
|
confirm="是否确认更新程序"
|
||||||
@@ -291,8 +311,7 @@
|
|||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//sheet//notebook//page[@name='operations']" position="attributes">
|
<xpath expr="//sheet//notebook//page[@name='operations']" position="attributes">
|
||||||
<attribute name="attrs">{'invisible': ['|',('schedule_state', '=', '未排'),('workorder_ids', '=',
|
<attribute name="attrs">{'invisible': [('workorder_ids', '=', [])]}
|
||||||
[])]}
|
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
@@ -333,10 +352,34 @@
|
|||||||
<field name="part_drawing" widget="adaptive_viewer"/>
|
<field name="part_drawing" widget="adaptive_viewer"/>
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//sheet//notebook//page[@name='components']" position="before">
|
||||||
|
<page string="工艺设计">
|
||||||
|
<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)]}"
|
||||||
|
string="参数" context="{'route_id':route_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">
|
<xpath expr="//sheet//notebook" position="inside">
|
||||||
<page string="质检标准">
|
<page string="质检标准">
|
||||||
<field name="quality_standard" widget="adaptive_viewer"/>
|
<field name="quality_standard" widget="adaptive_viewer"/>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -517,8 +560,9 @@
|
|||||||
<separator/>
|
<separator/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//search" position="inside">
|
<xpath expr="//search" position="inside">
|
||||||
<searchpanel class="account_root">
|
<searchpanel>
|
||||||
<field name="state" icon="fa-filter" enable_counters="1"/>
|
<field name="state" icon="fa-filter" enable_counters="1"/>
|
||||||
|
<field name="delivery_status" icon="fa-filter" enable_counters="1"/>
|
||||||
</searchpanel>
|
</searchpanel>
|
||||||
</xpath>
|
</xpath>
|
||||||
<filter name='todo' position="replace"/>
|
<filter name='todo' position="replace"/>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
</field>
|
</field>
|
||||||
<field name="bom_product_template_attribute_value_ids" position="after">
|
<field name="bom_product_template_attribute_value_ids" position="after">
|
||||||
<field name="routing_type" required="1"/>
|
<field name="routing_type" required="1"/>
|
||||||
|
<field name="routing_tag" required="1" string="工序标签"/>
|
||||||
<field name="is_repeat"/>
|
<field name="is_repeat"/>
|
||||||
<field name="reserved_duration"/>
|
<field name="reserved_duration"/>
|
||||||
</field>
|
</field>
|
||||||
|
|||||||
@@ -32,8 +32,10 @@
|
|||||||
</field>
|
</field>
|
||||||
<xpath expr="//field[@name='qty_remaining']" position="after">
|
<xpath expr="//field[@name='qty_remaining']" position="after">
|
||||||
<field name="manual_quotation" optional="show"/>
|
<field name="manual_quotation" optional="show"/>
|
||||||
<field name='tag_type' widget="badge"
|
<field name="construction_period_status" optional="show" widget="badge"
|
||||||
decoration-danger="tag_type == '重新加工'"/>
|
decoration-success="construction_period_status == '正常'"
|
||||||
|
decoration-warning="construction_period_status == '预警'"
|
||||||
|
decoration-danger="construction_period_status == '已逾期'"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='date_planned_start']" position="replace">
|
<xpath expr="//field[@name='date_planned_start']" position="replace">
|
||||||
<field name="date_planned_start" string="计划开始日期" optional="show"/>
|
<field name="date_planned_start" string="计划开始日期" optional="show"/>
|
||||||
@@ -248,7 +250,7 @@
|
|||||||
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
|
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
|
||||||
decoration-danger="tag_type == '重新加工'"/>
|
decoration-danger="tag_type == '重新加工'"/>
|
||||||
<field name="is_test_env" invisible="1"/>
|
<field name="is_test_env" invisible="1"/>
|
||||||
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
||||||
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget='qrcode_widget'/>
|
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget='qrcode_widget'/>
|
||||||
<field name="rfid_code" string="RFID码(手动输入框)" force_save="1" readonly="0" cache="True"
|
<field name="rfid_code" string="RFID码(手动输入框)" force_save="1" readonly="0" cache="True"
|
||||||
attrs="{'invisible': ['|',('rfid_code_old', '!=', False), ('is_test_env', '=', False)]}"/>
|
attrs="{'invisible': ['|',('rfid_code_old', '!=', False), ('is_test_env', '=', False)]}"/>
|
||||||
@@ -257,11 +259,12 @@
|
|||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//header" position="inside">
|
<xpath expr="//header" position="inside">
|
||||||
<div class="o_statusbar_buttons"><button name="button_change_env"
|
<div class="o_statusbar_buttons">
|
||||||
type="object"
|
<button name="button_change_env"
|
||||||
string="演示模式"
|
type="object"
|
||||||
class="btn-primary"
|
string="演示模式"
|
||||||
groups="sf_manufacturing.group_show_button"/>
|
class="btn-primary"
|
||||||
|
groups="sf_manufacturing.group_show_button"/>
|
||||||
</div>
|
</div>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
@@ -281,6 +284,7 @@
|
|||||||
<label for="material_height" string="高"/>
|
<label for="material_height" string="高"/>
|
||||||
<field name="material_height" class="o_address_zip"/>
|
<field name="material_height" class="o_address_zip"/>
|
||||||
</div>
|
</div>
|
||||||
|
<field name="part_name"/>
|
||||||
<field name="part_number" string="成品的零件图号"/>
|
<field name="part_number" string="成品的零件图号"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//label[1]" position="attributes">
|
<xpath expr="//label[1]" position="attributes">
|
||||||
@@ -481,11 +485,11 @@
|
|||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
<page string="2D加工图纸" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
|
<page string="2D加工图纸" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
|
||||||
<field name="machining_drawings" widget="adaptive_viewer"/>
|
<field name="machining_drawings" widget="adaptive_viewer"/>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
<page string="质检标准" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
|
<page string="质检标准" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
|
||||||
<field name="quality_standard" widget="adaptive_viewer"/>
|
<field name="quality_standard" widget="adaptive_viewer"/>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
@@ -542,11 +546,11 @@
|
|||||||
<!-- attrs='{"invisible": ["|","|",("state","!=","progress"),("user_permissions","=",False),("results","=","合格")]}'/>-->
|
<!-- attrs='{"invisible": ["|","|",("state","!=","progress"),("user_permissions","=",False),("results","=","合格")]}'/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
</page>
|
</page>
|
||||||
<page string="2D加工图纸" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
<page string="2D加工图纸" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||||
<field name="machining_drawings" widget="adaptive_viewer"/>
|
<field name="machining_drawings" widget="adaptive_viewer"/>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
<page string="质检标准" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
<page string="质检标准" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||||
<field name="quality_standard" widget="adaptive_viewer"/>
|
<field name="quality_standard" widget="adaptive_viewer"/>
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
@@ -18,10 +18,13 @@
|
|||||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//field[@name='user_id']" position="after">
|
<xpath expr="//field[@name='user_id']" position="after">
|
||||||
<field name="retrospect_ref"/>
|
<field name="picking_type_sequence_code" invisible="1"/>
|
||||||
<field name="person_of_delivery"/>
|
<field name="retrospect_ref"
|
||||||
<field name="telephone_of_delivery"/>
|
attrs="{'invisible':[('picking_type_sequence_code','not in',['DL', 'INT', 'PC'])]}"/>
|
||||||
<field name="address_of_delivery"/>
|
<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>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
from . import workpiece_delivery_wizard
|
from . import workpiece_delivery_wizard
|
||||||
from . import rework_wizard
|
from . import rework_wizard
|
||||||
from . import production_wizard
|
from . import production_wizard
|
||||||
|
from . import production_technology_wizard
|
||||||
|
from . import production_technology_re_adjust_wizard
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# -*- 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=False)
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
if self.is_technology_re_adjust is True:
|
||||||
|
domain = [('origin', '=', self.origin), ('state', '=', 'confirmed')]
|
||||||
|
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:
|
||||||
|
for td_other in production_item.technology_design_ids:
|
||||||
|
if td_other.is_auto is False:
|
||||||
|
td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
|
||||||
|
if not td_del or td_del.active is False:
|
||||||
|
td_other.write({'active': False})
|
||||||
|
for td_main in technology_designs:
|
||||||
|
route_other = production_item.technology_design_ids.filtered(
|
||||||
|
lambda td: td.route_id.id == td_main.route_id.id)
|
||||||
|
if not route_other and td_main.active is True:
|
||||||
|
production_item.write({'technology_design_ids': [(0, 0, {
|
||||||
|
'route_id': td_main.route_id.id,
|
||||||
|
'process_parameters_id': False if td_main.process_parameters_id is False else
|
||||||
|
self.env[
|
||||||
|
'sf.production.process.parameter'].search(
|
||||||
|
[('id', '=', td_main.process_parameters_id.id)]).id,
|
||||||
|
'sequence': td_main.sequence,
|
||||||
|
'is_auto': td_main.is_auto})]})
|
||||||
|
else:
|
||||||
|
for ro in route_other:
|
||||||
|
domain = [('production_id', '=', self.production_id.id), ('active', 'in', [True, False]),
|
||||||
|
('route_id', '=', ro.route_id.id)]
|
||||||
|
if ro.route_id.routing_type == '表面工艺':
|
||||||
|
domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
|
||||||
|
elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
|
||||||
|
display_name = ro.route_id.display_name
|
||||||
|
domain += [('name', 'ilike', display_name)]
|
||||||
|
elif ro.panel is not False:
|
||||||
|
domain += [('panel', '=', ro.panel)]
|
||||||
|
td_upd = self.env['sf.technology.design'].sudo().search(domain)
|
||||||
|
if td_upd:
|
||||||
|
ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
|
||||||
|
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:
|
||||||
|
# 工单采购单外协出入库单皆需取消
|
||||||
|
domain = [('production_id', '=', special.production_id.id)]
|
||||||
|
if special.process_parameters_id:
|
||||||
|
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id)]
|
||||||
|
else:
|
||||||
|
domain += [('name', '=', special.route_id.name)]
|
||||||
|
workorder = self.env['mrp.workorder'].search(domain)
|
||||||
|
if workorder.state != 'cancel':
|
||||||
|
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.origin)])
|
||||||
|
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.route_id.routing_type == '表面工艺':
|
||||||
|
display_name = special.process_parameters_id.display_name
|
||||||
|
else:
|
||||||
|
display_name = special.route_id.display_name
|
||||||
|
workorder = self.env['mrp.workorder'].search(
|
||||||
|
[('name', '=', display_name), ('production_id', '=', special.production_id.id)])
|
||||||
|
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:
|
||||||
|
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
|
||||||
|
productions._reset_work_order_sequence()
|
||||||
|
productions.get_subcontract_pick_purchase()
|
||||||
|
for item in productions:
|
||||||
|
workorders = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
|
||||||
|
key=lambda a: a.sequence)
|
||||||
|
workorders[0].state = 'waiting'
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?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 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>
|
||||||
64
sf_manufacturing/wizard/production_technology_wizard.py
Normal file
64
sf_manufacturing/wizard/production_technology_wizard.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# -*- 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=False)
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
if self.is_technology_confirm is True:
|
||||||
|
domain = [('origin', '=', self.origin)]
|
||||||
|
else:
|
||||||
|
domain = [('id', '=', self.production_id.id)]
|
||||||
|
technology_designs = self.production_id.technology_design_ids
|
||||||
|
productions = self.env['mrp.production'].search(domain)
|
||||||
|
for production in productions:
|
||||||
|
if production != self.production_id:
|
||||||
|
for td_other in production.technology_design_ids:
|
||||||
|
if td_other.is_auto is False:
|
||||||
|
td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
|
||||||
|
if not td_del or td_del.active is False:
|
||||||
|
td_other.write({'active': False})
|
||||||
|
for td_main in technology_designs:
|
||||||
|
route_other = production.technology_design_ids.filtered(
|
||||||
|
lambda td: td.route_id.id == td_main.route_id.id)
|
||||||
|
if not route_other and td_main.active is True:
|
||||||
|
production.write({'technology_design_ids': [(0, 0, {
|
||||||
|
'route_id': td_main.route_id.id,
|
||||||
|
'process_parameters_id': False if td_main.process_parameters_id is False else self.env[
|
||||||
|
'sf.production.process.parameter'].search(
|
||||||
|
[('id', '=', td_main.process_parameters_id.id)]).id,
|
||||||
|
'sequence': td_main.sequence})]})
|
||||||
|
else:
|
||||||
|
for ro in route_other:
|
||||||
|
domain = [('production_id', '=', self.production_id.id),
|
||||||
|
('active', 'in', [True, False]),
|
||||||
|
('route_id', '=', ro.route_id.id)]
|
||||||
|
if ro.route_id.routing_type == '表面工艺':
|
||||||
|
domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
|
||||||
|
elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
|
||||||
|
display_name = ro.route_id.display_name
|
||||||
|
domain += [('name', 'ilike', display_name)]
|
||||||
|
elif ro.panel is not False:
|
||||||
|
domain += [('panel', '=', ro.panel)]
|
||||||
|
td_upd = self.env['sf.technology.design'].sudo().search(domain)
|
||||||
|
if td_upd:
|
||||||
|
ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
|
||||||
|
# special = production.technology_design_ids.filtered(
|
||||||
|
# lambda td: td.is_auto is False and td.process_parameters_id is not False)
|
||||||
|
# # if special:
|
||||||
|
productions._create_workorder(False)
|
||||||
|
productions.get_subcontract_pick_purchase()
|
||||||
|
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']:
|
||||||
|
workorder[0].state = 'waiting'
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?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 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>
|
||||||
@@ -28,6 +28,8 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
domain = [('programming_no', '=', ret['programming_no'])]
|
domain = [('programming_no', '=', ret['programming_no'])]
|
||||||
if ret['manufacturing_type'] in ('scrap', 'invalid_tool_rework'):
|
if ret['manufacturing_type'] in ('scrap', 'invalid_tool_rework'):
|
||||||
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
|
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
|
||||||
|
else:
|
||||||
|
domain += [('state', '=', 'confirmed')]
|
||||||
productions = request.env['mrp.production'].with_user(
|
productions = request.env['mrp.production'].with_user(
|
||||||
request.env.ref("base.user_admin")).search(domain)
|
request.env.ref("base.user_admin")).search(domain)
|
||||||
if productions:
|
if productions:
|
||||||
@@ -48,26 +50,21 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
|
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
for production in productions:
|
for production in productions:
|
||||||
if not production.workorder_ids:
|
for panel in ret['processing_panel'].split(','):
|
||||||
production.product_id.model_processing_panel = ret['processing_panel']
|
# 查询状态为进行中且工序类型为CNC加工的工单
|
||||||
production._create_workorder(ret)
|
cnc_workorder_has = production.workorder_ids.filtered(
|
||||||
productions.process_range_time()
|
lambda ach: ach.routing_type == 'CNC加工' and ach.state not in ['progress', 'done',
|
||||||
else:
|
'rework',
|
||||||
for panel in ret['processing_panel'].split(','):
|
'cancel'] and ach.processing_panel == panel)
|
||||||
# 查询状态为进行中且工序类型为CNC加工的工单
|
if cnc_workorder_has:
|
||||||
cnc_workorder_has = production.workorder_ids.filtered(
|
if cnc_workorder_has.cnc_ids:
|
||||||
lambda ach: ach.routing_type == 'CNC加工' and ach.state not in ['progress', 'done',
|
cnc_workorder_has.cmm_ids.sudo().unlink()
|
||||||
'rework',
|
cnc_workorder_has.cnc_ids.sudo().unlink()
|
||||||
'cancel'] and ach.processing_panel == panel)
|
# request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
|
||||||
if cnc_workorder_has:
|
# production)
|
||||||
if cnc_workorder_has.cnc_ids:
|
cnc_workorder_has.write(
|
||||||
cnc_workorder_has.cmm_ids.sudo().unlink()
|
{'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret),
|
||||||
cnc_workorder_has.cnc_ids.sudo().unlink()
|
'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)})
|
||||||
# request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
|
|
||||||
# production)
|
|
||||||
cnc_workorder_has.write(
|
|
||||||
{'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret),
|
|
||||||
'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)})
|
|
||||||
for panel in ret['processing_panel'].split(','):
|
for panel in ret['processing_panel'].split(','):
|
||||||
# 查询状态为进行中且工序类型为CNC加工的工单
|
# 查询状态为进行中且工序类型为CNC加工的工单
|
||||||
cnc_workorder = productions.workorder_ids.filtered(
|
cnc_workorder = productions.workorder_ids.filtered(
|
||||||
@@ -77,7 +74,6 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
|
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
|
||||||
# panel)
|
# panel)
|
||||||
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
|
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
|
||||||
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
|
|
||||||
files_panel = os.listdir(program_path_tmp_panel)
|
files_panel = os.listdir(program_path_tmp_panel)
|
||||||
if files_panel:
|
if files_panel:
|
||||||
for file in files_panel:
|
for file in files_panel:
|
||||||
@@ -85,17 +81,13 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
if file_extension.lower() == '.pdf':
|
if file_extension.lower() == '.pdf':
|
||||||
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
||||||
logging.info('panel_file_path:%s' % panel_file_path)
|
logging.info('panel_file_path:%s' % panel_file_path)
|
||||||
logging.info('更新工作指令:%s' % cnc_workorder)
|
|
||||||
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
logging.info('更新工作指令完成:%s' % cnc_workorder)
|
|
||||||
pre_workorder = productions.workorder_ids.filtered(
|
pre_workorder = productions.workorder_ids.filtered(
|
||||||
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', 'rework'
|
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', 'rework'
|
||||||
'cancel'] and ap.processing_panel == panel)
|
'cancel'] and ap.processing_panel == panel)
|
||||||
if pre_workorder:
|
if pre_workorder:
|
||||||
logging.info('更新加工图纸:%s' % pre_workorder)
|
|
||||||
pre_workorder.write(
|
pre_workorder.write(
|
||||||
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
logging.info('更新加工图纸完成:%s' % pre_workorder)
|
|
||||||
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
|
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
|
||||||
logging.info('已更新制造订单编程状态:%s' % productions.ids)
|
logging.info('已更新制造订单编程状态:%s' % productions.ids)
|
||||||
res.update({
|
res.update({
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
_logger.info("同步刀具物料每齿走刀量完成")
|
_logger.info("同步刀具物料每齿走刀量完成")
|
||||||
self.env['sf.machining.accuracy'].sync_machining_accuracy_all()
|
self.env['sf.machining.accuracy'].sync_machining_accuracy_all()
|
||||||
_logger.info("同步加工精度完成")
|
_logger.info("同步加工精度完成")
|
||||||
|
self.env['sf.embryo.redundancy'].sync_embryo_redundancy_all()
|
||||||
|
_logger.info("同步坯料冗余完成")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_logger.info("sf_all_sync error: %s" % e)
|
_logger.info("sf_all_sync error: %s" % e)
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ class MrStaticResourceDataSync(models.Model):
|
|||||||
_logger.info("同步刀具物料每齿走刀量完成")
|
_logger.info("同步刀具物料每齿走刀量完成")
|
||||||
self.env['sf.machining.accuracy'].sync_machining_accuracy_all()
|
self.env['sf.machining.accuracy'].sync_machining_accuracy_all()
|
||||||
_logger.info("同步加工精度完成")
|
_logger.info("同步加工精度完成")
|
||||||
|
self.env['sf.embryo.redundancy'].sync_embryo_redundancy_all()
|
||||||
|
_logger.info("同步坯料冗余完成")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback_error = traceback.format_exc()
|
traceback_error = traceback.format_exc()
|
||||||
logging.error("同步静态资源库失败:%s" % traceback_error)
|
logging.error("同步静态资源库失败:%s" % traceback_error)
|
||||||
@@ -3149,7 +3151,7 @@ class MachiningAccuracySync(models.Model):
|
|||||||
r = requests.post(strUrl, json={}, data=None, headers=headers)
|
r = requests.post(strUrl, json={}, data=None, headers=headers)
|
||||||
r = r.json()
|
r = r.json()
|
||||||
result = json.loads(r['result'])
|
result = json.loads(r['result'])
|
||||||
_logger.info('加工精度:%s' % result)
|
# _logger.info('加工精度:%s' % result)
|
||||||
if result['status'] == 1:
|
if result['status'] == 1:
|
||||||
machining_accuracy_all_list = result['machining_accuracy_all_list']
|
machining_accuracy_all_list = result['machining_accuracy_all_list']
|
||||||
# 获取同步的id集合
|
# 获取同步的id集合
|
||||||
@@ -3168,3 +3170,42 @@ class MachiningAccuracySync(models.Model):
|
|||||||
"name": time['name'],
|
"name": time['name'],
|
||||||
"standard_tolerance": time['standard_tolerance'],
|
"standard_tolerance": time['standard_tolerance'],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
class EmbryoRedundancySync(models.Model):
|
||||||
|
_inherit = 'sf.embryo.redundancy'
|
||||||
|
_description = '坯料冗余'
|
||||||
|
url = '/api/embryo_redundancy/list'
|
||||||
|
|
||||||
|
def sync_embryo_redundancy_all(self):
|
||||||
|
config = self.env['res.config.settings'].get_values()
|
||||||
|
headers = Common.get_headers(self, config['token'], config['sf_secret_key'])
|
||||||
|
strUrl = config['sf_url'] + self.url
|
||||||
|
r = requests.post(strUrl, json={}, data=None, headers=headers)
|
||||||
|
r = r.json()
|
||||||
|
result = json.loads(r['result'])
|
||||||
|
# _logger.info('加工精度:%s' % result)
|
||||||
|
if result['status'] == 1:
|
||||||
|
embryo_redundancy_all_list = result['embryo_redundancy_all_list']
|
||||||
|
# 获取同步的id集合
|
||||||
|
codes = [obj['code'] for obj in embryo_redundancy_all_list]
|
||||||
|
self.env['sf.embryo.redundancy'].sudo().search(
|
||||||
|
[('code', 'not in', codes)]).unlink()
|
||||||
|
for item in embryo_redundancy_all_list:
|
||||||
|
embryo_redundancy = self.env['sf.embryo.redundancy'].sudo().search(
|
||||||
|
[('code', '=', item['code'])])
|
||||||
|
if embryo_redundancy:
|
||||||
|
embryo_redundancy.name = item['name']
|
||||||
|
embryo_redundancy.code = item['code']
|
||||||
|
embryo_redundancy.long = item['long']
|
||||||
|
embryo_redundancy.width = item['width']
|
||||||
|
embryo_redundancy.height = item['height']
|
||||||
|
embryo_redundancy.active = item['active']
|
||||||
|
else:
|
||||||
|
self.env['sf.embryo.redundancy'].sudo().create({
|
||||||
|
"name": item['name'],
|
||||||
|
"code": item['code'],
|
||||||
|
"long": item['long'],
|
||||||
|
"width": item['width'],
|
||||||
|
"height": item['height'],
|
||||||
|
"active": item['active'],
|
||||||
|
})
|
||||||
@@ -93,7 +93,11 @@ class sf_production_plan(models.Model):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
|
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
|
||||||
|
"""
|
||||||
|
修改搜索方法,只有制造订单状态为待排程时才显示
|
||||||
|
"""
|
||||||
|
domain = domain or []
|
||||||
|
domain.append(('production_id.state', 'not in', ['draft', 'technology_to_confirmed']))
|
||||||
info = super(sf_production_plan, self).search_read(domain, fields, offset, limit, order)
|
info = super(sf_production_plan, self).search_read(domain, fields, offset, limit, order)
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@@ -415,6 +419,14 @@ class sf_production_plan(models.Model):
|
|||||||
print(ret)
|
print(ret)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise UserError(e)
|
raise UserError(e)
|
||||||
|
|
||||||
|
# 增加制造订单类型
|
||||||
|
production_type = fields.Selection(
|
||||||
|
[('自动化产线加工', '自动化产线加工'), ('人工线下加工', '人工线下加工')],
|
||||||
|
string='制造类型',
|
||||||
|
related='production_id.production_type',
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# 机台作业计划
|
# 机台作业计划
|
||||||
@@ -438,8 +450,12 @@ class MrpProductionInheritForPlan(models.Model):
|
|||||||
def toggle_active(self):
|
def toggle_active(self):
|
||||||
# 调用父类方法切换 active 状态
|
# 调用父类方法切换 active 状态
|
||||||
res = super(MrpProductionInheritForPlan, self).toggle_active()
|
res = super(MrpProductionInheritForPlan, self).toggle_active()
|
||||||
|
stage_active = self.filtered('active')
|
||||||
|
stage_inactive = self - stage_active
|
||||||
self.env['sf.production.plan'].search(
|
self.env['sf.production.plan'].search(
|
||||||
[('production_id', '=', self.id), '|', ('active', '=', False), ('active', '=', True)]).write(
|
[('production_id', 'in', stage_active.ids)]).write(
|
||||||
{'active': self.active})
|
{'active': True})
|
||||||
|
self.env['sf.production.plan'].search(
|
||||||
|
[('production_id', 'in', stage_inactive.ids)]).write(
|
||||||
|
{'active': False})
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
<field name="production_line_id"/>
|
<field name="production_line_id"/>
|
||||||
<field name="date_planned_start"/>
|
<field name="date_planned_start"/>
|
||||||
<field name="date_planned_finished"/>
|
<field name="date_planned_finished"/>
|
||||||
|
<field name="production_type" widget="badge" decoration-warning="production_type == '人工线下加工'" decoration-success="production_type == '自动化产线加工'"/>
|
||||||
<field name="actual_start_time" optional='hide'/>
|
<field name="actual_start_time" optional='hide'/>
|
||||||
<field name="actual_end_time" optional='hide'/>
|
<field name="actual_end_time" optional='hide'/>
|
||||||
<field name="actual_process_time" optional='hide'/>
|
<field name="actual_process_time" optional='hide'/>
|
||||||
@@ -89,7 +90,7 @@
|
|||||||
<group string="加工信息">
|
<group string="加工信息">
|
||||||
|
|
||||||
<field name="date_planned_start" placeholder="如果不选择计划开始时间,会取当前时间来做排程" required="1"/>
|
<field name="date_planned_start" placeholder="如果不选择计划开始时间,会取当前时间来做排程" required="1"/>
|
||||||
<field name="date_planned_finished" required="1"/>
|
<field name="date_planned_finished" required="0" invisible="1"/>
|
||||||
<field name="actual_process_time"/>
|
<field name="actual_process_time"/>
|
||||||
<field name="actual_start_time"/>
|
<field name="actual_start_time"/>
|
||||||
<field name="actual_end_time"/>
|
<field name="actual_end_time"/>
|
||||||
@@ -264,7 +265,7 @@
|
|||||||
|
|
||||||
<!-- 这个也是制造订单生产计划,只是把tree视图放在了默认位置 -->
|
<!-- 这个也是制造订单生产计划,只是把tree视图放在了默认位置 -->
|
||||||
<record id="sf_production_plan_action1" model="ir.actions.act_window">
|
<record id="sf_production_plan_action1" model="ir.actions.act_window">
|
||||||
<field name="name">制造订单生产计划</field>
|
<field name="name">CNC产线计划排程</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">sf.production.plan</field>
|
<field name="res_model">sf.production.plan</field>
|
||||||
<field name="view_mode">tree,gantt,form</field>
|
<field name="view_mode">tree,gantt,form</field>
|
||||||
|
|||||||
@@ -7059,7 +7059,7 @@ msgstr ""
|
|||||||
#. module: sf_dlm
|
#. module: sf_dlm
|
||||||
#: model:product.template,name:sf_dlm.product_template_sf_product_template
|
#: model:product.template,name:sf_dlm.product_template_sf_product_template
|
||||||
msgid "CNC加工产品模板"
|
msgid "CNC加工产品模板"
|
||||||
msgstr "冲压模具滑块"
|
msgstr "自动化产线加工模板"
|
||||||
|
|
||||||
#. module: sf_tool_management
|
#. module: sf_tool_management
|
||||||
#: model:ir.model,name:sf_tool_management.model_sf_cnc_processing
|
#: model:ir.model,name:sf_tool_management.model_sf_cnc_processing
|
||||||
@@ -16130,6 +16130,11 @@ msgstr "在手库存"
|
|||||||
msgid "Inventory Overview"
|
msgid "Inventory Overview"
|
||||||
msgstr "库存概览"
|
msgstr "库存概览"
|
||||||
|
|
||||||
|
#. module: stock
|
||||||
|
#: model:ir.actions.act_window,name:stock.stock_picking_type_action
|
||||||
|
msgid "Internal Transfers"
|
||||||
|
msgstr "厂内出入库"
|
||||||
|
|
||||||
#. module: stock
|
#. module: stock
|
||||||
#: model:ir.model.fields,field_description:stock.field_stock_quant__inventory_quantity_set
|
#: model:ir.model.fields,field_description:stock.field_stock_quant__inventory_quantity_set
|
||||||
msgid "Inventory Quantity Set"
|
msgid "Inventory Quantity Set"
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class SfQualityCncTest(models.Model):
|
|||||||
|
|
||||||
machining_drawings = fields.Binary('2D加工图纸', related='workorder_id.machining_drawings', readonly=True)
|
machining_drawings = fields.Binary('2D加工图纸', related='workorder_id.machining_drawings', readonly=True)
|
||||||
quality_standard = fields.Binary('质检标准', related='workorder_id.quality_standard', readonly=True)
|
quality_standard = fields.Binary('质检标准', related='workorder_id.quality_standard', readonly=True)
|
||||||
|
part_name = fields.Char(related='workorder_id.part_name', string='零件名称')
|
||||||
|
|
||||||
def submit_pass(self):
|
def submit_pass(self):
|
||||||
if self.test_results in ['返工', '报废']:
|
if self.test_results in ['返工', '报废']:
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
<field name="model_file" widget="Viewer3D"/>
|
<field name="model_file" widget="Viewer3D"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
|
<field name="part_name"/>
|
||||||
<field name="part_number"/>
|
<field name="part_number"/>
|
||||||
<field name="processing_panel"/>
|
<field name="processing_panel"/>
|
||||||
</group>
|
</group>
|
||||||
@@ -174,8 +175,8 @@
|
|||||||
<field name="name">驾驶舱</field>
|
<field name="name">驾驶舱</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">quality.cnc.test</field>
|
<field name="res_model">quality.cnc.test</field>
|
||||||
<field name="view_mode">kanban,tree,form</field>
|
<field name="view_mode">tree,kanban,form</field>
|
||||||
<field name="view_id" ref="quality_cnc_test_view_kanban"/>
|
<field name="view_id" ref="quality_cnc_test_view_tree"/>
|
||||||
<field name="search_view_id" ref="quality_cnc_test_search"/>
|
<field name="search_view_id" ref="quality_cnc_test_search"/>
|
||||||
<field name="domain">[]</field>
|
<field name="domain">[]</field>
|
||||||
<field name="context">{ 'search_default_filter_waiting':1}</field>
|
<field name="context">{ 'search_default_filter_waiting':1}</field>
|
||||||
|
|||||||
@@ -23,10 +23,10 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="quality_control.quality_alert_action_check" model="ir.actions.act_window">
|
<record id="quality_alert_action_check" model="ir.actions.act_window">
|
||||||
<field name="name">质量缺陷单</field>
|
<field name="name">质量缺陷</field>
|
||||||
<field name="res_model">quality.alert</field>
|
<field name="res_model">quality.alert</field>
|
||||||
<field name="view_mode">kanban,tree,form,pivot,graph,calendar</field>
|
<field name="view_mode">tree,kanban,form,pivot,graph,calendar</field>
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="o_view_nocontent_smiling_face">
|
<p class="o_view_nocontent_smiling_face">
|
||||||
Create a new quality alert
|
Create a new quality alert
|
||||||
@@ -34,6 +34,13 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="quality_control.menu_quality_alert"
|
||||||
|
name="质量缺陷"
|
||||||
|
action="quality_alert_action_check"
|
||||||
|
parent="quality_control.menu_quality_control"
|
||||||
|
sequence="20"/>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="quality_point_view_form_inherit_sf">
|
<record model="ir.ui.view" id="quality_point_view_form_inherit_sf">
|
||||||
<field name="name">quality.point.form.inherit.sf</field>
|
<field name="name">quality.point.form.inherit.sf</field>
|
||||||
<field name="model">quality.point</field>
|
<field name="model">quality.point</field>
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ class QuickEasyOrder(models.Model):
|
|||||||
'quality_standard_mimetype': '',
|
'quality_standard_mimetype': '',
|
||||||
'machining_drawings': item.machining_drawings,
|
'machining_drawings': item.machining_drawings,
|
||||||
'quality_standard': '',
|
'quality_standard': '',
|
||||||
|
'part_name': '',
|
||||||
})
|
})
|
||||||
# res['bfm_process_order_list'] = json.dumps(res['bfm_process_order_list'])
|
# res['bfm_process_order_list'] = json.dumps(res['bfm_process_order_list'])
|
||||||
product_id = self.env.ref('sf_dlm.product_template_sf').sudo()
|
product_id = self.env.ref('sf_dlm.product_template_sf').sudo()
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class ReSaleOrder(models.Model):
|
|||||||
|
|
||||||
# 业务平台分配工厂后在智能工厂先创建销售订单
|
# 业务平台分配工厂后在智能工厂先创建销售订单
|
||||||
def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address,
|
def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address,
|
||||||
deadline_of_delivery, payments_way, pay_way):
|
deadline_of_delivery, payments_way, pay_way, state='sale'):
|
||||||
now_time = datetime.datetime.now()
|
now_time = datetime.datetime.now()
|
||||||
partner = self.get_customer()
|
partner = self.get_customer()
|
||||||
data = {
|
data = {
|
||||||
@@ -69,7 +69,7 @@ class ReSaleOrder(models.Model):
|
|||||||
'name': self.env['ir.sequence'].next_by_code('sale.order', sequence_date=now_time),
|
'name': self.env['ir.sequence'].next_by_code('sale.order', sequence_date=now_time),
|
||||||
'partner_id': partner.id,
|
'partner_id': partner.id,
|
||||||
'check_status': 'approved',
|
'check_status': 'approved',
|
||||||
'state': 'sale',
|
'state': state,
|
||||||
'user_id': partner.user_id.id,
|
'user_id': partner.user_id.id,
|
||||||
'person_of_delivery': delivery_name,
|
'person_of_delivery': delivery_name,
|
||||||
'telephone_of_delivery': delivery_telephone,
|
'telephone_of_delivery': delivery_telephone,
|
||||||
@@ -133,8 +133,8 @@ class ReSaleOrder(models.Model):
|
|||||||
'product_uom_qty': item['number'],
|
'product_uom_qty': item['number'],
|
||||||
'model_glb_file': base64.b64decode(item['model_file']),
|
'model_glb_file': base64.b64decode(item['model_file']),
|
||||||
'remark': item.get('remark'),
|
'remark': item.get('remark'),
|
||||||
'is_incoming_material': item.get('is_incoming_material'),
|
'embryo_redundancy_id': item.get('embryo_redundancy_id'),
|
||||||
'incoming_size': item.get('incoming_size'),
|
'manual_quotation': item.get('manual_quotation')
|
||||||
}
|
}
|
||||||
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
|
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
|
||||||
|
|
||||||
@@ -175,8 +175,9 @@ class ResaleOrderLine(models.Model):
|
|||||||
check_status = fields.Selection(related='order_id.check_status')
|
check_status = fields.Selection(related='order_id.check_status')
|
||||||
remark = fields.Char('备注')
|
remark = fields.Char('备注')
|
||||||
|
|
||||||
is_incoming_material = fields.Boolean('是否带料', default=False)
|
# is_incoming_material = fields.Boolean('客供料', default=False)
|
||||||
incoming_size = fields.Char('带料尺寸')
|
embryo_redundancy_id = fields.Many2one('sf.embryo.redundancy', '坯料冗余')
|
||||||
|
manual_quotation = fields.Boolean('人工编程', default=False)
|
||||||
|
|
||||||
@api.depends('product_template_id')
|
@api.depends('product_template_id')
|
||||||
def _compute_model_glb_file(self):
|
def _compute_model_glb_file(self):
|
||||||
@@ -208,7 +209,8 @@ class RePurchaseOrder(models.Model):
|
|||||||
compute='_compute_user_id',
|
compute='_compute_user_id',
|
||||||
store=True)
|
store=True)
|
||||||
|
|
||||||
purchase_type = fields.Selection([('standard', '标准采购'), ('consignment', '委外加工')], string='采购类型', default='standard')
|
purchase_type = fields.Selection([('standard', '标准采购'), ('consignment', '委外加工')], string='采购类型',
|
||||||
|
default='standard')
|
||||||
|
|
||||||
@api.depends('partner_id')
|
@api.depends('partner_id')
|
||||||
def _compute_user_id(self):
|
def _compute_user_id(self):
|
||||||
@@ -243,38 +245,39 @@ class RePurchaseOrder(models.Model):
|
|||||||
production_process = product_id_to_production_names.get(
|
production_process = product_id_to_production_names.get(
|
||||||
production.product_id.id)
|
production.product_id.id)
|
||||||
for pp in consecutive_process_parameters:
|
for pp in consecutive_process_parameters:
|
||||||
if pp.gain_way == '外协':
|
server_template = self.env['product.template'].search(
|
||||||
server_template = self.env['product.template'].search(
|
[('server_product_process_parameters_id', '=', pp.surface_technics_parameters_id.id),
|
||||||
[('server_product_process_parameters_id', '=', pp.id),
|
('detailed_type', '=', 'service')])
|
||||||
('detailed_type', '=', 'service')])
|
purchase_order_line = self.env['purchase.order.line'].search(
|
||||||
purchase_order_line = self.env['purchase.order.line'].search(
|
[('product_id', '=', server_template.product_variant_id.id),
|
||||||
[('product_id', '=', server_template.product_variant_id.id),
|
('product_qty', '=', len(production_process))], limit=1, order='id desc')
|
||||||
('product_qty', '=', len(production_process))], limit=1, order='id desc')
|
if not purchase_order_line:
|
||||||
if not purchase_order_line:
|
server_product_process.append((0, 0, {
|
||||||
server_product_process.append((0, 0, {
|
'product_id': server_template.product_variant_id.id,
|
||||||
'product_id': server_template.product_variant_id.id,
|
'product_qty': len(production_process),
|
||||||
'product_qty': len(production_process),
|
'product_uom': server_template.uom_id.id
|
||||||
'product_uom': server_template.uom_id.id
|
}))
|
||||||
}))
|
else:
|
||||||
else:
|
for item in purchase_order_line:
|
||||||
for item in purchase_order_line:
|
if production.name in production_process:
|
||||||
if production.name in production_process:
|
purchase_order = self.env['purchase.order'].search(
|
||||||
purchase_order = self.env['purchase.order'].search(
|
[('state', '=', 'draft'), ('origin', '=', ','.join(production_process)),
|
||||||
[('state', '=', 'draft'), ('origin', '=', ','.join(production_process)),
|
('id', '=', item.order_id.id)])
|
||||||
('id', '=', item.order_id.id)])
|
if not purchase_order:
|
||||||
if not purchase_order:
|
server_product_process.append((0, 0, {
|
||||||
server_product_process.append((0, 0, {
|
'product_id': server_template.product_variant_id.id,
|
||||||
'product_id': server_template.product_variant_id.id,
|
'product_qty': len(production_process),
|
||||||
'product_qty': len(production_process),
|
'product_uom': server_template.uom_id.id
|
||||||
'product_uom': server_template.uom_id.id
|
}))
|
||||||
}))
|
|
||||||
if server_product_process:
|
if server_product_process:
|
||||||
self.env['purchase.order'].sudo().create({
|
self.env['purchase.order'].sudo().create({
|
||||||
'partner_id': server_template.seller_ids.partner_id.id,
|
'partner_id': server_template.seller_ids[0].partner_id.id,
|
||||||
'origin': ','.join(production_process),
|
'origin': ','.join(production_process),
|
||||||
'state': 'draft',
|
'state': 'draft',
|
||||||
|
'purchase_type': 'consignment',
|
||||||
'order_line': server_product_process})
|
'order_line': server_product_process})
|
||||||
# self.env.cr.commit()
|
# self.env.cr.commit()
|
||||||
|
|
||||||
@api.onchange('order_line')
|
@api.onchange('order_line')
|
||||||
def _onchange_order_line(self):
|
def _onchange_order_line(self):
|
||||||
|
|||||||
@@ -218,6 +218,7 @@
|
|||||||
<field name="context">
|
<field name="context">
|
||||||
{"search_default_categ_id":1,"search_default_filter_to_purchase":1, "purchase_product_template": 1}
|
{"search_default_categ_id":1,"search_default_filter_to_purchase":1, "purchase_product_template": 1}
|
||||||
</field>
|
</field>
|
||||||
|
<field name="view_mode">tree,kanban,form,activity</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
<!-- <field name="process_id"/>-->
|
<!-- <field name="process_id"/>-->
|
||||||
<field name="parameter_ids" widget="many2many_tags" string="表面工艺参数"
|
<field name="parameter_ids" widget="many2many_tags" string="表面工艺参数"
|
||||||
options="{'no_create': True}"/>
|
options="{'no_create': True}"/>
|
||||||
<field name="machining_precision" required="1"/>
|
<!-- <field name="machining_precision" required="1"/>-->
|
||||||
<field name="processing_time"/>
|
<field name="processing_time"/>
|
||||||
<field name="quantity" options="{'format': false}"/>
|
<field name="quantity" options="{'format': false}"/>
|
||||||
<field name="unit_price"/>
|
<field name="unit_price"/>
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
||||||
</field>
|
</field>
|
||||||
<field name="payment_term_id" position="after">
|
<field name="payment_term_id" position="after">
|
||||||
<field name="deadline_of_delivery" readonly="0"/>
|
<field name="deadline_of_delivery" readonly="0"/>
|
||||||
<field name="payments_way" invisible="1"/>
|
<field name="payments_way" invisible="1"/>
|
||||||
<field name="pay_way" invisible="1"/>
|
<field name="pay_way" invisible="1"/>
|
||||||
<!-- <field name="schedule_status" readonly="1"/> -->
|
<!-- <field name="schedule_status" readonly="1"/> -->
|
||||||
@@ -118,8 +118,8 @@
|
|||||||
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="replace">
|
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="replace">
|
||||||
<field name="name" widget="section_and_note_text" optional="show"
|
<field name="name" widget="section_and_note_text" optional="show"
|
||||||
string="参数说明(长/宽/高/体积/精度/材质)"/>
|
string="参数说明(长/宽/高/体积/精度/材质)"/>
|
||||||
<field name="is_incoming_material" optional="hide"/>
|
<field name="manual_quotation" readonly="1"/>
|
||||||
<field name="incoming_size" optional="hide"/>
|
<field name="embryo_redundancy_id" readonly="1"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<field name="user_id" position="attributes">
|
<field name="user_id" position="attributes">
|
||||||
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
||||||
@@ -166,8 +166,8 @@
|
|||||||
</xpath>
|
</xpath>
|
||||||
<!--新增带料字段-->
|
<!--新增带料字段-->
|
||||||
<xpath expr="//field[@name='order_line']/form//group//group//field[@name='analytic_distribution']" position="after">
|
<xpath expr="//field[@name='order_line']/form//group//group//field[@name='analytic_distribution']" position="after">
|
||||||
<field name="is_incoming_material"/>
|
<field name="manual_quotation" />
|
||||||
<field name="incoming_size" attrs="{'invisible': [('is_incoming_material', '=', False)],'readonly':1}"/>
|
<field name="embryo_redundancy_id"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -217,12 +217,12 @@
|
|||||||
<field name="create_date" position="attributes">
|
<field name="create_date" position="attributes">
|
||||||
<attribute name="string">下单时间</attribute>
|
<attribute name="string">下单时间</attribute>
|
||||||
</field>
|
</field>
|
||||||
<field name="state" position="after">
|
<!-- <field name="state" position="after">
|
||||||
<field name="check_status" widget="badge"
|
<field name="check_status" widget="badge"
|
||||||
decoration-success="check_status == 'approved'"
|
decoration-success="check_status == 'approved'"
|
||||||
decoration-warning="check_status == 'pending'"
|
decoration-warning="check_status == 'pending'"
|
||||||
decoration-danger="check_status == 'fail'"/>
|
decoration-danger="check_status == 'fail'"/>
|
||||||
</field>
|
</field> -->
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -283,6 +283,8 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="sale.product_template_action" model="ir.actions.act_window">
|
<record id="sale.product_template_action" model="ir.actions.act_window">
|
||||||
|
<field name="view_mode">tree,kanban,form,activity</field>
|
||||||
|
<field name="view_id" ref="product.product_template_tree_view"/>
|
||||||
<field name="context">{"search_default_categ_id":1,
|
<field name="context">{"search_default_categ_id":1,
|
||||||
"search_default_filter_to_sell":1,"sale_multi_pricelist_product_template": 1}
|
"search_default_filter_to_sell":1,"sale_multi_pricelist_product_template": 1}
|
||||||
</field>
|
</field>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
<record id="stock.product_template_action_product" model="ir.actions.act_window">
|
<record id="stock.product_template_action_product" model="ir.actions.act_window">
|
||||||
|
<field name="view_mode">tree,kanban,form,activity</field>
|
||||||
<field name="context">
|
<field name="context">
|
||||||
{"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
|
{"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
|
||||||
</field>
|
</field>
|
||||||
|
|||||||
@@ -1106,6 +1106,9 @@ class SfProcurementGroup(models.Model):
|
|||||||
class SfPickingType(models.Model):
|
class SfPickingType(models.Model):
|
||||||
_inherit = 'stock.picking.type'
|
_inherit = 'stock.picking.type'
|
||||||
|
|
||||||
|
code = fields.Selection([('incoming', 'Receipt'), ('outgoing', 'Delivery'), ('internal', '厂内出入库')],
|
||||||
|
'Type of Operation', required=True)
|
||||||
|
|
||||||
def _default_show_operations(self):
|
def _default_show_operations(self):
|
||||||
return self.user_has_groups(
|
return self.user_has_groups(
|
||||||
'stock.group_production_lot,'
|
'stock.group_production_lot,'
|
||||||
|
|||||||
Reference in New Issue
Block a user